我有以下模块和类:
module MyModule
def self.included base
base.extend(ClassMethods)
end
module ClassMethods
attr_reader :config
# this method MUST be called by every class which includes MyModule
def configure &block
@config = {}
block.call(@config) if block
end
end
end
class A
include MyModule
configure do |config|
# do sth with the config
end
end
class B
include MyModule
end
是否可以检查模块中的 configure
方法是否被调用?这意味着 A
应该没问题,但是 B
应该抛出错误,因为它从未调用过 configure
。
我在 self.included
回调中尝试过,但是之后调用了 configure
方法。
最佳答案
从技术上讲,@ndn 是正确的,它可以在类被评估后调用。但是,听起来您想要的是验证 configure 方法是否已在类主体定义中的某个点被调用(这也将允许任何已包含的模块完成评估,因此如果模块包含调用 configure方法,这一切都很好)。
我想出的解决这种情况的最接近的解决方案可以在这里找到:
https://github.com/jasonayre/trax_core/blob/master/lib/trax/core/abstract_methods.rb
上面的代码是 ruby 的抽象方法实现,从技术上讲这不是你要的(你说的是调用方法,抽象方法是检查子类是否定义了它),但我的技巧是一样的在那里可以应用。
基本上,我使用 ruby 的跟踪点库来监视要命中的类定义的末尾,此时它会触发一个事件,我检查该方法是否已定义,如果没有定义则抛出错误。因此,只要您从类中调用配置,类似的解决方案就可以为您工作。类似(未测试):
module MustConfigure
extend ::ActiveSupport::Concern
module ClassMethods
def inherited(subklass)
super(subklass)
subklass.class_attribute :_configured_was_called
subklass._configured_was_called = false
trace = ::TracePoint.new(:end) do |tracepoint|
if tracepoint.self == subklass #modules also trace end we only care about the class end
trace.disable
raise NotImplementedError.new("Must call configure") unless subklass._configured_was_called
end
end
trace.enable
subklass
end
def configure(&block)
self._configured_was_called = true
#do your thing
end
end
end
class A
include MustConfigure
end
class B < A
configure do
#dowhatever
end
end
class C < B
#will blow up here
end
或者,您可以尝试使用我的库中的 InheritanceHooks 模块并跳过手动跟踪点处理:
class BaseClass
include::Trax::Core::InheritanceHooks
after_inherited do
raise NotImplementedError unless self._configure_was_called
end
end
请注意,虽然我目前正在生产中使用这种模式,并且在 MRI 上一切正常,因为 tracepoint 是一个为调试而构建的库,但在使用 jruby 时存在一些限制。 (现在它会中断,除非您传递 jruby 调试标志)——我前段时间打开了一个问题,试图在不必显式启用调试的情况下添加跟踪点。
关于ruby - 检查是否调用了类方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34763817/