2010-01-08 66 views
1

可以說我有兩個表。 ActiveRecord - 自動合併模型數據作爲一個聚合

class CreateUsers < ActiveRecord::Migration 
    def self.up 
    create_table :users do |t| 
     t.string :type, :default => 'User' 
     t.string :user_name, :null => false 
     t.boolean :is_registered, :default => true 
     # ... many more fields 
    end 
    end 
end 

class CreateContactInfo < ActiveRecord::Migration 
    def self.up 
    create_table :contact_info do |t| 
     t.integer :resource_id 
     t.string :resource_type 
     t.string :first_name 
     t.string :last_name 
     t.string :middle_initial 
     t.string :title 
    end 
    end 
end 

class ContactInfo < ActiveRecord::Base 
    belongs_to :contactable, :polymorphic => true 
end 

class User < ActiveRecord::Base 
    has_one :contact_info, :as => :contactable 
    # composed_of :contact_info # ... It would be nice if magics happened here 
end 

我想有用戶的contact_info自動合併到我的用戶對象爲用戶對象的屬性,而不必說@ user.contact_info.first_name;相反,我更願意寫@ user.first_name。

我將屬性分解到contact_info表的原因是它們是多個模型的通用屬性。這就是爲什麼我要將contact_info設置爲多態關聯。

有誰知道一個很好的方法來將contact_info的屬性直接聚合/合併到我的用戶模型中嗎?

+1

感謝編輯,使其更容易閱讀的代碼:-) – 2010-01-08 21:03:25

回答

1

我終於得到它!謝謝amikazmi和Topher Fangio。我必須實現委託和method_missing技術才能實現這一目標。

下面是最終爲我工作的瘋狂!如果有人對如何進一步改進這一點有什麼建議,我很樂意聽取您的建議。

class User < ActiveRecord::Base 
    attr_accessible *([:user_name, :udid, :password, :password_confirmation, :contact_info] + ContactInfo.accessible_attributes.to_a.map {|a| a.to_sym}) 

    has_one :contact_info, :as => :contactable 

    def method_missing(method_id, *args) 
    if (!self.respond_to?(method_id) && self.contact_info.respond_to?(method_id)) 
     self.contact_info.send(method_id, *args) 
    elsif (!self.class.respond_to?(method_id) && ContactInfo.respond_to?(method_id)) 
     ContactInfo.send(method_id, *args) 
    else 
     super(method_id, *args) 
    end 
    end 

    # delegating attributes seems redundant with the method_missing above, but this secret sauce works. 
    ContactInfo.accessible_attributes.to_a.each do |a| 
    delegate a.to_sym, "#{a}=".to_sym, :to => :contact_info 
    end 

    def initialize(*args) 
    options = args.extract_options! 
    contact_attrs = ContactInfo.accessible_attributes.to_a.map{|a| a.to_sym} 
    @ci = ContactInfo.new(options.reject {|k,v| !contact_attrs.include?(k) }) 
    super(*(args << options.reject { |k,v| contact_attrs.include?(k) }.merge(:contact_info => @ci))) 
    self.contact_info = @ci 
    end 

    validates_presence_of :user_name 
    validates_uniqueness_of :user_name 

    validates_associated :contact_info 

    def after_save 
    # automatically save the contact info record for the user after the user has been saved. 
    self.contact_info.save! 
    end 

end 

class ContactInfo < ActiveRecord::Base 
    set_table_name "contact_info" 

    belongs_to :contactable, :polymorphic => true 

    validates_presence_of :email 
    validates_uniqueness_of :email 

    attr_accessible :first_name, 
        :last_name, 
        :middle_initial, 
        :title, 
        :organization_name, 
        :email, 
        :email_2, 
        :twitter_name, 
        :website_url, 
        :address_1, 
        :address_2, 
        :city, 
        :state, 
        :zip, 
        :phone_work, 
        :phone_mobile, 
        :phone_other, 
        :phone_other_type 

    def full_name 
    [self.first_name, self.last_name].compact.join(' ') 
    end 

end 
1

不一定是辦法做到這一點,但我沒有通過重寫method_missing方法,然後打電話給我聚合的對象類似的東西。因此,它看起來是這樣的:

class User 
    def method_missing(method_id) 
    self.contact_info.send(method_id) 
    end 
end 

編輯1:更好地執行(我認爲這將工作):

class User 
    alias_method :orig_method_missing, :method_missing 

    def method_missing(method_id) 
    if (self.contact_info.respond_to?(method_id)) 
     self.contact_info.send(method_id) 
    else 
     orig_method_missing(method_id) 
    end 
    end 
end 

以上的優點是所有其他未知的方法調用將得到正確傳遞。

+0

感謝你爲這個。你的推薦讓我走上了正軌。 – 2010-01-08 23:51:48

8

使用委託:

class User < ActiveRecord::Base 
    has_one :contact_info, :as => :contactable 

    delegate :name, :name=, :email, :email=, :to => :contact_info 
end 
+0

這看起來應該可以工作,但我得到一個錯誤,例如。 我實施委託:電子郵件:contact_info,但它仍然無法正常工作。 >> u = User.create(:email =>「[email protected]」) ActiveRecord :: UnknownAttributeError:unknown屬性:電子郵件 – 2010-01-08 22:53:22

+4

這是隻讀的代表團,如果你想寫也,你需要添加setter方法「委託:名稱,電子郵件,:姓名=,:電子郵件=」 – amitkaz 2010-01-08 23:23:36

+0

謝謝。這讓我再次滾動。這有助於解決我的問題。我發佈我的解決方案。 – 2010-01-09 03:31:42