gpt4 book ai didi

ruby - 如何设置 Hook 以在 Ruby 类定义的末尾运行代码?

转载 作者:数据小太阳 更新时间:2023-10-29 06:41:52 25 4
gpt4 key购买 nike

我正在构建一个插件,允许开发人员通过类定义中的简单声明(遵循正常的 acts_as 模式)向类添加各种功能。

例如,使用插件的代码可能如下所示

class YourClass
consumes_my_plugin option1: :value1, specific_method_to_use: :your_method
end

我的问题出现是因为我想错误检查为 :specific_method_to_use 参数提供的值是否作为方法存在,但代码通常组织和加载的方式尚不存在该方法。

我的插件中的代码暂定如下所示:

module MyPlugin
extend ActiveSupport::Concern

module ClassMethods
def consumes_my_plugin(options = {})
raise ArgumentError.new("#{options[:specific_method_to_use]} is not defined") if options[:specific_method_to_use].present? && !self.respond_to?(options[:specific_method_to_use])
end
end
end

这会起作用:

class YourClass
def your_method; true; end

consumes_my_plugin option1: :value1, specific_method_to_use: :your_method
end

但这是大多数人编写代码的方式,它不会:

class YourClass
consumes_my_plugin option1: :value1, specific_method_to_use: :your_method

def your_method; true; end
end

如何在 YourClass 加载时失败?然后我希望它出错,而不是在运行时出现 NoMethodError。我可以推迟执行引发 ArgumentError 的行,直到加载整个类,或者做一些其他聪明的事情来实现这一点吗?

最佳答案

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

OP问题的具体解决方案

以下是如何将 TracePoint 直接安装到您预先存在的模块中。

require 'active_support/all'

module MyPlugin
extend ActiveSupport::Concern

module ClassMethods
def consumes_my_plugin(**options)
m = options[:specific_method_to_use]

TracePoint.trace(:end) do |t|
break unless self == t.self

raise ArgumentError.new("#{m} is not defined") unless instance_methods.include?(m)

t.disable
end
end
end
end

下面的例子证明它按规定工作:

# `def` before `consumes`: evaluates without errors
class MethodBeforePlugin
include MyPlugin
def your_method; end
consumes_my_plugin option1: :value1, specific_method_to_use: :your_method
end

# `consumes` before `def`: evaluates without errors
class PluginBeforeMethod
include MyPlugin
consumes_my_plugin option1: :value1, specific_method_to_use: :your_method
def your_method; end
end

# `consumes` with no `def`: throws ArgumentError at load time
class PluginWithoutMethod
include MyPlugin
consumes_my_plugin option1: :value1, specific_method_to_use: :your_method
end

关于ruby - 如何设置 Hook 以在 Ruby 类定义的末尾运行代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32233860/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com