适用于4.0的Rails Observer替代品

ken*_*nyc 149 ruby-on-rails ruby-on-rails-4

随着Observers正式从Rails 4.0中删除,我很好奇其他开发人员在他们的位置使用了什么.(除了使用提取的宝石.)虽然Observers肯定被滥用并且有时很容易变得笨拙,但是在缓存清除之外有许多用例,它们是有益的.

例如,需要跟踪模型更改的应用程序.观察者可以轻松地观察模型A的变化,并在数据库中记录模型B的变化.如果你想观察几个模型的变化,那么一个观察者就可以处理它.

在Rails 4中,我很好奇其他开发人员使用什么策略代替Observers来重新创建该功能.

就个人而言,我倾向于某种"胖控制器"实现,在每个模型控制器的创建/更新/删除方法中跟踪这些更改.虽然它稍微膨胀了每个控制器的行为,但它确实有助于可读性和理解,因为所有代码都在一个地方.缺点是现在的代码非常相似,分散在几个控制器中.将该代码提取到帮助器方法是一种选择,但您仍然可以调用遍布各处的方法.不是世界末日,也不是"精瘦控制者"的精神.

ActiveRecord回调是另一种可能的选择,虽然我个人并不喜欢,因为在我看来它往往会将两个不同的模型紧密地结合在一起.

所以在Rails 4中,no-Observers世界,如果你必须在创建/更新/销毁另一条记录之后创建一条新记录,你会使用什么样的设计模式?胖控制器,ActiveRecord回调,还是完全不同的东西?

谢谢.

Unc*_*dam 79

看看忧虑

在models目录中创建一个名为concern的文件夹.在那里添加一个模块:

module MyConcernModule
  extend ActiveSupport::Concern

  included do
    after_save :do_something
  end

  def do_something
     ...
  end
end
Run Code Online (Sandbox Code Playgroud)

接下来,在您希望运行after_save的模型中包含:

class MyModel < ActiveRecord::Base
  include MyConcernModule
end
Run Code Online (Sandbox Code Playgroud)

根据你正在做的事情,这可能会让你在没有观察者的情况下接近.

  • 这种方法存在问题.值得注意的是,它不会清理你的模型; _include_将方法从模块复制回到您的类中.将类方法提取到模块可以将它们按关注进行分组,但是类仍然是臃肿的. (20认同)
  • 标题是'Rails Observer Alternatives for 4.0'而不是'我如何最小化膨胀'.怎么担心不做Steven的工作呢?并且不,表明"膨胀"是为什么这不能作为观察者的替代品的原因还不够好.你必须提出一个更好的建议,以帮助社区或解释为什么问题不能作为观察员的替代品.希望你能说两个= D. (14认同)
  • 膨胀始终是一个问题.一个更好的选择是[wisper](https://github.com/krisleech/wisper),如果实施得当,它可以让你通过将它们提取到与模型没有紧密耦合的单独类来清理问题. .这也使得单独测试变得容易得多 (10认同)
  • 模型膨胀或整个应用程序臃肿通过拉入宝石来做到这一点 - 我们可以将其留给个人偏好.感谢您的补充建议. (4认同)

Kri*_*ris 33

他们现在在插件中.

我还可以推荐一种替代方案,它可以为您提供以下控制器:

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
Run Code Online (Sandbox Code Playgroud)


Mik*_*keJ 21

我的建议是阅读James Golick的博客文章http://jamesgolick.com/2010/3/14/crazy-heretical-and-awesome-the-way-i-write-rails-apps.html(试着忽略如何标题听起来不谦虚).

回到那一天,这一切都是"胖模特,瘦瘦的控制者".然后脂肪模型成为一个巨大的头痛,特别是在测试期间.最近推出的是瘦模型 - 这个想法是每个类应该处理一个责任而模型的工作就是将数据保存到数据库中.那么我所有复杂的业务逻辑最终会在哪里结束?在业务逻辑类中 - 表示事务的类.

当逻辑开始变得复杂时,这种方法可能变成泥潭(giggity).这个概念虽然合理 - 而不是使用难以测试和调试的回调或观察器隐式触发事物,而是在模型顶层逻辑层的类中显式触发事物.

  • 在过去的几个月里,我一直为这个项目做过类似的事情.你最终会得到很多小服务,但是测试和维护它的难易程度肯定超过了它们的缺点.我对这个中型系统的相当广泛的规格仍然只需要5秒钟运行:) (4认同)

agm*_*min 13

使用活动记录回调只会翻转耦合的依赖关系.例如,如果你有modelACacheObserver观察modelArails 3样式,你可以删除CacheObserver没有问题.现在,反过来说A必须手动调用CacheObserver后保存,这将是rails 4.你只是移动你的依赖,所以你可以安全删除A但不是CacheObserver.

现在,从我的象牙塔,我更喜欢观察者依赖于它所观察的模型.我是否足够关心我的控制器?对我来说,答案是否定的.

大概你已经考虑了为什么你想要/需要观察者,因此创建一个依赖于它的观察者的模型并不是一个可怕的悲剧.

对于依赖于控制器动作的任何类型的观察者,我也有(合理的基础,我认为)厌恶.突然之间,您必须将观察者注入任何可能更新您想要观察的模型的控制器动作(或其他模型).如果您可以保证您的应用程序只会通过创建/更新控制器操作修改实例,那么您将获得更多权力,但这不是我对轨道应用程序的假设(考虑嵌套表单,模型业务逻辑更新关联等)


ops*_*psb 13

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
Run Code Online (Sandbox Code Playgroud)


Pan*_*nic 8

在某些情况下,我只使用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
Run Code Online (Sandbox Code Playgroud)