CSC/ECE 517 Spring 2015/oss E1506 SYZ

From Expertiza_Wiki
Jump to navigation Jump to search

E1506: Refactoring, testing and new features related to “users”

Overview

Code refactoring

Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior<ref>Refactoring</ref>. Refactoring adds to the value of any program that has at least one of the following shortcomings<ref>Benefits of Code Refactoring Michael Hunger. Oct. 25, 2000</ref>:

  • Programs that are hard to read are hard to modify;
  • Programs that have duplicate logic are hard to modify;
  • Programs that require additional behavior that requires you to change running code are hard to modify;
  • Programs with complex conditional logic are hard to modify.

Rspec

RSpec is a behavior-driven development (BDD) framework for the Ruby programming language, inspired by JBehave. It contains its own mocking framework that is fully integrated into the framework based upon JMock. The framework can be considered a domain-specific language (DSL) and resembles a natural language specification<ref>Rspec</ref>.

Object-oriented Design Principles

Object-oriented programming (OOP) is a programming language model organized around objects rather than "actions" and data rather than logic<ref>Margaret Rouse. object-oriented programming (OOP) definition. Aug 3, 2008</ref>. Historically, a program has been viewed as a logical procedure that takes input data, processes it, and produces output data. Though an Object oriented language provides us with highly useful and important programming concepts like Inheritance, Polymorphism, Abstraction and Encapsulation which definitely makes the code more efficient, it is equally important to have the knowledge of using them in the code. Object Oriented Design Principles are core of OOPS programming<ref>Javin Paul. Blogspot. March 3, 2012</ref>. It is important to know these design principles, to create clean and modular design. There are many design principles that help us to create clean and efficient code.


Project Resources

Todo List

Related files: users_controller.rb, participants_controller.rb, user.rb, users/show.html.erb

  • Find the un-called methods if any and delete them. [done]
  • Change the Rails 2 syntax to Rails 4 style.
  • Refactor users_controller.rb
    • Change the white space for the second harf of this file, starts at “def edit”. [done]
    • Separate the “paginate_list” method into two methods. The search method should be in model and the paginating method should be in the controller. [done]
  • New feature: delete users
    • A user can be deleted if (s)he has not participated in an assignment. [done]
    • If the user is participating in an assignment, the system will ask, “User is participating in k assignments. Delete as a participant in these assignment(s)?” [done]
    • If the user has submitted or reviewed in any of these assignments, the system will say the user cannot be deleted, but offer to rename the user account to <current_account_name>_hidden.
      • rename (javascript calling update method in users_controller.rb) [done]
      • different users have different delete methods. [done]
    • If the person trying to delete does not want to rename the account, the system will just say that the user can’t be deleted. [done]
    • Write tests (with Rspec) for this feature.[done]
  • Testing feature: search for users
    • In rails 4 branch, admins can search for the users with 1) users’ login names 2) users’ last or first names and 3) users’ emails. Please write tests (with Rspec) for this feature [done]

Delete Uncalled Methods

Change Rails 2 syntax to Rails 4

with_scope

Refactor users_controller.rb

New feature: delete users

Design

At the very beginning, we decide to use "Cascading Delete" to delete users. Because there are many relationship between different users. They can be reviewer, reviewee, teaching assistant, and so on.If we have to delete an user, we have to not only delete the record in user table, but also other related tables.

So, we divide all users into two set, one is new users without any relationship, the other is the old user with some relationships. And we find that it is quite easy to achieve the functionality of deleting new users. When deleting old users, we find some problems. Because old users may be a reviewer before and score some assignments. If we delete some old users, the assignments' review scores will be a mess.

After discussing with professor, we decide to deprecate"Cascading Delete". And we use below algorithm to handle user deletion.


Confirm box flow diagram


  • A user can be deleted if (s)he has not participated in an assignment;
  • If the user is participating in an assignment, the system will ask, “User is participating in k assignments. Delete as a participant in these assignment(s)?”;


Delete confirm box


  • If the user has submitted or reviewed in any of these assignments, the system will say the user cannot be deleted, but offer to rename the user account to <current_account_name>_hidden;


Rename confirm box


  • Rename (javascript calling update method in users_controller.rb);


Rename success 2


  • If the person trying to delete does not want to rename the account, the system will just say that the user can’t be deleted.


Cannot delete

Implementation

We add a userDeleteConfirmBox.js file to implement this unique confirm box algorithm.

  • First, we overwrite the Rails default confirm function.
