I'm building a plugin that will allow a developer to add various features to a class with a simple declaration in the class definition (following the normal acts_as pattern).
我正在构建一个插件,允许开发人员在类定义中使用简单声明向类添加各种功能(遵循正常的acts_as模式)。
For example, code consuming the plugin might look like
例如,使用插件的代码可能看起来像
class YourClass
consumes_my_plugin option1: :value1, specific_method_to_use: :your_method
end
My question arises because I want to error check that the value provided for the :specific_method_to_use parameter exists as a method, but the way code is typically organized and loaded, the method doesn't exist yet.
我的问题出现了,因为我想错误地检查为:specific_method_to_use参数提供的值是否作为方法存在,但代码通常是组织和加载的方式,该方法尚不存在。
The code in my plugin tentatively looks like this:
我的插件中的代码暂时看起来像这样:
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
This would work:
这可行:
class YourClass
def your_method; true; end
consumes_my_plugin option1: :value1, specific_method_to_use: :your_method
end
But this is how most people write code, and it would not:
但这是大多数人编写代码的方式,它不会:
class YourClass
consumes_my_plugin option1: :value1, specific_method_to_use: :your_method
def your_method; true; end
end
How can I fail at YourClass load time? I want it to error then, not at run time with a NoMethodError. Can I defer execution of the line that raises the ArgumentError until the entire class is loaded, or do something else clever to achieve that?
如何在YourClass加载时失败?我希望它出错,而不是在运行时使用NoMethodError。我可以推迟执行引发ArgumentError的行,直到加载整个类,或者做一些其他聪明的事情来实现吗?
1 个解决方案
#1
5
Use TracePoint
to track when your class sends up an :end
event.
使用TracePoint跟踪类何时发送:end事件。
Specific solution to your problem
具体解决您的问题
Here's how you can fit TracePoint
directly into your pre-existing module.
以下是如何将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
The examples below demonstrate that it works as specified:
以下示例表明它按指定的方式工作:
# `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
General solution
一般解决方案
This module will let you create a self.finalize
callback in any class.
此模块将允许您在任何类中创建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
Now you can extend your class and define self.finalize
, which will run as soon as the class definition ends:
现在,您可以扩展您的类并定义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
#1
5
Use TracePoint
to track when your class sends up an :end
event.
使用TracePoint跟踪类何时发送:end事件。
Specific solution to your problem
具体解决您的问题
Here's how you can fit TracePoint
directly into your pre-existing module.
以下是如何将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
The examples below demonstrate that it works as specified:
以下示例表明它按指定的方式工作:
# `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
General solution
一般解决方案
This module will let you create a self.finalize
callback in any class.
此模块将允许您在任何类中创建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
Now you can extend your class and define self.finalize
, which will run as soon as the class definition ends:
现在,您可以扩展您的类并定义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