Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #6934 (closed enhancement: duplicate)

Opened 2 years ago

Last modified 7 months ago

[PATCH] belongs_to association supports before_add and after_add callbacks

Reported by: jchris Assigned to: core
Priority: normal Milestone: 2.x
Component: ActiveRecord Version: edge
Severity: normal Keywords:
Cc:

Description

There are many reasons before and after add callbacks will be useful on belongs_to associations. Here's one:

In my application, I have a set of associations that need to be maintained together. Specifically, I have versioned wiki pages, with comments. The comments belong to the wiki page itself, as well as to the particular version that was current when the comment was written. It looks like this in practice:

class Comment < ActiveRecord::Base

  belongs_to :wiki_page, :after_add => :associate_with_current_version
  belongs_to :wiki_page_version
  
  def associate_with_current_version wiki_page
    self.wiki_page_version = wiki_page.current_version
  end
end

Ok, so now I'm rethinking my schema here, and might just go with a simpler association only with the wiki_page_version, on the grounds that caching the wiki_page_id on the table is premature optimization. But callbacks on belongs_to associations seem just as useful as those on any other association.

If there is demand for it, I can also add the before_remove and after_remove callbacks.

My patch includes tests.

Attachments

add_callbacks_for_belongs_to.diff (7.6 kB) - added by jchris on 01/06/07 07:59:51.
code and tests for before_add and after_add callbacks on belongs_to associations

Change History

01/06/07 07:59:51 changed by jchris

  • attachment add_callbacks_for_belongs_to.diff added.

code and tests for before_add and after_add callbacks on belongs_to associations

07/03/07 17:47:11 changed by clawsoon

I'd love to have the :before_remove and :after_remove callbacks. Any chance you could make those happen, or do you need more demand than just me? :-)

A question: In your example, would:

wikipage.comments << comment2

or

wikipage.comments.create()

...trigger the belongs_to :after_add callback, or not?

Thanks.

Andrew

07/03/07 18:26:28 changed by jchris

Andrew, I'm not sure... I'm guessing yes, but it's been a while since I've used this code, so things may have changed. It should be trivial to add a test case that exercises the other side of the association. If I see interest in this patch picking up I'll be motivated to add the other callbacks, although this might be a good place for someone new to Rails' internals to dive in.

The work to add before and after remove callbacks would mirror the before and after add callbacks, so you can probably figure it out by reading this patch. Could be a good afternoon project.

07/03/07 18:55:43 changed by clawsoon

I guess I could've tried before I asked... looks like the belongs_to callbacks aren't triggered when the has_many object's << or .delete methods are called. Too bad. That means I'm left still looking for a callback-y thingie that's triggered whenever an association is changed, from whatever direction. (Or, rather, I'm stuck writing a motley assortment of callbacks in different classes to cover every possibility.)

I only started with Ruby and Rails about a week ago, so I'm not sure I'm quite ready to dig into the internals yet. Too many dragons. ;-)

11/02/07 03:59:42 changed by dysinger

  • milestone changed from 2.x to 2.0.

I also think this would be useful. I have a join object with a polymorphic end on one side. I would like to be able to use after_add on the belongs_to but it does look like it exists. Since it's polymorphic, I can't really add a after_add on the other end.

11/03/07 00:53:21 changed by nzkoz

  • milestone changed from 2.0 to 2.x.

11/15/07 01:10:09 changed by ggarside

There any plans to add this sooner than 2.x? I'm needing to trigger a model field change based on the changing of the reference to target of a belongs_to association and I've been banging my head against the wall trying to do this. I can't help thinking that having before/after add/remove callbacks would make this a lot easier. Though I have to admit this is probably a fairly rare use case.

+1 on this if it helps.

11/22/07 11:16:39 changed by Henrik N

Looks nice. Doesn't apply cleanly to current edge, though. Also, I think you should either implement or entirely remove the commented out *_remove bits.

If you update this to apply cleanly, I'll apply it, run the test suite and will +1 if it passes.

01/03/08 01:28:40 changed by pedrobelo

+1

just got a huge headache because this is not in yet (and can't use a custom rails version)

01/03/08 10:22:41 changed by ggarside

pedrobelo, you might be able to code round this, depending on how you need to use it, by aliasing the association writer method.

Something like this, the changing of Model#association should reset the value of Model#some_other_field

def association_with_trigger=(val)
  returning(self.send(:association_without_trigger=, val)) do
    if @association && @association.updated?
      @flag_some_other_field_should_change = true
    end
  end
end
alias_method_chain :association=, :trigger

before_save :act_on_flags

def act_on_flags
  if @flag_some_other_field_should_change
    @flag_some_other_field_should_change = false
    self.some_other_field = 'cheese'
  end
end

tweak as required

01/17/08 07:24:07 changed by pedrobelo

got it, thanks.

(but still +1! heh)

12/13/08 22:32:19 changed by bitsweat

  • status changed from new to closed.
  • resolution set to duplicate.