$.rails.allowAction = function(link) {
  console.log(link.attr('data-relationship'))
  console.log(link.attr('data-username'))
  if ((!link.attr('data-confirm')) || (link.attr('data-relationship') && link.attr('data-relationship') == 'false')) {
   return true;
  }
  $.rails.showConfirmDialog(link);
  return false;
};
$.rails.confirmed = function(link) {
  link.removeAttr('data-confirm');
  return link.trigger('click.rails');
};
  • Then, we write code to achieve customed confirm box using our own algorithm.
$.rails.showConfirmDialog = function(link) {
 var message = link.attr('data-confirm');
 //confirmation box style
 $(function() {
   $(html1).modal();
   $("#dialog-confirm1").dialog({
     width: 340,
     buttons: {
       "Cancel": function() {
         $( this ).dialog( "close" );
         location.reload();
       },
       "Yes": function() {
         //return $.rails.confirmed(link);
         $( this ).dialog( "close" );
         $(html2).modal();
         $("#dialog-confirm2").dialog({
           width: 340,
           buttons: {
             "Cancel": function() {
               $( this ).dialog( "close" );
               location.reload();
             },
             "Yes": function() {                 
                   $('#rename').click() 
                 $( this ).dialog( "close" );
                 $(html3).modal();
                 $("#dialog-confirm3").dialog({
                   width: 340,
                 });
             },
             "No, delete any way!": function() {
                 $( this ).dialog( "close" );
                 $(html4).modal();
                 $("#dialog-confirm4").dialog({
                     width: 340,
                     buttons: {
                       "Close": function() {
                         $( this ).dialog( "close" );
                         location.reload();
                       }
                     }
                 });
               }
             }
          });
        }
      }
    });
 });
 return $('#dialog-confirm .confirm').on('click', function() {
   return $.rails.confirmed(link);
 });
};
  • After that, we all the if condition in /users/show.html.erb file. So when deleting the old users, Expertiza uses the functionality and customed confirm box we define; when deleting the new users, Expertiza will use the default confirm box.
 <% if @assignment_participant_num != 0 and (!@maps.nil? or @maps.length != 0)%> 
     <%= link_to 'Delete', {:action => 'destroy', :id => @user}, data:{:confirm => "User is participating in #{@assignment_participant_num} assignments.
Delete as a participant in these assignment(s)?", :relationship => 'true', :username => @user.name}, :method => :delete, remote: true%> <% else %> <%= link_to 'Delete', {:action => 'destroy', :id => @user}, data:{:confirm => "User joins in #{@assignment_participant_num} assignment(s), but without participates in any assignments. Delete as a participant in these assignment(s)?", :relationship => 'false'}, :method => :delete, remote: true%> <% end %>

Testing feature: search for users

Design

We write some features test to test "search for users" functionality.

  • Instructor searches users bylogin names
feature 'Method 1: Instructor search a user' do
 before(:all) do
   instructor.save
   student.save
   log_in instructor.name, "password"
 end
 scenario 'by login name' do
   visit '/users/list'
   fill_in 'letter', with: 'student'
   find('#search_by').select 'Username'
   click_button 'Search'
   expect(page).to have_content("Student, Perfect")
   expect(page).to have_content("pstudent@dev.null")
 end
end
  • Instructor searches users by first name or last name
feature 'Method2: Instructor search a user' do
 before(:all) do
   instructor.save
   student.save
   log_in instructor.name, "password"
 end
 scenario 'by last or first name' do
   visit '/users/list'
   fill_in 'letter', with: 'Bob'
   find('#search_by').select 'Full name'
   click_button 'Search'
   expect(page).to have_content("Dole, Bob")
   expect(page).to have_content("bdole@dev.null")
 end
end
  • Instructor searches users by email
feature 'Method3: Instructor search a user' do
 before(:all) do
   instructor.save
   student.save
   log_in instructor.name, "password"
 end
 scenario 'by email' do
   visit '/users/list'
   fill_in 'letter', with: 'bdole@dev.null'
   find('#search_by').select 'Email'
   click_button 'Search'
   expect(page).to have_content("Dole, Bob")
   expect(page).to have_content("bdole@dev.null")
 end
end
  • Instructor deletes new users
feature 'Instructor delete a user' do
 before(:all) do
   instructor.save
   student.save
   log_in instructor.name, "password"
 end
 scenario 'which has no relationship' do
   visit '/users/list'
   #in order to show whole user list
   fill_in 'letter', with: 
   find('#search_by').select 'Username'
   click_button 'Search'
   click_link 'student'
   click_link 'Delete'
   expect(page).to_not have_content("student")
   expect(page).to have_content("instructor")
 end
end

Implement

rspec spec/features/users_spec.rb

References

<references/>