public
Last active

  • Download Gist
gistfile1.rbx
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
module HasAttributesFor
module ActiveRecord
def self.included(ar)
ar.extend ClassMethods
end
 
module ClassMethods
def has_attributes_for(association)
primary_key_name = reflect_on_association(association).primary_key_name
class_eval %{
def #{association}_attributes=(params)
@original_#{association} ||= #{association}.clone
 
existing = new_record? ? {} : #{association}.index_by(&:id)
 
keep = []
(params||[]).each do |attributes|
id = attributes[:id].to_i
if id && r = existing[id]
r.attributes = attributes.except(:id)
else
# we use build so that it doesn't save the record yet instead of <<
r = #{association}.build(attributes.except(:id))
end
 
keep << r
end
 
# remove all the original records that were no longer referenced
# this will not actually delete these records, only unreference them
deletions = self.#{association} - keep
deletions.each do |r|
# important to use target here to remove the record from memory but not the database
self.#{association}.target.delete(r)
end
end
}
class_eval %{
def original_#{association}
@original_#{association}
end
def original_#{association}=(value)
@original_#{association} = value
end
}, __FILE__, __LINE__
class_eval %{
before_update do |p|
if p.original_#{association}
new_records = p.#{association}
p.original_#{association}.each do |r|
p.#{association}.delete(r) unless new_records.member?(r)
end
end
p.#{association}.each do |r|
if r.changed?
r.#{primary_key_name} = p.id
r.save
end
end
end
after_save do |p|
p.original_#{association} = nil
end
}, __FILE__, __LINE__
end
end
end
end

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.