【问题标题】:Ruby 1.9.3 -> 2.0 alias_method and extendRuby 1.9.3 -> 2.0 alias_method 和扩展
【发布时间】:2013-11-21 00:25:12
【问题描述】:

我正在尝试将 Ruby 1.9.3 应用程序升级到 2.0,除了一个小问题,一切似乎都很顺利。我编写了一个模块,将其包含在我的模型中以覆盖 activerecord 破坏。它将现有的destroy 方法别名为destroy!,然后覆盖destroy 以更改记录上的deleted_at 时间戳。只有当我升级到 ruby​​ 2.0 destroy! 不再破坏记录,但行为就像我的新覆盖方法一样。知道为什么会这样吗?更相关的代码部分如下。完整要点here.

  def self.included(base)                                                          
    base.class_eval do                                                             
      alias_method :destroy!, :destroy                                             
      alias_method :delete!, :delete                                               
      default_scope -> { where(:deleted_at => nil) }                               
    end                                                                            

    base.send :extend, ClassMethods                                                
    base.send :include, InstanceMethods                                            
  end

【问题讨论】:

  • 这是解决您最初问题的一种非常糟糕的方法。覆盖核心方法(如销毁或删除)将导致您和任何其他从事此工作的开发人员非常头疼...我建议使用类似update_deleted_at 的方法(这实际上是您正在做的)而不是覆盖destroy 方法。仅仅因为你可以用 ruby​​ 覆盖某些东西,并不意味着你应该...
  • 我同意@tyler 的观点,我想不出一个令人信服的理由,这样可以节省比维护它所浪费的时间更多的时间。
  • 虽然我可以理解这种情绪,但我的目标不是节省时间,而是防止意外删除数据并使其对外部用户透明。我肯定会继续修改我的解决方案,但如果只是出于好奇,您对我的问题有答案吗?

标签: ruby-on-rails ruby activerecord ruby-2.0


【解决方案1】:

查看paranoia gem。这是一个兼容 Rails 3/4 的软删除实现,可以满足您的需求。如果您只想提供软删除,那么我会使用 gem 并完成它。如果您想要自己实现软删除,那么该实现可以让您深入了解之前的实现方式。

【讨论】:

  • 是的,我看了偏执狂,但认为它对我们来说太重了。当我们弄清楚我们希望它如何工作时,我希望能够灵活地改变行为。不过,我已经查看了一些实现以寻找灵感。我再看看。
  • 如果您处于项目的早期阶段,那么我会选择最简单的问题解决方案,特别是如果它的实施与您的计划一致。根据个人经验,我可以告诉您,“……但我们可能希望这样做……”的开发方法会让您陷入大量无法支持的代码。等待该功能要求将其包含在内,然后重构(在这种情况下,这应该就像为您自己切换一个社区 gem 一样简单)。也就是说,Rails 添加了 alias_method_chain 宏就是为了这个目的。
  • 在这种情况下,解决问题的最简单方法是自己构建它,而不是使用重量级的解决方案。知道我们对我们希望它在未来如何工作没有明确的理解,我选择自己构建它,这样它就可以轻松扩展,而不必围绕一个 gem 进行破解。代码不是很复杂,并且经过了很好的测试,因此维护应该不会很麻烦。我不止一次被那些超出我需要或以奇怪方式与他人互动的宝石咬伤。
【解决方案2】:

如果您为当前类中未直接定义的方法设置别名,则alias 在执行该方法的类的最近祖先中查找该方法。

当您将Trashable::InstanceMethods 包含到您的一个模型中时,它会插入到该模型的祖先链的前面。因此,在该模型中调用 destroy! 会触发 Trashable::InstanceMethods 上的 destroy 方法。

如果您将def destroyInstanceMethods 移动到base.class_eval,那么它将直接在包含模型中定义,并且包含“destroy”的该模型的最近祖先将是ActiveRecord 中的相关模块。因此,调用 destroy! 将按预期触发 SQL DELETE

请参阅class.ancestors 以进一步探索此行为。

【讨论】:

  • 甜,成功了。谢谢!奇怪的是它在 1.9.3 中的行为与 2.0.0 不同。两者的血统看起来都一样。比如:[..., Trashable::InstanceMethods, Trashable, ..., ActiveRecord::Base, ...].
【解决方案3】:

在 Ruby 2.0 中,他们引入了前置模块的概念,因此您可以在模型和 ActiveRecord::Base 之间插入行为。我建议将您的代码移动到一个模块中,而不是包含该模型,您可以预先添加它。从周围的别名方法中保存。

以下是一些与新的前置功能相关的文章:

https://gist.github.com/mattetti/5104790

http://blog.crowdint.com/2012/11/05/3-killer-features-that-are-coming-on-ruby-2-0.html

http://dev.af83.com/2012/10/19/ruby-2-0-module-prepend.html

【讨论】:

  • 与上述答案一样,您正在实施的更多的是安全删除......即。将记录设置为已删除而不实际删除它。对于这类工作,我通常使用状态机(通常是 state_machine gem:github.com/pluginaweek/state_machine),因为它可以轻松自定义您想要发生的事情的行为并提供各种有用的方法。这是一篇关于它的好文章:mojolingo.com/blog/2013/state-machines
  • 实现状态机在这里根本没有帮助。无需管理复杂的状态转换。它要么被删除,要么没有。我想覆盖现有的删除功能,并且除非我专门查询它们,否则删除的记录不会出现在任何地方。
  • 查看@AndyV anser。 acts_as_paranoid 正是您正在寻找的...但是状态机不必很复杂,并且通常比管理布尔标志更好...即使您只有两个状态...但我同意你,对于一个偏执的删除类型的功能,状态机是矫枉过正的。使用paranoia
  • 我在上面提到过,我已经研究过那个宝石,发现它也有点矫枉过正,尽管我一直在使用它作为参考。他们处理它的方式只是完全重写原始的destroy 方法,而不是给它起别名。他们的 destroy! 方法在功能上与 rails 提供的方法冲突。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多