Problem with callbacks and has_many :through association


I've got a following scenario: Reply has_many :venues, :through
=> :venue_suggestions. When user answers to a question he can add
venues to it as suggestions. The problem is that these venues
(actually their IDs) are added to the reply form, before the reply is
created. Wnever a venue is added or removed from a reply, I'd like to
call a function that updates a score of the added venue.

And here's the problem:

- I can't use after_add/after_remove callbacks in Reply model, because
after_add is called before the reply is actually saved and the
associations are not built yet

- I can't use after_create/after_destroy callbacks in VenueSuggestion,
because after_destroy callback is never called. Looking at the code in
Rails 2.3.8 branch it seems that doing something like this:
reply.venue_ids = []
calls delete_all, which doesn't call any callbacks. In Rails 3 it
works in the same way.

So I guess I'm left with after_create and after_remove callbacks in 2
models... Does anyone have any idea how these callbacks should
actually work in has_many :through association?

Hi Szymon,

you can use after_create and after_destroy callback method in
venue_suggestions model.

So when ever you are assigning a new venue to a reply it will create a
record in venue_suggestions table and call the after_create method.

But the issue with after_destroy is:

if you are trying to remove venues from the reply means it will not fire
the after_destroy call_back. So better some thing like below:

r = Reply.first
r.venues << Venue.first
r.venues << Venue.last

#So it will fire the after_create call back.

#But if you want to remove a particular venue from the reply means, go
some thing like this

r.reply_venues.find_all_by_venue_id(1).each{|x| x.destroy}

but r.venues = [] wont call the after_destroy, cos these type of
assignment will fire the delete_all method against venue_suggestions
table where delete_all wont run any callbacks.

Hope this will help you, if you need further assistance on this, let me
Szymon Nowak wrote:

Thanks for the reply!

Basically the problem was that we were using a form were we had
dynamically added/removed hidden fields with venue IDs - they were
later passed as an array (:params => {:venue_ids => [something]}),
that's why I was mainly interested in reply#venue_ids= assignment. I
solved it with combination of after_create and after_remove callbacks,
but the main point is that it's not very well documented and it's
actually impossible to get desired behavior in case of habtm
association where only after_add/remove callbacks are available.

I'm not sure if the behavior of these callbacks is intended and it's
simply an issue with properly documenting these quirks or the behavior
itself should be fixed.