kch*_*kch 20 ruby inheritance metaprogramming class
#inherited在class Foo声明之后立即调用.我想要一些只在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.
Run Code Online (Sandbox Code Playgroud)
这样的事情存在吗?可以创建吗?我完全没有运气吗?
Son*_*tos 10
我迟到了,但我想我有一个答案(对任何访问这里的人).
您可以跟踪,直到找到类定义的结尾.我用一种方法做到了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
Run Code Online (Sandbox Code Playgroud)
输出:
YYY
XXX
Run Code Online (Sandbox Code Playgroud)
你可能运气不好.但这只是一个警告,而不是一个明确的答案.
Ruby挂钩了类定义的开头,而不是结尾,因为Class#inheritedb/c ruby类定义没有真正的结束.他们可以随时重新开放.
几年前有一些关于添加const_added触发器的讨论,但它还没有通过. 来自马茨:
我不打算实现所有可能的钩子.所以当有人提出更具体的用法时,我会再次考虑这个问题.它会
const_added,而不是class_added.
所以这可能会处理你的情况 - 但我不确定(它可能在一开始就触发,当它最终实现时).
你想用这个触发器做什么?可能还有另一种方法可以做到这一点.
看看defined宝石。您可以这样:
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
Run Code Online (Sandbox Code Playgroud)
输出:
B was defined
A was inherited by B
Run Code Online (Sandbox Code Playgroud)
但是,self.defined将在每个类定义之后触发。因此,如果您添加以下代码
class B < A
puts "B was redefined"
end
Run Code Online (Sandbox Code Playgroud)
你会看见
B was defined
A was inherited by B
B was redefined
A was inherited by B
Run Code Online (Sandbox Code Playgroud)
有一些方法可以避免这种情况,如果您愿意,我可以向您解释。
但是,如上所述,可能有更好的方法来解决您的问题。
使用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
Run Code Online (Sandbox Code Playgroud)
现在,您可以扩展您的类并定义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
Run Code Online (Sandbox Code Playgroud)