Ivar Ivar - 3 months ago 10
Ruby Question

Can I invoke a class method from an ActiveRecord::Concern without mixing it into another class?

I'm creating an ActiveSupport::Concern that defines several class methods using the

method. With a regular module it is possible to invoke the class methods directly using
NameOfModule.target_method
(e.g. in the stdlib class Math it's common to invoke acos like so
Math.acos(x)
) but I'm at a loss finding how to perform a similar invocation my
Concern
. Is this possible, if so, how?

Answer

No, you can't because the methods defined in the block of class_methods are actually defined in the module Foo::ClassMethods (Foo is your concern). Here is the relevant source code of ActiveSupport::Concern

module ActiveSupport
  # ...
  module Concern
    # ...
    def class_methods(&class_methods_module_definition)
      mod = const_defined?(:ClassMethods, false) ?
        const_get(:ClassMethods) :
        const_set(:ClassMethods, Module.new)

      mod.module_eval(&class_methods_module_definition)
    end
  end
end

You can see that class_methods just creates the module ClassMethods for you if it is not defined by yourself. The methods you defined are just instance methods in that module, so you can't call it at the module level.

Later on, the module ClassMethods will be extended by the class that includes your concern. Here is the relevant source code:

module ActiveSupport
  # ...
  module Concern
    def append_features(base)
      if base.instance_variable_defined?(:@_dependencies)
        # ...
      else
        # ...
        base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)  # <-- Notice this line
        # ...
      end
    end
  end
end
Comments