我已经创建了一个模块来在类中的方法调用之前挂钩方法:
module Hooks
def self.included(base)
base.send :extend, ClassMethods
end
module ClassMethods
# everytime we add a method to the class we check if we must redifine it
def method_added(method)
if @hooker_before.present? && @methods_to_hook_before.include?(method)
hooked_method = instance_method(@hooker_before)
@methods_to_hook_before.each do |method_name|
begin
method_to_hook = instance_method(method_name)
rescue NameError => e
return
end
define_method(method_name) do |*args, &block|
hooked_method.bind(self).call
method_to_hook.bind(self).(*args, &block) ## your old code in the method of the class
end
end
end
end
def before(*methods_to_hooks, hookers)
@methods_to_hook_before = methods_to_hooks
@hooker_before = hookers[:call]
end
end
end
Run Code Online (Sandbox Code Playgroud)
我把模块包含在我的一个班级中:
require_relative 'hooks'
class Block
include Indentation
include Hooks
attr_accessor :file, :indent
before :generate, call: :indent
# after :generate, call: :write_end
def initialize(file, indent=nil)
self.file = file
self.indent = indent
end
def generate
yield
end
end
Run Code Online (Sandbox Code Playgroud)
此Block类是另一个实现其自身版本的generate方法并且实际实现的类的父类.
当我的代码运行时,method_added实际上是用方法调用的:在某种无限循环中生成as参数.我无法弄清楚为什么在这个无限循环中捕获了method_added.你知道这段代码有什么问题吗?这是完整代码的链接: 链接到github上的代码
你已经引起了无限的递归,因为你在define_method里面打电话method_added.堆栈跟踪(不幸的是你没有提供)应该显示这一点.
解决这个问题的一个稍微丑陋的解决方法可能是显式设置变量(例如@_adding_a_method)并将其用作保护子句method_added:
module ClassMethods
def method_added(method)
return if @_adding_a_method
if @hooker_before.present? && @methods_to_hook_before.include?(method)
# ...
@_adding_a_method = true
define_method(method_name) do |*args, &block|
# ...
end
@_adding_a_method = false
# ...
end
end
end
Run Code Online (Sandbox Code Playgroud)
然而,退后一步,我不确定这个模块试图实现的目标.难道你不能用Module#prepend这个元编程实现这个吗?
这段代码让我想起了你在一篇关于高级元编程技术的旧Ruby 1.8/1.9教程中可能会发现的内容.Module#prepend在大多数情况下使这些变通办法变得多余.