涉及has_many& amp;的元编程问题belongs_to association

时间:2011-01-25 01:49:54

标签: ruby-on-rails metaprogramming

我有两个班级:

class Activity < ActiveRecord::Base
  belongs_to :activity_type

  def belongs_to_cat_a?
    self.activity_category == ActivityCategory.category_a
  end

  def belongs_to_cat_b?
    self.activity_category == ActivityCategory.category_b
  end

end

class ActivityCategory < ActiveRecord::Base
  has_many :activities

  def self.cat_a
    ActivityCategory.find_by_name("CatA")
  end

  def self.cat_b
    ActivityCategory.find_by_name("CatB")
  end
end

使用元编程,我将ActivityCategory更改为以下内容:

class ActivityCategory < ActiveRecord::Base
  has_many :activities

  CATEGORIES = ['CatA', 'CatB']

  class << self
      CATEGORIES.each do |c|
          define_method "#{c.underscore.downcase}" do # for ex: cat_a
              find_by_name(c)
          end
      end
  end

end

确定。现在想象在类Activity中我有大约12种方法来检查它属于哪个类别 似乎是使用MP进行干燥的完美候选者。

我该怎么办?

3 个答案:

答案 0 :(得分:7)

我不确定这是MP的好候选人。首先,您正在对类别进行硬编码,它们正在编写代码,而不是生成代码。如果要在被询问是否属于某个类别时返回true / false语句,您可以执行以下操作:

class Activity < ActiveRecord::Base
  ...

  def belongs_to? activity
    activity_type.name == activity
  end

end

并按原样执行...

a = Activity.save(:activity_category => ActivityCategory.new(:name => "CatA")
a.belongs_to? "CatA" #=> true

还是我错过了这一点?

答案 1 :(得分:0)

这不是一种推荐的方式,因为这段代码往往有点难以维护(Jed的解决方案更好),但你可以应用你已经拥有的相同风格的MP:

class Activity < ActiveRecord::Base
  belongs_to :activity_type

  CATEGORIES = ['CatA', 'CatB']

  class << self
    CATEGORIES.each do |c|
      define_method "belongs_to_#{c.underscore.downcase}?" do # for ex: cat_a
        self.activity_category == ActivityCategory.send( "category_#{c[-1]}".to_sym )  
      end
    end
  end
end

答案 2 :(得分:0)

首先,我会改变你已有的东西。

如果使用method_missing拦截它,则可以避免对类别列表进行硬编码。

class ActivityCategory < ActiveRecord::Base
  has_many :activities
  alias_method :old_method_missing, :method_missing

  def self.method_missing(method, *args, &block)
    if cat = self.class.find_by_name(method.to_s)
      return cat
    else
      old_method_missing(method, *args, &block)
    end
  end

end

这是有效的,因为如果未检测到调用的方法,它将把它传递给缺少的旧方法。如果你想提出任何类似的技巧,请不要将任何类别命名为“发现”或类似的任何类别!

以同样的方式,在活动中,你可以做到

class Activity < ActiveRecord::Base
  belongs_to :activity_type
  alias_method :old_method_missing, :method_missing

  def method_missing(method, *args, &block)
    if matchdata = /\Abelongs_to_category_(\w)\?/.match(method.to_s)
      return ActivityCategory.find_by_name(matchdata[1]) == ActivityCategory.send(matchdata[1].to_sym)
    else
      old_method_missing(method, *args, &block)
    end
  end

end 

我不确定语法是否完全正确,但您可以调查一般方法