2012-06-21 90 views
1

我對用戶模型的字段列表(User belongs_to的Location和belongs_to的Company):嵌套收集屬性更改ActiveRecord的

approval_fields = [:email, :location => [:first_name, :last_name], :company => [:name, :address]] 

我希望收集用戶的所有改變,當我試圖通過它來更新記錄代碼:

user.update_attributes(params[:user]) 

我已經寫了我一塊醜陋的代碼如下:

# Collects changed fields and returns hash of changes: 
# Example: approval_fields = [:email, :location => [:first_name, :last_name]] 
#   res = _collect_approval_changes(approval_fields) 
#   res # => {'email' => '[email protected]', 
#     'location_attributes' => {'first_name' => 'NewFirstName', 'last_name' => 'NewLastName'}} 
def _collect_approval_changes(approval_fields) 
    changes = {} 
    approval_fields.each do |f| 
    if f.is_a?(Hash) 
     key = f.keys.first 
     next unless self.public_send(key) # skip this association if associated object is nil 
     changes["#{key}_attributes"] ||= {} 
     f[key].each do |v| 
     if self.public_send(key).public_send("#{v}_changed?") 
      changes["#{key}_attributes"][v.to_s] = self.public_send(key).read_attribute(v) 
     end 
     end 
     changes.delete("#{key}_attributes") if changes["#{key}_attributes"].blank? 
    else 
     changes[f.to_s] = self.read_attribute(f) if self.public_send("#{f}_changed?") 
    end 
    end 
    changes 
end 

你能否給出建議如何重構此方法?謝謝!

回答

0

這是很多代碼來做一些Rails已經涵蓋的東西。

我看你已經在使用changed?的方法來比較check if an attribute has changed與數據庫中的當前值。

但是,由於您使用的是update_attributes,因此更改會立即保存,因此跟蹤更改變得更加困難。您可以在您的模型上使用before_save回調,該回調可以在更新之前跟蹤是否有變化。

例如:

before_save :check_changed 
def check_changed 
    puts (changed? ? "changed" : "unchanged") 
end 

或者你可以patch ActiveRecord itself,你叫update_attributes後返回更改的屬性:

module ActiveRecord 
    class Base 
    def update_attributes_changed(attributes) 
     self.attributes = attributes 
     changes = self.changes 
     return save, changes 
    end 
    end 
end 

status, changes = user.update_attributes_changed(params[:user])