Active Record callbacks vs super

On the last podcast, a reference was made to using super instead of callbacks.

Can someone post an example of how that works?

Thanks!
Don

1 Like

The idea is that instead of:

class User < ActiveRecord::Base
  after_save :do_something_after
end

You would do something like:

class User < ActiveRecord::Base
  
  # override ActiveRecord::Base#save
  def save
    super
    do_something_after
  end
end

That would replicate the after_save callback by calling super to do the save and then calling do_something_after when that’s done. You could call methods before super to handle before_save

6 Likes

awesome… thanks!

Aside from the taboo of overriding ‘save’, what does this approach achieve?

I feel like we’re not getting rid of callbacks, but rather re-implementing them.

To me, the main benefit is that it uses traditional inheritance over features provided by the base class to mimic inheritance. Less importantly, It’s also immediately clear when looking at the class that save has some additional logic applied to it.

Why is overriding save taboo? Why would overriding an inherited method ever be taboo? Isn’t that a problem?

1 Like

Mmmm… I like it - makes the code feel more OOPish :slight_smile:

Along this same line… is it kosher to do something like the following, and still expect to have the same return value as if you called save directly? If the calculate_total method somehow fails, would this method return false and have the same behavior (e.g. return with failed validations, etc) if you called save directly and had a failure?

def calculate_total_and_save
  calculate_total && save
end

I vote for the callbacks - they’re provided by Rails specifically for that purpose so you don’t have to override their methods and possibly screw things up. Hooks like this are a time-honored way to provide safe injection of behavior into a package that you don’t necessarily control - i.e.,

  1. what if the signature changes: you have to figure that out and go in and change it accordingly.
  2. the callbacks allow for selective action, using the on:, only: keys - the example above doesn’t support that…
  3. Also, remember that you shouldn’t override initialize (Dale's Blog: Rails: Don't override initialize on ActiveRecord objects) … I think this is still the case…
  4. And what about programmers who have to maintain your code when they are used to the callback hooks that almost every app uses?

I’m sure there are more reasons, so why would you start doing things in a non-standard way which you cannot use consistently and which doesn’t provide the support that hooks do?

Yeah… possibly. Personally I haven’t run into a lot of problems with callbacks, other than managing a bunch of conditional callbacks. However, there seems to be a lot of noise in the community lately arguing against using callbacks ever, unless they’re dead simple.

I share your concern about ‘non-standard’ methodologies, but it seems using super is usually explicit enough to avoid too much confusion.

Regardless, I think the improper use of callbacks (and ActiveRecord in general) has definitely contributed to the rise of the discussion around using so-called “Service Objects” and “Form Objects”, etc.

Here are some of the resources that have ‘colored’ my view, fwiw :slight_smile:

  1. Ernie Miller’s ActiveRecord talk at Railsconf (http://erniemiller.org/2013/05/22/talk-an-intervention-for-activerecord) Slide 165 starts the callbacks section.
  2. http://samuelmullen.com/2013/05/the-problem-with-rails-callbacks/
  3. Caike Suiza’s Powerful Interfaces talk: http://www.confreaks.com/videos/902-railsconf2012-powerful-interfaces
2 Likes

@JESii, just to play devil’s advocate, here’s an answer to a few of your points.

what if the signature changes: you have to figure that out and go in and change it accordingly.

I think it’s pretty safe to say that initialize, save, create, and the like are not going to change their signature. Your code would already be broken if it did. That said, calling super handles signature changes pretty nicely.

the callbacks allow for selective action, using the on:, only: keys - the example above doesn’t support that…

So I can only find three cases where this is applicable, before_validate, after_validate, and after_commit. For the validation cases, Ruby provides if which also allows for selective action. on: :create becomes if new_record? and so on. I do think that after_commit is a case that can’t be replaced with super at the moment, if that is the case, it definitely won’t be the case for longer.

Also, remember that you shouldn’t override initialize (Dale's Blog: Rails: Don't override initialize on ActiveRecord objects) … I think this is still the case…

You’re right, and that’s something that’s being worked on. As of Rails 4, I believe this is only the case for STI that it’s problematic.

And what about programmers who have to maintain your code when they are used to the callback hooks that almost every app uses?

I’d make the point of what about programmers who have to maintain your app and are used to the way you handle this in Ruby? It’s quite easy to follow that something happens on create when you see

def create
  super
  do_something_else
end
1 Like
def create
  super
  do_something_else
end

if validation fails, does do_something_else get called?