检查是否调用了类方法

时间:2016-01-13 10:09:52

标签: ruby

我有以下模块和类:

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方法。

2 个答案:

答案 0 :(得分:0)

从技术上讲,@ nnd是对的,可以在评估完课程后调用它。但是,听起来你想要的是验证在类体定义中的某个点已经调用了configure方法(这也将允许已包含的任何模块完成评估,因此如果模块包含调用configure方法,一切都很好。

我可以在这里找到最接近解决这种情况的解决方案:

https://github.com/jasonayre/trax_core/blob/master/lib/trax/core/abstract_methods.rb

上面的代码是ruby的一个抽象方法实现,从技术上来说并不是你所要求的(你在谈论调用方法,抽象方法是关于检查子类定义它),但同样的技巧我使用那里可以应用。

基本上,我使用ruby的跟踪点库来监视要触发的类定义的结束,此时它会触发一个事件,我检查方法是否已定义,如果没有则抛出错误。因此,只要您从类中调用WITHIN,就可以使用类似的解决方案。像(未经测试)的东西:

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调试标志) - 我开了一个问题,试图在不必明确启用调试的情况下添加tracepoint。

https://github.com/jruby/jruby/issues/3096

答案 1 :(得分:0)

以下是基于您的结构的示例。 它会在实例化时检查是否已调用configure,并且会自动使用您在其上添加MyModule的任何类。

它检查每个实例化是否已调用configure,但它只是检查一个布尔值,因此它不会对性能产生任何影响。

我找了一种方法来取消定义特定类的前置方法,但没有找到任何东西。

module MyModule
  def self.prepended base
    base.extend(ClassMethods)
  end

  module ClassMethods
    attr_reader :config

    def configured?
      @configured
    end

    def configure &block
      @configured = true
      @config = {}
      block.call(@config) if block
    end
  end

  def initialize(*p)
    klass = self.class
    if klass.configured? then
      super
    else
      raise "Please run #{klass}.configure before calling #{klass}.new"
    end
  end
end

class A
  prepend MyModule

  configure do |config|
    config[:a] = true
    puts "A has been configured with #{config}"
  end
end

class B
  prepend MyModule
end

A.new
puts "A has been instantiated"

puts

B.new
puts "B has been instantiated"

# =>
# A has been configured with {:a=>true}
# A has been instantiated

# check_module_class.rb:27:in `initialize': Please run B.configure before calling B.new (RuntimeError)
#   from check_module_class.rb:50:in `new'
#   from check_module_class.rb:50:in `<main>'