Monkeypatch ActiveRecord :: FinderMethods

时间:2016-04-26 22:37:56

标签: ruby-on-rails ruby ruby-on-rails-4 activerecord

我试图修补ActiveRecord :: FinderMethods,以便为我的模型使用散列ID。例如,User.find(1)变为User.find(" FEW")。可悲的是,我的覆盖方法没有被调用。有关如何覆盖find_one方法的任何想法吗?

module ActiveRecord
  module FinderMethods
    alias_method :orig_find_one, :find_one
    def find_one(id)
      if id.is_a?(String)
        orig_find_one decrypt_id(id)
      else
        orig_find_one(id)
      end
    end
  end
end

2 个答案:

答案 0 :(得分:1)

这篇文章讨论如何通过覆盖User.primary_key方法来实际执行您想要的操作,如:

class User
  self.primary_key = 'hashed_id'
end

允许您拨打User.find并将其传递给“hashed_id”:

http://ruby-journal.com/how-to-override-default-primary-key-id-in-rails/

所以,这是可能的。

那就是说,我建议不要这样做,而是使用像User.find_by_hashed_id这样的东西。唯一的区别是,当找不到结果而不是抛出nil异常时,此方法将返回ActiveRecord::RecordNotFound。你可以在控制器中手动抛出它:

def show
  @user = User.find_by_hashed_id(hashed_id)
  raise ActiveRecord::RecordNotFound.new if @user.nil?
  ... continue processing ...
end

最后,还有一个注意事项可以让你更容易--Rails还有一个方法可以覆盖你的模型to_param,告诉它在生成路由时使用什么属性。当然,默认情况下,它使用id,但您可能希望使用hashed_id。

class User
  def to_param
    self.hashed_id
  end
end

现在,在您的控制器中,params[:id]将包含hashed_id而不是id。

def show
  @user = User.find_by_hashed_id(params[:id])
  raise ActiveRecord::RecordNotFound.new if @user.nil?
  ... continue processing ...
end

答案 1 :(得分:1)

我同意你这样做时应该小心,但这是可能的。

如果您有方法decode_id将哈希ID转换回原始ID,则以下内容将起作用:

在User.rb

# Extend AR find method to allow finding records by an encoded string id:
def self.find(*ids)
  return super if ids.length > 1

  # Note the short-circuiting || to fall-back to default behavior
  find_by(id: decode_id(ids[0])) || super
end

如果传递了无效的哈希值,请确保decode_id返回nil。这样您可以通过Hashed ID和标准ID找到,因此如果您的用户ID为12345,则以下内容:

User.find(12345)
User.find("12345")
User.find(encode_id(12345))

所有人都应该返回同一个用户。