CSC/ECE 517 Fall 2017/E1746 Refactor user.rb: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
Line 24: Line 24:
  https://github.com/AnranZhou/expertiza/blob/master/spec/models/user_spec.rb
  https://github.com/AnranZhou/expertiza/blob/master/spec/models/user_spec.rb


* '''Mission 2''':Refactor get_user_list method
====Mission 2:Refactor get_user_list method====
::The task is to write failing test first and ,move if conditions to corresponding subclasses (eg. ta.rb, instructor.rb) with same method name,then replace the conditional with the relevant method calls.
::The task is to write failing test first and ,move if conditions to corresponding subclasses (eg. ta.rb, instructor.rb) with same method name,then replace the conditional with the relevant method calls.
::First writing the failing test first according to the requirements of the refactor.
::First writing the failing test first according to the requirements of the refactor.
Line 136: Line 136:
</pre>
</pre>
::So the refactor of get_user_list method has been done.
::So the refactor of get_user_list method has been done.
* '''Mission 3''':Refactor self.search_users method.
====Mission 3====:Refactor self.search_users method.
::This task is required to write the failing test first and extract duplicated code to a new method.
::This task is required to write the failing test first and extract duplicated code to a new method.
::First, write the failing test.
::First, write the failing test.
Line 202: Line 202:
         search_filter = letter + '%'
         search_filter = letter + '%'
         users = User.order('name').where("(role_id in (?) or id = ?) and name like ?", role.get_available_roles, user_id, search_filter)
         users = User.order('name').where("(role_id in (?) or id = ?) and name like ?", role.get_available_roles, user_id, search_filter)
'''Mission 4''':Rename method and change all other places it is used.First, is to write failing test of the renamed method which has been finished in mission one.So just skip to the second part to rename them.
====Mission 4====:Rename method and change all other places it is used.First, is to write failing test of the renamed method which has been finished in mission one.So just skip to the second part to rename them.
::is_recursively_parent_of → recursively_parent_of?
::is_recursively_parent_of → recursively_parent_of?
</pre>
</pre>
Line 214: Line 214:
<pre>
<pre>
</pre>
</pre>
'''Mission 5''':Use find_by instead of dynamic method
====Mission 5====:Use find_by instead of dynamic method


===References===
===References===

Revision as of 23:13, 27 October 2017

E1746 [Test First Development] Refactor user.rb

This page provides a description of the Expertiza based OSS project.

About Expertiza

Expertiza is an open source project based on Ruby on Rails framework. Expertiza allows the instructor to create new assignments and customize new or existing assignments. It also allows the instructor to create a list of topics the students can sign up for. Students can form teams in Expertiza to work on various projects and assignments. Students can also peer review other students' submissions. Expertiza supports submission across various document types, including the URLs and wiki pages.

Problem Statement

The following tasks were accomplished in this project:

  • Complete the pending tests in user_spec.rb
  • Refactor get_user_list method
  • Refactor self.search_users method
  • Rename method and change all other places it is used
  • Use find_by instead of dynamic method

Missions and Solutions

Mission 1:Complete the pending tests in user_spec.rb

There are many pending tests in the file 'user.rb'. What we have done is finishing these pending tests and makes them pass.The user_spec.rb file on github is below.
https://github.com/AnranZhou/expertiza/blob/master/spec/models/user_spec.rb

Mission 2:Refactor get_user_list method

The task is to write failing test first and ,move if conditions to corresponding subclasses (eg. ta.rb, instructor.rb) with same method name,then replace the conditional with the relevant method calls.
First writing the failing test first according to the requirements of the refactor.
#describe '#get_user_list' do
    context 'when current user is super admin' do
      it 'fetches all users' do
        allow(user).to receive_message_chain("role.super_admin?"){ true }
        allow(user).to receive_message_chain("role.instructor?"){ false }
        allow(user).to receive_message_chain("role.ta?"){false}
        allow(SuperAdministrator).to receive(:get_user_list).and_return([user1,user2])
        expect(user.get_user_list).to eq ([user1,user2])
      end
  end
    context 'when current user is an instructor' do
      it 'fetches all users in his/her course/assignment' do
        allow(user).to receive_message_chain("role.super_admin?"){ false }
        allow(user).to receive_message_chain("role.instructor?"){ true }
        allow(user).to receive_message_chain("role.ta?"){false}
        allow(Instructor).to receive(:get_user_list).and_return([user1,user2])
        expect(user.get_user_list).to eq ([user1,user2])
      end
    end

    context 'when current user is a TA' do
      it 'fetches all users in his/her courses'do
        allow(user).to receive_message_chain("role.super_admin?"){ false }
        allow(user).to receive_message_chain("role.ta?"){ true }
        allow(user).to receive_message_chain("role.instructor?"){ false }
        allow(Ta).to receive(:get_user_list).and_return([user1,user2])
        expect(user.get_user_list).to eq ([user1,user2])
    end
    end
  end
The next step is to move if conditionals to its relatively subclasses. In this case,the superadministrator.rb, instructor.rb and ta.rb.
#class SuperAdministrator < User
  def self.get_user_list
    user_list=[]
    User.all.find_each do |user|
    user_list << user
    end
  end
