Ruby mixin 和调用超级方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10808/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Ruby mixins and calling super methods
提问by Mike
Ok, so I've been refactoring my code in my little Rails app in an effort to remove duplication, and in general make my life easier (as I like an easy life). Part of this refactoring, has been to move code that's common to two of my models to a module that I can include where I need it.
好的,所以我一直在我的小 Rails 应用程序中重构我的代码以消除重复,并且总的来说让我的生活更轻松(因为我喜欢轻松的生活)。这种重构的一部分是将我的两个模型通用的代码移动到一个模块中,我可以将它包含在我需要的地方。
So far, so good. Looks like it's going to work out, but I've just hit a problem that I'm not sure how to get around. The module (which I've called sendable), is just going to be the code that handles faxing, e-mailing, or printing a PDF of the document. So, for example, I have a purchase order, and I have Internal Sales Orders (imaginatively abbreviated to ISO).
到现在为止还挺好。看起来它会成功,但我刚刚遇到了一个我不知道如何解决的问题。该模块(我称之为可发送)只是处理传真、电子邮件或打印文档 PDF 的代码。因此,例如,我有一个采购订单,我有内部销售订单(想象中缩写为 ISO)。
The problem I've struck, is that I want some variables initialised (initialized for people who don't spell correctly :P ) after the object is loaded, so I've been using the after_initializehook. No problem... until I start adding some more mixins.
我遇到的问题是,我想在加载对象后初始化一些变量(为拼写不正确的人初始化 :P ),所以我一直在使用after_initialize钩子。没问题......直到我开始添加更多的mixin。
The problem I have, is that I can have an after_initializein any one of my mixins, so I need to include a supercall at the start to make sure the other mixin after_initializecalls get called. Which is great, until I end up calling super and I get an error because there is no super to call.
我遇到的问题是,我可以after_initialize在我的任何一个 mixin 中使用一个,所以我需要在开始时包含一个超级调用,以确保其他 mixinafter_initialize调用被调用。这很好,直到我最终调用 super 并且我收到一个错误,因为没有可调用的 super。
Here's a little example, in case I haven't been confusing enough:
这是一个小例子,以防我还不够困惑:
class Iso < ActiveRecord::Base
include Shared::TracksSerialNumberExtension
include Shared::OrderLines
extend Shared::Filtered
include Sendable::Model
validates_presence_of :customer
validates_associated :lines
owned_by :customer
order_lines :despatched # Mixin
tracks_serial_numbers :items # Mixin
sendable :customer # Mixin
attr_accessor :address
def initialize( params = nil )
super
self.created_at ||= Time.now.to_date
end
end
So, if each one of the mixins have an after_initialize call, with a supercall, how can I stop that last supercall from raising the error? How can I test that the super method exists before I call it?
因此,如果每个 mixin 都有一个 after_initialize 调用,并且有一个super调用,我该如何阻止最后一个super调用引发错误?如何在调用之前测试 super 方法是否存在?
回答by valo
You can use this:
你可以使用这个:
super if defined?(super)
Here is an example:
下面是一个例子:
class A
end
class B < A
def t
super if defined?(super)
puts "Hi from B"
end
end
B.new.t
回答by newtonapple
Have you tried alias_method_chain? You can basically chained up all your after_initializecalls. It acts like a decorator: each new method adds a new layer of functionality and passes the control onto the "overridden" method to do the rest.
你试过alias_method_chain吗?您基本上可以将所有after_initialize呼叫链接起来。它就像一个装饰器:每个新方法都添加了一个新的功能层,并将控制权传递给“被覆盖”的方法来完成剩下的工作。
回答by James A. Rosen
The including class (the thing that inherits from ActiveRecord::Base, which, in this case is Iso) coulddefine its own after_initialize, so any solution other than alias_method_chain(or other aliasing that saves the original) risks overwriting code. @Orion Edwards' solution is the best I can come up with. There are others, but they are farmore hackish.
包含类(从 继承的东西ActiveRecord::Base,在这种情况下是Iso)可以定义自己的after_initialize,因此除alias_method_chain(或其他保存原始文件的别名)之外的任何解决方案都有覆盖代码的风险。@Orion Edwards 的解决方案是我能想到的最好的解决方案。还有其他的,但他们远远更多的hackish。
alias_method_chainalso has the benefit of creating named versions of the after_initialize method, meaning you can customize the call order in those rare cases that it matters. Otherwise, you're at the mercy of whatever order the including class includes the mixins.
alias_method_chain还具有创建 after_initialize 方法的命名版本的好处,这意味着您可以在重要的极少数情况下自定义调用顺序。否则,您将受到包含类包含 mixin 的任何顺序的支配。
later:
后来:
I've posted a question to the ruby-on-rails-core mailing list about creating default empty implementations of all callbacks. The saving process checks for them all anyway, so I don't see why they shouldn't be there. The only downside is creating extra empty stack frames, but that's pretty cheap on every known implementation.
我已经向 ruby-on-rails-core 邮件列表发布了一个关于创建所有回调的默认空实现的问题。无论如何,保存过程都会检查它们,所以我不明白为什么它们不应该在那里。唯一的缺点是创建了额外的空堆栈帧,但这对于每个已知的实现来说都非常便宜。
回答by James A. Rosen
You can just throw a quick conditional in there:
你可以在那里抛出一个快速的条件:
super if respond_to?('super')
and you should be fine - no adding useless methods; nice and clean.
你应该没问题——不要添加无用的方法;漂亮干净。
回答by Orion Edwards
Rather than checking if the super method exists, you can just define it
与其检查 super 方法是否存在,不如直接定义它
class ActiveRecord::Base
def after_initialize
end
end
This works in my testing, and shouldn't break any of your existing code, because all your other classes which define it will just be silently overriding this method anyway
这在我的测试中有效,并且不应该破坏您现有的任何代码,因为定义它的所有其他类无论如何都会默默地覆盖此方法

