I want the following module to be included in a class I have:
module InheritanceEnumerator
def self.included(klass)
klass.instance_eval do
instance_variable_set('@subclasses',[])
def self.subclasses
@subclasses
end
original_method = self.respond_to?(:inherited) ? self.public_method(:inherited) : nil
开发者_开发百科 instance_variable_set('@original_inherited_method', original_method)
def self.inherited(subclass)
@original_inherited_method.call(subclass) if @original_inherited_method
@subclasses<<subclass
end
end
end
end
What I'm trying to achieve is that I want my parent class to have references to direct children. I also need any other previous "inherited" methods set on my class by other stuff to stay in place. What am I doing wrong?
Your code works in this situation (for me):
class C; include InheritanceEnumerator; end
C.subclasses #=> []
class C1 < C; end
class C2 < C; end
C.subclasses #=> [C1, C2]
But fails in the following situation:
class C11 < C1; end
C1.subclasses => NoMethodError: undefined method `<<' for nil:NilClass
This is because you are only initializing @subclasses
when the module is included; but you are forgetting that subclasses of C
also have access to the modules methods but do not explictly include
it.
You fix this by doing the following:
def self.subclasses
@subclasses ||= []
@subclasses
end
def self.inherited(subclass)
@original_inherited_method.call(subclass) if @original_inherited_method
@subclasses ||= []
@subclasses << subclass
end
EDIT:
Okay, in future, please state what your problem is more fully and provide the test code you are using; as this was an exercise in frustration.
The following works fine with your code:
class C
def self.inherited(s)
puts "inherited by #{s}!"
end
include InheritanceEnumerator
end
class D < C; end #=> "inherited by D!"
C.subclasses #=> [D]
Perhaps the reason it wasn't working for you is that you included InheritanceEnumerator
before you had defined the inherited
method?
精彩评论