views:

36

answers:

1

I have a mixin for which I would like to get a list of all the classes that have included it. In the mixin module, I did the following:

module MyModule
  def self.included(base)
    @classes ||= []
    @classes << base.name
  end

  def self.classes
    @classes
  end
end

class MyClass
  include MyModule
end

This works pretty well:

> MyModule.classes #=> nil
> MyClass.new #=> #<MyClass ...>
> MyModule.classes #=> ["MyClass"]

Now, I would like to extract this part out into a separate module that can be included in my other mixins. So, I came up with the following:

module ListIncludedClasses
  def self.included(base)
    p "...adding #{base.name} to #{self.name}.classes"

    @classes ||= []
    @classes << base.name

    base.extend(ClassMethods)
  end

  def self.classes
    @classes
  end

  module ClassMethods
    def included(module_base)
      p "...adding #{module_base.name} to #{self.name}.classes"

      @module_classes ||= []
      @module_classes << module_base.name
      super(module_base)
    end
    def classes
      @module_classes
    end
  end

end

module MyModule
  include ListIncludedClasses
end

This doesn't work though, because the #included(module_base) method being added to MyModule from ListIncludedClasses is never getting run. Interestingly enough, it does successfully add #classes to MyModule.

> MyModule.classes #=> 
  "...adding Rateable to ListIncludedClasses.classes"
  => nil 
> ListIncludedClasses #=> ["MyModule"]
> MyClass.new #=> #<MyClass ...>
# ^^ THIS SHOULD BE ADDING MyClass TO MyModule.classes ^^
> MyModule.classes #=> nil

What am I missing?

+1  A: 

You probably should use extend instead of include since former adds class level methods, while latter - instance level methods (why you have access to @classes).

Try this:

module MyModule
  extend ListIncludedClasses::ClassMethods
end
Eimantas
I don't know if you missed it, but extend(ClassMethods) is in the self.included(base) method, so it is extending the class methods. There are other instance methods that need to be added; this is the popularly accepted technique to both including the instance methods and extending the class methods. Also, directly extending the class methods, as you suggested, makes no difference.
jangosteve