是否有类似于inheritance的类#的钩子只在Ruby类定义之后触发?
在class Foo
语句之后#inherited
调用#inherited
。 我想要的东西只能在关闭类声明的end
语句之后运行。
这里有一些代码来举例说明我的需求:
class Class def inherited m puts "In #inherited for #{m}" end end class Foo puts "In Foo" end puts "I really wanted to have #inherited tiggered here." ### Output: # In #inherited for Foo # In Foo # I really wanted to have #inherited tiggered here.
这样的事情存在吗? 可以创建吗? 我完全没有运气吗?
你可能运气不好。 但这只是一个警告,而不是一个明确的答案。
Ruby挂钩了类定义的开头,而不是结束,因为Class#inherited
b / c ruby类定义没有真正的结束。 他们可以随时重新开放。
几年前有一些关于添加const_added触发器的讨论 ,但它还没有通过。 来自马茨 :
我不打算实现所有可能的钩子。 所以当有人提出更具体的用法时,我会再次考虑这个问题。 它将是
const_added
,而不是class_added
。
所以这可能会处理你的情况 – 但我不确定(它可能在一开始就触发,当它最终实现时)。
你想用这个触发器做什么? 可能还有另一种方法可以做到这一点。
我迟到了,但我想我有一个答案(对任何访问这里的人)。
您可以跟踪,直到找到类定义的结尾。 我用一种叫做after_inherited
的方法after_inherited
:
class Class def after_inherited child = nil, &blk line_class = nil set_trace_func(lambda do |event, file, line, id, binding, classname| unless line_class # save the line of the inherited class entry line_class = line if event == 'class' else # check the end of inherited class if line == line_class && event == 'end' # if so, turn off the trace and call the block set_trace_func nil blk.call child end end end) end end # testing... class A def self.inherited(child) after_inherited do puts "XXX" end end end class B < A puts "YYY" # .... code here can include class << self, etc. end
输出:
YYY XXX
看看defined
gem。 你可以这样做:
require "defined" Defined.enable! class A def self.after_inherited(child) puts "A was inherited by #{child}" end def self.defined(*args) superclass.after_inherited(self) if superclass.respond_to?(:after_inherited) end end class B < A puts "B was defined" end
输出:
B was defined A was inherited by B
但是,每个类定义后都会触发self.defined
。 因此,如果您添加以下代码
class B < A puts "B was redefined" end
你会看见
B was defined A was inherited by B B was redefined A was inherited by B
如果你愿意,有办法避免我可以向你解释。
但是,如上所述,可能有更好的方法来解决您的问题。
使用set_trace_func
的技术不再有效。 改为使用TracePoint
,并在类发送:end
事件时进行跟踪。
此模块将允许您在任何类中创建self.finalize
回调。
module Finalize def self.extended(obj) TracePoint.trace(:end) do |t| if obj == t.self obj.finalize t.disable end end end end
现在,您可以扩展您的类并定义self.finalize
,它将在类定义结束后立即运行:
class Foo puts "Top of class" extend Finalize def self.finalize puts "Finalizing #{self}" end puts "Bottom of class" end puts "Outside class" # output: # Top of class # Bottom of class # Finalizing Foo # Outside class
如果您愿意假设您的Ruby实现了ObjectSpaces,您可以在事后查找所有模型实例,然后适当地修改它们。 谷歌建议http://phrogz.net/ProgrammingRuby/ospace.html
Rails有一个subclasses
方法,可能值得看一下实现:
class Fruit; end class Lemon < Fruit; end Fruit.subclasses # => [Lemon]
尝试自动为所有模型添加通用validation时,我遇到了同样的问题。 问题是,如果模型使用了#set_table_name,那么添加了基于DB数据类型的validation的代码会爆炸,因为它根据模型的名称猜测表名(因为#inherited被调用了在#set_table_name之前)。
就像你一样,我真的在寻找一种方法让#inherited在模型中的所有内容都已加载后启动。 但我真的不需要把它拿走那么远,我需要的就是在#set_table_name之后解雇的东西。 所以事实certificate这个方法很简单。 你可以看到我在这里做的一个例子: https : //gist.github.com/1019294
在上面的评论中,您说“我正在尝试向activerecord模型添加行为,但我需要在我搞乱之前完成所有模型自定义”。 所以我的问题是,如果您关注特定的模型自定义,如果是这样,那么也许您可以使用锯齿方法来实现您想要的结果。