通过方法加载has_many选项哈希

时间:2013-06-17 00:15:15

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

我已经成为一个复杂的模型,我正试图干掉它。对于我的has_many选项,我不想让它们重复,而是简单地从类中的方法加载它们。

class ExampleClass < ActiveRecord::Base
  has_many :related_things, get_association_hash(arg1)
  has_many :other_things, get_association_hash(arg2)

  def get_association_hash(arg)
    { :class_name => 'SomeClass', :conditions => ['table.column = ?', arg] }
  end
end

不幸的是,加载课程时会产生undefined method ‘get_association_hash’ for #<Class:0x007f9ae9efe6c0>

(作为一个完整性检查,如果我只是单独调用它,而不将其包含在has_many中,那么该方法就可以了。而且,实际的类相当大,所以DRY比这个小例子更有用。)< / p>

我注意到错误消息提到Class,而不是我的派生ExampleClass,所以也许它与has_many的加载方式有关,以及我定义方法的位置?

1 个答案:

答案 0 :(得分:1)

has_many只是一个类方法所以:

has_many :related_things, get_association_hash(arg1)

只是一个方法调用,而且该上下文中的接收者是你的ExampleClass。这意味着get_association_hash需要是一个类方法。您还必须在 has_many来电之前定义,否则您将无法在任何地方拨打电话:

class ExampleClass < ActiveRecord::Base
  def self.get_association_hash(arg)
    { :class_name => 'SomeClass', :conditions => ['table.column = ?', arg] }
  end
  has_many :related_things, get_association_hash(arg1)
  has_many :other_things, get_association_hash(arg2)
end

这可能有点难看,并且弄乱了通常的定义顺序。如果是这种情况,那么您可以将get_association_hash方法推送到模块中,然后将该模块包含在班级的顶部:

module Pancakes
  def self.included(base)
    # There are various different ways to do this, use whichever one you like best
    base.class_exec do
      def self.get_association_hash(arg)
        # ...
      end
    end
  end
end

class ExampleClass < ActiveRecord::Base
  include Pancakes
  has_many :related_things, get_association_hash(arg1)
  has_many :other_things, get_association_hash(arg2)
end

你可能会把你的模块称为比Pancakes更合理的东西,这只是我的默认名称(因为 foo 在一段时间后变得无聊而我更喜欢Fargo传统)。