2011-01-25 116 views
2

元編程問題,我有兩個類:涉及的has_many和belongs_to的關聯

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 DRY'DY了一下。

我該怎麼做?

回答

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 

還是我我錯過了這一點?

0

這不是一個推薦的方式,因爲這代碼往往是有點難以維持(傑德的解決方案是更好),但你可以申請你同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 
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 

我不知道語法是完全正確的,但你可以調查,通用的做法

相關問題