end
  #def self.get_user_list
    user_list=[]
    participants = []
    Course.where(instructor_id: self.id).find_each do |course|
      participants << course.get_participants
    end
    Assignment.where(instructor_id: self.id).find_each do |assignment|
      participants << assignment.participants
    end
    participants.each do |p_s|
      next if p_s.empty?
      p_s.each do |p|
        user_list << p.user if self.role.hasAllPrivilegesOf(p.user.role)
      end
    end
end
#def self.get_user_list
  get_user_list=[]
  courses.each do |course_id|
      course = Course.find(course_id)
      participants << course.get_participants
    end
    participants.each do |p_s|
      next if p_s.empty?
      p_s.each do |p|
        user_list << p.user if self.role.hasAllPrivilegesOf(p.user.role)
      end
    end
end
Then replace the conditional with the relevant method calls.
#def get_user_list
    user_list = []
    # If the user is a super admin, fetch all users
    if self.role.super_admin?
       return SuperAdministrator.get_user_list
    end


    # If the user is an instructor, fetch all users in his course/assignment
    if self.role.instructor?
      return Instructor.get_user_list
    end

    # If the user is a TA, fetch all users in his courses
    if self.role.ta?
      return Ta.get_user_list
    end

    # Add the children to the list
    unless self.role.super_admin?
      User.all.find_each do |u|
        if is_recursively_parent_of(u)
          user_list << u unless user_list.include?(u)
        end
      end
    end

    user_list.uniq
  end
So the refactor of get_user_list method has been done.

====Mission 3====:Refactor self.search_users method.

This task is required to write the failing test first and extract duplicated code to a new method.
First, write the failing test.
 #descirbe '.search_users' do

    let(:role) { Role.new }
    before(:each) do
        allow(User).to receive_message_chain(:order,:where).and_return(user)
    end

    it 'when the search_by is 1' do
      search_by = "1"
      user_id = double
      letter = 'name'
      search_filter = '%' + letter + '%'
      expect(User).to receive_message_chain(:order,:where).with("(role_id in (?) or id = ?) and name like ?", role.get_available_roles, user_id, search_filter)
      expect(User.search_users(role,user_id,letter,search_by)).to eq user
    end

    it 'when the search_by is 2' do
      search_by = "2"
      user_id = double
      letter = 'fullname'
      search_filter = '%' + letter + '%'
      expect(User).to receive_message_chain(:order,:where).with("(role_id in (?) or id = ?) and fullname like ?", role.get_available_roles, user_id, search_filter)
      expect(User.search_users(role,user_id,letter,search_by)).to eq user
    end

    it 'when the search_by is 3' do
      search_by = "3"
      user_id = double
      letter = 'email'
      search_filter = '%' + letter + '%'
      expect(User).to receive_message_chain(:order,:where).with("(role_id in (?) or id = ?) and email like ?", role.get_available_roles, user_id, search_filter)
      expect(User.search_users(role,user_id,letter,search_by)).to eq user
    end

    it 'when the search_by is default value' do
      search_by = nil
      user_id = double
      letter = ''
      search_filter = letter + '%'
      expect(User).to receive_message_chain(:order,:where).with("(role_id in (?) or id = ?) and name like ?", role.get_available_roles, user_id, search_filter)
      expect(User.search_users(role,user_id,letter,search_by)).to eq user
    end
  end
Then,extract duplicated code to a new method.Since this is kind of changing original method.So below is the changing on github.
#   def self.search_users(role, user_id, letter, search_by)
 -    if search_by == '1' # search by user name
 +    key_word = {'1'=>'name','2'=>'fullname','3'=>'email'}
 +    if key_word.include? search_by
        search_filter = '%' + letter + '%'
 -      users = User.order('name').where("(role_id in (?) or id = ?) and name like ?", role.get_available_roles, user_id, search_filter)
 -    elsif search_by == '2' # search by full name
 -      search_filter = '%' + letter + '%'
 -      users = User.order('name').where("(role_id in (?) or id = ?) and fullname like ?", role.get_available_roles, user_id, search_filter)
 -    elsif search_by == '3' # search by email
 -      search_filter = '%' + letter + '%'
 -      users = User.order('name').where("(role_id in (?) or id = ?) and email like ?", role.get_available_roles, user_id, search_filter)
 +      users = User.order('name').where("(role_id in (?) or id = ?) and #{key_word[search_by]} like ?", role.get_available_roles, user_id, search_filter)
      else # default used when clicking on letters
        search_filter = letter + '%'
        users = User.order('name').where("(role_id in (?) or id = ?) and name like ?", role.get_available_roles, user_id, search_filter)
====Mission 4====:Rename method and change all other places it is used.First, is to write failing test of the renamed method which has been finished in mission one.So just skip to the second part to rename them.
::is_recursively_parent_of → recursively_parent_of?
is_creator_of? → creator_of?

is_teaching_assistant_for? → teaching_assistant_for?

is_teaching_assistant? → teaching_assistant?

====Mission 5====:Use find_by instead of dynamic method

References

  1. Expertiza on GitHub
  2. GitHub Project Repository Fork
  3. The live Expertiza website
  4. Demo link
  5. Expertiza project documentation wiki
  6. Rspec Documentation
  7. Clean Code: A handbook of agile software craftsmanship. Author: Robert C Martin