1st
(ActiveRecord::Callbacks + acts_as_taggable).gotcha?
Have you ever been in a situation where you have this strange bug, that takes you more time than expected to find out what (tha hell) isĀ going on? That happened to us (me and my team) last Rails Rumble when we were trying to have some stuff done after the creation of an element that had some tags associated to it.
The code looked something like the following:
## app/models/entry.rb
class Entry < ActiveRecord::Base
##################
### Extensions ###
##################
acts_as_taggable
#################
### Callbacks ###
#################
after_create :do_something_with_tags
def do_something_with_tags
self.tags.each do |tag|
# do something with tag
end
end
protected :do_something_with_tags
end
## app/controllers/entries_controller.rb
class EntriesController < ApplicationController
# ...
def create
@entry = Entry.new(params[:entry])
entry.tag_list = params[:tags]
if entry.save
# success
else
# failure
end
end
# ...
end
The problem was that the do_something_with_tags callback was not doing what it supposed to. At first we thought it was a problem with our implementation, but after some debugger sessions, we found out what the real problem was. The tags array didn’t correspond with the value of the the tag_list that was being assigned on the controller level.
After having some thoughts I decided to go inside the acts_as_taggable_redux source code (the version of acts_as_taggable that we were using), and I found something pretty peculiar related to the tag creation; This tags were being created on an after_save callback. The extract of the acts_as_taggable looked something like this:
def acts_as_taggable(options = {})
has_many :taggings, :as => :taggable, :dependent => :destroy, :include => :tag
has_many :tags, :through => :taggings, :order => 'LOWER(name) asc', :select => "DISTINCT tags.*"
after_save :update_tags
extend ActiveRecord::Acts::Taggable::SingletonMethods
include ActiveRecord::Acts::Taggable::InstanceMethods
end
As you can tell, an after_save was invoking this callback update_tags; after looking more closely, this method was the one that created the tag list, (the one we needed for our custom callback do_something_with_tags to work). So I checked in the Rails documentation page the precedence of callbacks just to be sure which one was being invoked first. Our main problem was that our callback was an after_create, and those get executed after the after_save callbacks.
To solve this issue, we simply called the update_tags callback directly on the first line of our callback, making the final do_something_with_tags look something like this:
def do_something_with_tags
update_tags # invoke callback by hand
self.tags.each do |tag|
# do something with tag
end
end
Because the acts_as_taggable library is so well implemented (at least the redux version, I’m not sure about others), the second time the update_tags method was being invoked simply returned without repeating the process of tag creation again.
Small issues like this are the ones that keep you having debugging fun for hours. I hope this may help you if you are this kind of trouble.
