0
votes

How do you make class methods defined within a nested series of modules propagate up the module mixin tree?

Consider the following:

module A
  def self.included(base)
    base.extend(ClassMethods)
  end

  def foo; end

  module ClassMethods
    def bar; end
  end
end

module B
  include A
end

class C
  include B
end

puts "B class methods:   #{(B.methods-Module.methods).inspect}"
puts "B instance methods #{B.instance_methods.inspect}"
puts "C class methods:   #{(C.methods-Class.methods).inspect}"
puts "C instance methods #{(C.instance_methods-Class.instance_methods).inspect}"

Class C does not inherit the class methods defined in A, even though it includes B.

B class methods:   [:bar]
B instance methods [:foo]
C class methods:   []
C instance methods [:foo]

Is there a neat way of ensuring the the class methods from A are propagated upwards into C (so I could call C.bar)? I'm looking for a nice generic method that doesn't involve specifically calling out and extending C with every inherited module.

Kind regards

Steve

2

2 Answers

0
votes

If you don't want to manually extend ClassMethods and you want anyone including B having its class interface extended by ClassMethods you could do that:

module B
  include A

  def self.included(base)
    base.extend(ClassMethods)
  end
end

Or you could have B defined as a class and have C inherit from it. I guess it all depends on your design and what exactly you are trying to achieve.

0
votes

OK - so I figured it out. It is a slight hack, because it depends on the module hierarchy always using the module name ClassMethods to hold any Class Methods.

However, the following works (in A) - it basically detects whether the base module has ClassMethods defined. If so, it adds A's Classmethods to the base ClassMethods. If not it creates a ClassMethods module in the base (cloning A's class methods)

 def self.included(base)
    extend ClassMethods

    if base.kind_of? Module  
      if  base.include? ClassMethods
         base::ClassMethods.extend(ClassMethods)
      else 
         base.const_set(:ClassMethods, ClassMethods)
         base.module_eval('def self.included(klass);klass.extend(ClassMethods.clone);end') 
      end
    end
  end