如何使用Rspec重构Rails模型中的复杂方法?

时间:2012-10-30 19:38:44

标签: ruby-on-rails ruby ruby-on-rails-3 rspec refactoring

我有以下复杂的方法。我正在努力寻找并实施可能的改进。现在我把if语句移到Access类。

def add_access(access)
   if access.instance_of?(Access)
     up = UserAccess.find(:first, :conditions => ['user_id = ? AND access_id = ?', self.id, access.id])
     if !up && company
       users = company.users.map{|u| u.id unless u.blank?}.compact
       num_p = UserAccess.count(:conditions => ['user_id IN (?) AND access_id = ?', users, access.id])
       if num_p < access.limit
         UserAccess.create(:user => self, :access => access)
       else
         return "You have exceeded the maximum number of alotted permissions"
       end
     end
   end
 end

我想在重构之前添加规范。我先加了一个。怎么看起来像其他人?

  describe "#add_permission" do
    before do
      @permission = create(:permission)
      @user = create(:user)
    end

    it "allow create UserPermission" do
      expect {
        @user.add_permission(@permission)
      }.to change {
        UserPermission.count
      }.by(1)
    end
  end

3 个答案:

答案 0 :(得分:2)

您是否为此课程进行了单元测试或集成测试? 我会在重构前先写一些。

假设您有测试,第一个目标可能是缩短此方法的长度。

以下是一些改进:

  1. UserAccess.find调用移至UserAccess模型并将其设为命名范围。
  2. 同样,也可以移动count方法。
  3. 每次更换后重新测试并保持提取,直到它干净。每个人都对清洁有不同的看法,但是当你看到它时就会知道。

答案 1 :(得分:2)

我会这样做。

将Access的检查更像是一个初始断言,并在发生错误时引发错误。

创建一种新方法来检查现有的用户访问权限 - 这似乎是可重用的,并且更具可读性。

然后,公司限制更像是对我的验证,将其作为自定义验证移至UserAccess类。

class User

  has_many :accesses, :class_name=>'UserAccess'

  def add_access(access)
    raise "Can only add a Access: #{access.inspect}" unless access.instance_of?(Access)

    if has_access?(access)
      logger.debug("User #{self.inspect} already has the access #{access}")
      return false
    end

    accesses.create(:access => access)
  end

  def has_access?(access)
    accesses.find(:first, :conditions => {:access_id=> access.id})
  end

end

class UserAccess

  validate :below_company_limit

  def below_company_limit
    return true unless company
    company_user_ids = company.users.map{|u| u.id unless u.blank?}.compact
    access_count = UserAccess.count(:conditions => ['user_id IN (?) AND access_id = ?', company_user_ids, access.id])
    access_count < access.limit
  end

end

答案 2 :(得分:1)

其他想法,与移动代码无关但更清晰:

users = company.users.map{|u| u.id unless u.blank?}.compact
num_p = UserAccess.count(:conditions => ['user_id IN (?) AND access_id = ?', users, access.id])

可以成为:

num_p = UserAccess.where(user_id: company.users, access_id: access.id).count