Вопрос метапрограммирования с участием ассоциации has_many & own_to - PullRequest
2 голосов
/ 25 января 2011

У меня есть два класса:

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 ]

7 голосов
/ 25 января 2011

Я не уверен, что это хороший кандидат в депутаты. Во-первых, вы жестко кодируете свои категории, которые сразу же пишут код, а не генерируют его. Если вы хотите вернуть утверждение 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

или я упускаю суть?

0 голосов
/ 01 февраля 2011

Во-первых, я бы изменил то, что у вас уже есть.

Если вы перехватываете его методом 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 

IЯ не уверен, что синтаксис полностью правильный, но вы могли бы исследовать этот общий подход

0 голосов
/ 01 февраля 2011

Это не рекомендуемый способ, так как этот код немного сложен в обслуживании (решение Джеда лучше), но вы можете просто применить тот же стиль 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
...