轨道观测替代品4.0(Rails Observer Alternatives for 4.0)

2019-07-20 19:22发布

随着正式观察员从Rails的4.0移除 ,我很好奇,其他开发人员使用自己的地方是什么。 (除了使用提取的宝石。)虽然观察员出席了肯定虐待,可以很容易地在时间不方便的成为,有他们在那里许多有益的使用情况只是缓存清理之外。

举个例子来说,需要跟踪更改模型的应用程序。 观察者可以很容易地观看有关模型的变化,并记录在数据库中与B型的变化。 如果你想观看跨越几个型号的变化,那么单站可以​​搞定。

在Rails 4,我很好奇,其他开发人员使用的地方观察什么样的战略来重建该功能。

就个人而言,我倾向于一种“脂肪控制器”的实施,在这些变化中的每个模型控制器的创建/更新/删除方法跟踪。 虽然它腌每个控制器的行为咯,它确实有助于可读性和理解所有的代码是在一个地方。 缺点是,现在有代码,这是非常相似遍布几个控制器。 提取代码到辅助方法是一种选择,但你还是留下电话到处散落的那些方法。 世界不是结束,但并不完全在“瘦控制器”的精神,无论是。

ActiveRecord的回调是另一种可能的选择,虽然一个我不喜欢的个人,因为它在我看来,往往夫妻两个不同的模型太紧密地联系在一起。

因此,在钢轨4,无观察员的世界里,如果你要创建一个新的纪录再创历史新高后创建/更新/销毁,你会用什么设计模式? 脂肪控制器,ActiveRecord的回调,或别的东西完全?

谢谢。

Answer 1:

看看关注

创建你的模型目录下名为关注的文件夹。 添加一个模块有:

module MyConcernModule
  extend ActiveSupport::Concern

  included do
    after_save :do_something
  end

  def do_something
     ...
  end
end

接下来,包括在模型中要在运行after_save的:

class MyModel < ActiveRecord::Base
  include MyConcernModule
end

根据你在做什么,这可能让你关闭而不观察员。



Answer 2:

他们是在一个插件了。

我也可以推荐的替代 ,这将给你喜欢控制器:

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


Answer 3:

我的建议是在读詹姆斯Golick的博客文章http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html (试图忽略如何不正派的标题声音)。

早在一天,这一切都是“胖模型,瘦控制器”。 那么胖模型成为一个巨大的头痛,尤其是在测试过程中。 最近,推一直瘦车型 - 想法是,每个类应处理一个责任和模特的工作就是你的数据保存到数据库中。 那么,这是否我所有的复杂的业务逻辑结束了? 在业务逻辑类 - 类,代表交易。

这种方法可以变成一个泥潭(giggity)当逻辑开始变得复杂。 这个概念是健全的,但 - 而不是用回调或难以测试和调试观察家隐含触发的东西,一类层逻辑明确触发的东西在你的模型之上。



Answer 4:

使用活动记录的回调只是翻转你的耦合的依赖。 举例来说,如果你有modelACacheObserver观察modelA轨3的风格,你可以删除CacheObserver没有问题。 现在,而不是说A具有手动调用CacheObserver保存后,这将是轨道4.您只是感动你的依赖,所以你可以安全地删除A ,但不是CacheObserver

现在,从我的象牙塔我喜欢观察依赖于它的观测模型。 难道我关心不够给弄乱我的控制器? 对我来说,答案是否定的。

想必你已经花了心思,为什么你想/需要的观测,并因此形成取决于其观测模型是不是一个可怕的悲剧。

我也有一个(合理的接地,我认为)厌恶任何形式的观察员依赖于一个控制器的动作。 突然间,你在任何控制器动作(或其他模型)可能会更新你要观察的模型注入您的观察者。 如果你能保证你的应用程序将只修改实例通过创建/更新控制器动作,更多的权力给你,但是这不是一个假设,我会做出一个Rails应用程序(考虑嵌套形式,对业务逻辑更新协会等)



Answer 5:

WISPER是一个很好的解决方案。 我的回调个人喜好是,他们被解雇的模型,但事件仅听取当一个请求进来,即我不想当我设置的测试等模型回调解雇你,但我也希望他们每当控制器参与解雇。 这是很容易与WISPER设置,因为你可以告诉它只能听一个块中的事件。

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


Answer 6:

在某些情况下,我只是用积极支持仪器仪表

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


Answer 7:

我的替代到Rails 3个观察员是一个手工执行,它利用模型中定义的回调还设法(如agmin上面他的回答状态)“翻转的依赖......耦合”。

我的对象从一个基类,它提供了一种为观察员继承:

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

(当然,在超过继承组合物的精神,上述代码可以被放置在模块中和在每个模型中混合。)

一个初始化寄存器观察员:

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

然后,每个模型可以定义自己观察到的事件,超出了基本的ActiveRecord的回调。 例如,我的用户模型公开2个事件:

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

一个希望接收通知这些事件的任何观察者仅需要(1)与公开的事件和(2)具有其名称的事件相匹配的方法的模型登记。 正如人们所预料的,多个观察者可以为同一个事件注册,并(在参考了原来的问题的第2段),观察员可以观看跨越几个型号的事件。

下面的NotificationSender和ProfilePictureCreator观察员类定义为通过各种模型公开的事件的方法:

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

一个需要注意的是,在所有模型中公开的所有事件的名称必须是唯一的。



Answer 8:

我觉得被弃用观察员的问题不在于观察员和他们自己的,但他们是被滥用的药物为不良。

我奉劝不要在你的回调加入过多的逻辑或者干脆将代码围绕模拟观察者的行为时,已经有一个完善的解决这个问题的观察者模式。

如果它是有道理通过各种手段使用观察者使用观察者则。 只要明白,你需要确保你的观察员逻辑遵循例如扎实稳健的编码实践。

如果你想将其添加回项目的观察员宝石可以用RubyGems的https://github.com/rails/rails-observers

看到这个简短的线程,而不是充分和全面的讨论,我觉得基本论点是有效的。 https://github.com/rails/rails-observers/issues/2



Answer 9:

你可以尝试https://github.com/TiagoCardoso1983/association_observers 。 它尚未被测试的轨道4(这是尚未推出),并且需要更多的合作,但你可以检查它是否是卓有成效的为您服务。



Answer 10:

如何使用PORO呢?

这背后的逻辑是,你的“上保存多余的动作”很可能将是业务逻辑。 这是我喜欢让来自AR模型(​​这应该是尽可能简单)和控制器(这是麻烦的正确测试)独立

class LoggedUpdater

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

end

并简单地称呼其为这样的:

LoggedUpdater.save!(user)

你甚至可以扩大它,通过注入额外的后攒动的物体

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

而带来的“额外”的一个例子。 你可能想,虽然漂亮起来了一下:

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

如果你喜欢这种方式,我建议读布莱恩Helmkamps 7种图案的博客文章。

编辑:我还应该提到的是,上述解决方案允许在需要的时候添加事务逻辑。 例如,使用ActiveRecord的和支持的数据库:

class LoggedUpdater

  def self.save!([records])
    ActiveRecord::Base.transaction do
      records.each(&:save!)
      #log the changes here
    end
  end

end


Answer 11:

值得一提的是, Observable在Ruby标准库模块不能主动记录状物体中使用,因为实例方法changed?changed将来自那些交锋ActiveModel::Dirty

为Rails 2.3.2错误报告



Answer 12:

我有同样的probjem! 我找到一个解决方案::加载ActiveModel肮脏的,所以你可以跟踪你的模型的变化!

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



文章来源: Rails Observer Alternatives for 4.0