7

I have around 40 models in my RoR application. I want to setup a after_save callback for all models. One way is to add it to all models. Since this callback has the same code to run, is there a way to define it globally once so that it gets invoked for all models.

I tried this with no luck:

class ActiveRecord::Base

  after_save :do_something

  def do_something
    # .... 
  end
end

Same code works if I do it in individual models.

Thanks, Imran

Saim
  • 2,471
  • 5
  • 30
  • 43

5 Answers5

9

You should use observers for this:

class AuditObserver < ActiveRecord::Observer      

  observe ActiveRecord::Base.send(:subclasses)

  def after_save(record)
    AuditTrail.new(record, "UPDATED")
  end
end

In order to activate an observer, list it in the config.active_record.observers configuration setting in your config/application.rb file.

config.active_record.observers = :audit_observer

Note

In Rails 4, the observer feature is removed from core. Use the https://github.com/rails/rails-observers gem.

Harish Shetty
  • 64,083
  • 21
  • 152
  • 198
  • Great, thanks! so I need to add all the models to "observe", right? I hope there would be way to dynamically fetch the list of Models and pass it to "observe"? Thanks again. – Saim Oct 12 '10 at 11:13
  • I just found this: http://stackoverflow.com/questions/516579/is-there-a-way-to-get-a-collection-of-all-the-models-in-your-rails-app – Saim Oct 12 '10 at 11:21
  • Updated my answer, take a look. – Harish Shetty Oct 12 '10 at 11:31
  • 1
    That's cool. I think we need to add this line before the "observe" to make sure all models are loaded: Dir.glob(RAILS_ROOT + '/app/models/*.rb').each { |file| require file } Please correct me if I am wrong, or if there is a better to make sure all models are loaded. – Saim Oct 12 '10 at 11:57
  • If all your models are in the `models` directory this should work. Try it out and see. Should be pretty straight forward. Simply print the result of ` ActiveRecord::Base.send(:subclasses)` inside the `AuditObserver` class. – Harish Shetty Oct 12 '10 at 16:07
  • Is there any Rails 4 alternative coz this code is not working in rails 4 and yes I have added observer gem – Grey Aug 26 '14 at 14:44
  • This solution doesn't work for me, using Rails 4 and rails-observers 0.1.2. But I found you can just use `observe ActiveRecord::Base` and it will observe subclasses when they are defined (no need for eager loading or inspecting subclasses or descendants). – Anthony Smith Aug 26 '15 at 10:09
4

I'm pretty late on this one, but in case someone else is using Rails 3 and finds this, then this response might help.

Some models might not be loaded when the observer is loaded. The documentation says that you can override observed_classes, and that way you can get the subclasses of active record dynamically.

class AuditObserver < ActiveRecord::Observer
  def self.observed_classes                 
    ActiveRecord::Base.send(:subclasses)    
  end
end
aalin
  • 41
  • 2
1

This seemed to work for me:

ActiveRecord::Base.after_save do
   ...
end

Is there a problem I'm not seeing?

Jonathan Swartz
  • 1,913
  • 2
  • 17
  • 28
1

Based on @harish's answer and in this answer (https://stackoverflow.com/a/10712838/2226338):

class AuditObserver < ActiveRecord::Observer
    Rails.application.eager_load!
    observe ActiveRecord::Base.descendants

    def after_save(record)
        ...
    end
end
Community
  • 1
  • 1
ricardokrieg
  • 733
  • 8
  • 11
0

This actually works pretty well for me in 2.3.8:

class AudiObserver < ActiveRecord::Observer
  observe :'ActiveRecord::Base'
  #
  # observe methods...
  #
end
bramswenson
  • 71
  • 1
  • 2