是否有类似于继承的类#的钩子只在Ruby类定义之后触发?

kch*_*kch 20 ruby inheritance metaprogramming class

#inheritedclass 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)

  • 那只是让我发冷.:-)好工作的人,非常奇怪,完全可怕,但对于正确的问题... hawt. (2认同)

ram*_*ion 9

你可能运气不好.但这只是一个警告,而不是一个明确的答案.

Ruby挂钩了类定义的开头,而不是结尾,因为Class#inheritedb/c ruby​​类定义没有真正的结束.他们可以随时重新开放.

几年前一些关于添加const_added触发器的讨论,但它还没有通过. 来自马茨:

我不打算实现所有可能的钩子.所以当有人提出更具体的用法时,我会再次考虑这个问题.它会const_added,而不是class_added.

所以这可能会处理你的情况 - 但我不确定(它可能在一开始就触发,当它最终实现时).

你想用这个触发器做什么?可能还有另一种方法可以做到这一点.

  • 我相信const_added也会在const创建时触发.具有不同标准语法和元编程接口的ruby部分总是让我失望.像def/define_method一样.如果`类Foo; end`只是Class.new(superclass,&block)的语法糖,其中块将被传递给class_instance#instance_eval,可以只包装Class#initialize方法.哦,好吧,另一个星期一,另一天我考虑跳到Lisp. (4认同)

Sim*_*tsa 5

看看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)

有一些方法可以避免这种情况,如果您愿意,我可以向您解释。

但是,如上所述,可能有更好的方法来解决您的问题。

  • 顺便说一句,Ruby很酷,我们不需要Matz批准即可扩展语言:-) (5认同)
  • 我的意思是,定义的gem开始跟踪执行的红宝石代码行,以查找何时重新打开和关闭类。在Defined.enable之后,将在代码中的每条红宝石行上执行此跟踪功能!线。看一下已定义的gem源代码 (2认同)

use*_*951 5

使用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)