Skip to content

Instantly share code, notes, and snippets.

@sobrinho
Last active December 21, 2015 12:58
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sobrinho/6309391 to your computer and use it in GitHub Desktop.
Save sobrinho/6309391 to your computer and use it in GitHub Desktop.
Track habtm changes on active record
# See https://groups.google.com/forum/#!topic/rubyonrails-core/Lb9rBkZnZSo
module TrackHabtmChanges
def self.included(model)
model.after_initialize :track_habtm_initial_state
model.after_save :track_habtm_initial_state
end
def changes
super.merge(habtm_changes)
end
def changed_attributes
changed_attributes = super
habtm_changes.each do |key, (_, new)|
changed_attributes[key] = new
end
changed_attributes
end
protected
def track_habtm_initial_state
@habtm_initial_state = {}
self.class.reflect_on_all_associations.each do |association|
next unless association.macro == :has_and_belongs_to_many
method = "#{association.name.to_s.singularize}_ids"
@habtm_initial_state[method] = send(method)
end
end
def habtm_changes
@habtm_initial_state.inject({}) do |habtm_changes, (association, initial_state)|
current_state = send(association)
if initial_state != current_state
habtm_changes[association] = [initial_state, current_state]
end
habtm_changes
end
end
end
@sobrinho
Copy link
Author

sobrinho commented Aug 6, 2014

Warning: It is slow when you fetch hundreds of objects (n + 1)

@monfresh
Copy link

Instead of this:

self.class.reflect_on_all_associations.each do |association|
  next unless association.macro == :has_and_belongs_to_many

you can do this:

self.class.reflect_on_all_associations(:has_and_belongs_to_many).each do |association|
...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment