Rails Observer Alternatives for 4.0

前端 未结 12 761
伪装坚强ぢ
伪装坚强ぢ 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:40

    Take a look at Concerns

    Create a folder in your models directory called concerns. Add a module there:

    module MyConcernModule
      extend ActiveSupport::Concern
    
      included do
        after_save :do_something
      end
    
      def do_something
         ...
      end
    end
    

    Next, include that in the models you wish to run the after_save in:

    class MyModel < ActiveRecord::Base
      include MyConcernModule
    end
    

    Depending on what you're doing, this might get you close without observers.

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

    My alternative to Rails 3 Observers is a manual implementation which utilizes a callback defined within the model yet manages to (as agmin states in his answer above) "flip the dependency...coupling".

    My objects inherit from a base class which provides for registering observers:

    class Party411BaseModel
    
      self.abstract_class = true
      class_attribute :observers
    
      def self.add_observer(observer)
        observers << observer
        logger.debug("Observer #{observer.name} added to #{self.name}")
      end
    
      def notify_observers(obj, event_name, *args)
        observers && observers.each do |observer|
        if observer.respond_to?(event_name)
            begin
              observer.public_send(event_name, obj, *args)
            rescue Exception => e
              logger.error("Error notifying observer #{observer.name}")
              logger.error e.message
              logger.error e.backtrace.join("\n")
            end
        end
      end
    
    end
    

    (Granted, in the spirit of composition over inheritance, the above code could be placed in a module and mixed in each model.)

    An initializer registers observers:

    User.add_observer(NotificationSender)
    User.add_observer(ProfilePictureCreator)
    

    Each model can then define its own observable events, beyond the basic ActiveRecord callbacks. For instance, my User model exposes 2 events:

    class User < Party411BaseModel
    
      self.observers ||= []
    
      after_commit :notify_observers, :on => :create
    
      def signed_up_via_lunchwalla
        self.account_source == ACCOUNT_SOURCES['LunchWalla']
      end
    
      def notify_observers
        notify_observers(self, :new_user_created)
        notify_observers(self, :new_lunchwalla_user_created) if self.signed_up_via_lunchwalla
      end
    end
    

    Any observer that wishes to receive notifications for those events merely needs to (1) register with the model that exposes the event and (2) have a method whose name matches the event. As one might expect, multiple observers can register for the same event, and (in reference to the 2nd paragraph of the original question) an observer can watch for events across several models.

    The NotificationSender and ProfilePictureCreator observer classes below define methods for the events exposed by various models:

    NotificationSender
      def new_user_created(user_id)
        ...
      end
    
      def new_invitation_created(invitation_id)
        ...
      end
    
      def new_event_created(event_id)
        ...
      end
    end
    
    class ProfilePictureCreator
      def new_lunchwalla_user_created(user_id)
        ...
      end
    
      def new_twitter_user_created(user_id)
        ...
      end
    end
    

    One caveat is that the names of all events exposed across all the models must be unique.

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

    They are in a plugin now.

    Can I also recommend an alternative which will give you controllers like:

    class PostsController < ApplicationController
      def create
        @post = Post.new(params[:post])
    
        @post.subscribe(PusherListener.new)
        @post.subscribe(ActivityListener.new)
        @post.subscribe(StatisticsListener.new)
    
        @post.on(:create_post_successful) { |post| redirect_to post }
        @post.on(:create_post_failed)     { |post| render :action => :new }
    
        @post.create
      end
    end
    
    0 讨论(0)
  • 2020-12-04 05:50

    I think the the issue with Observers being deprecated is not that observers were bad in and of themselves but that they were being abused.

    I would caution against adding too much logic in your callbacks or simply moving code around to simulate the behavior of an observer when there is already a sound solution to this problem the Observer pattern.

    If it makes sense to use observers then by all means use observers. Just understand that you will need to make sure that your observer logic follows sound coding practices for example SOLID.

    The observer gem is available on rubygems if you want to add it back to your project https://github.com/rails/rails-observers

    see this brief thread, while not full comprehensive discussion I think the basic argument is valid. https://github.com/rails/rails-observers/issues/2

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

    I have the same probjem! I find a solution ActiveModel::Dirty so you can track your model changes!

    include ActiveModel::Dirty
    before_save :notify_categories if :data_changed? 
    
    
    def notify_categories
      self.categories.map!{|c| c.update_results(self.data)}
    end
    

    http://api.rubyonrails.org/classes/ActiveModel/Dirty.html

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

    Using active record callbacks simply flips the dependency of your coupling. For instance, if you have modelA and a CacheObserver observing modelA rails 3 style, you can remove CacheObserver with no issue. Now, instead say A has to manually invoke the CacheObserver after save, which would be rails 4. You've simply moved your dependency so you can safely remove A but not CacheObserver.

    Now, from my ivory tower I prefer the observer to be dependent on the model it's observing. Do I care enough to clutter up my controllers? For me, the answer is no.

    Presumably you've put some thought into why you want/need the observer, and thus creating a model dependent upon its observer is not a terrible tragedy.

    I also have a (reasonably grounded, I think) distaste for any sort of observer being dependent on a controller action. Suddenly you have to inject your observer in any controller action (or another model) that may update the model you want observed. If you can guarantee your app will only ever modify instances via create/update controller actions, more power to you, but that's not an assumption I would make about a rails application (consider nested forms, model business logic updating associations, etc.)

    0 讨论(0)
提交回复
热议问题