2010-01-08 67 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