Rails Observer Alternatives for 4.0

前端 未结 12 760
伪装坚强ぢ
伪装坚强ぢ 2020-12-04 05:29

With Observers officially removed from Rails 4.0, I\'m curious what other developers are using in their place. (Other than using the extracted gem.) While Observers were cer

相关标签:
12条回答
  • 2020-12-04 05:30

    Wisper is a great solution. My personal preference for callbacks is that they're fired by the models but the events are only listened to when a request comes in i.e. I don't want callbacks fired while I'm setting up models in tests etc. but I do want them fired whenever controllers are involved. This is really easy to setup with Wisper because you can tell it to only listen to events inside a block.

    class ApplicationController < ActionController::Base
      around_filter :register_event_listeners
    
      def register_event_listeners(&around_listener_block)
        Wisper.with_listeners(UserListener.new) do
          around_listener_block.call
        end
      end        
    end
    
    class User
      include Wisper::Publisher
      after_create{ |user| publish(:user_registered, user) }
    end
    
    class UserListener
      def user_registered(user)
        Analytics.track("user:registered", user.analytics)
      end
    end
    
    0 讨论(0)
  • 2020-12-04 05:30

    You could try https://github.com/TiagoCardoso1983/association_observers . It is not yet tested for rails 4 (which wasn't launched yet), and needs some more collaboration, but you can check if it does the trick for you.

    0 讨论(0)
  • 2020-12-04 05:30

    It's worth mentioning that Observable module from Ruby standard library cannot be used in active-record-like objects since instance methods changed? and changed will clash with the ones from ActiveModel::Dirty.

    Bug report for Rails 2.3.2

    0 讨论(0)
  • 2020-12-04 05:35

    My suggestion is to read James Golick's blog post at http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html (try to ignore how immodest the title sounds).

    Back in the day it was all "fat model, skinny controller". Then the fat models became a giant headache, especially during testing. More recently the push has been for skinny models -- the idea being that each class should be handling one responsibility and a model's job is to persist your data to a database. So where does all my complex business logic end up? In business logic classes -- classes that represent transactions.

    This approach can turn into a quagmire (giggity) when the logic starts getting complicated. The concept is sound though -- instead of triggering things implicitly with callbacks or observers that are hard to test and debug, trigger things explicitly in a class that layers logic on top of your model.

    0 讨论(0)
  • 2020-12-04 05:37

    How about using a PORO instead?

    The logic behind this is that your 'extra actions on save' are likely going to be business logic. This I like to keep separate from both AR models (which should be as simple as possible) and controllers (which are bothersome to test properly)

    class LoggedUpdater
    
      def self.save!(record)
        record.save!
        #log the change here
      end
    
    end
    

    And simply call it as such:

    LoggedUpdater.save!(user)
    

    You could even expand on it, by injecting extra post-save action objects

    LoggedUpdater.save(user, [EmailLogger.new, MongoLogger.new])
    

    And to give an example of the 'extras'. You might want to spiffy them up a bit though:

    class EmailLogger
      def call(msg)
        #send email with msg
      end
    end
    

    If you like this approach, I recommend a read of Bryan Helmkamps 7 Patterns blog post.

    EDIT: I should also mention that the above solution allows for adding transaction logic as well when needed. E.g. with ActiveRecord and a supported database:

    class LoggedUpdater
    
      def self.save!([records])
        ActiveRecord::Base.transaction do
          records.each(&:save!)
          #log the changes here
        end
      end
    
    end
    
    0 讨论(0)
  • 2020-12-04 05:38

    In some cases I simply use Active Support Instrumentation

    ActiveSupport::Notifications.instrument "my.custom.event", this: :data do
      # do your stuff here
    end
    
    ActiveSupport::Notifications.subscribe "my.custom.event" do |*args|
      data = args.extract_options! # {:this=>:data}
    end
    
    0 讨论(0)
提交回复
热议问题