I have 2 models that are linked through a polymorphic association
class MyModel < ActiveRecord::Base
has_many :taggings, :as => :taggable
has_many :tags, :through => :taggings
def tags=(ids)
self.tags.delete_all
self.tags << Tag.where(id: ids)
end
end
class Tagging < ActiveRecord::Base
include PublishablePolymorphicRelationship
belongs_to :tag
belongs_to :taggable, :polymorphic => true
end
class Tag < ActiveRecord::Base
has_many :taggings
has_many :my_models, :through => :taggings, :source => :taggable, :source_type => 'MyModel'
end
tag1 = Tag.create!(...)
tag2 = Tag.create!(...)
my_model = MyModel.create!(...)
my_model.update!(tags: [tag1.id])
I created a concern that implements the after_update
hook so that I can publish the changes on a message queue
However, when the hook is invoked, the changes hash is empty. as well as for the relation
module PublishablePolymorphicRelationship
extend ActiveSupport::Concern
included do
after_update :publish_update
def publish_update
model = self.taggable
puts model.changes
puts self.changes
... # do some message queue publish code
end
end
end This would return
{}
{}
Is there a way I can catch the changes for the polymorphic associations.
Ideally, I would not refer directly to the tags
model in the concern because I want this concern to be reusable for other models. I am open to adding bits of configuration in the model using the concern though.
Follow up question: Is this the right way to do this? I am surprised that the update hook is invoked in the first place. Perhaps I should act on either the creation or deletion hooks instead? I am open to suggestions.
It will never work as you think - taggings
is just a join model. Rows are only really inserted/deleted indirectly when you add/remove tags to an item. And when that happens there are no changes on either end of the association.
Thus unless you actually manually update the tagging and either end of the associations then publish_update
will return en empty hash.
If you want to create a resuable component that notifies you when a m2m association is created/destroyed you would do it like so:
module Trackable
included do
after_create :publish_create!
after_destroy :publish_destroy!
end
def publish_create!
puts "#{ taxonomy.name } was added to #{item_name.singular} #{ item.id }"
end
def publish_destroy!
puts "#{ taxonomy.name } was removed from #{item_name.singular} #{ item.id }"
end
def taxonomy_name
@taxonomy_name || = taxonomy.class.model_name
end
def item_name
@item_name || = item.class.model_name
end
end
class Tagging < ActiveRecord::Base
include PublishablePolymorphicRelationship
belongs_to :tag
belongs_to :taggable, polymorphic: true
alias_attribute :item, :taggable
alias_attribute :taxonomy, :tag
end
class Categorization < ActiveRecord::Base
include PublishablePolymorphicRelationship
belongs_to :category
belongs_to :item, polymorphic: true
alias_attribute :item, :taggable
alias_attribute :taxonomy, :tag
end
Otherwise you need to apply the tracking callbacks to the actual classes you are interested in the changes in.