是否有类似于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 

看看definedgem。 你可以这样做:

 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模型添加行为,但我需要在我搞乱之前完成所有模型自定义”。 所以我的问题是,如果您关注特定的模型自定义,如果是这样,那么也许您可以使用锯齿方法来实现您想要的结果。