简体   繁体   中英

Getting ordered list of class/modules implementing instance methods for method inheritance order/super

I'm including several modules in a Ruby class (in Ruby 2.0.0p247), some that define the same instance method and are all included in the same class. I'd like some code to generate a hash of each method name symbol to the list of classes in the inheritance hierarchy that implement that method, in order that they would be executed. How would I do that?

(That way I know when I call each x method, if each were to do super if defined?(super) , I know the order of method execution.)

Eg given class C that implements instance method x, where C includes modules A and B that both implement instance method x, then I could use some code to inspect C to result in something like:

=> {:x=>[C, A, B], ...}

To clarify, I want to know all of the methods that are inherited through modules and class inheritance, not just those that were defined on C.

Another example to show what I'm looking for:

module A
  def v
  end
  def x
  end
end

module B
  def x
  end
  def y
  end
end

module D
  def a
  end
end

class E
  include A, B
  def x
  end
  def y
  end
  def z
  end
end

class C < E
  include D
end

For that the result for C should be:

 => {:a=>[D], :x=>[E, A, B], :y=>[E, B], :z=>[E], :v=>[A], :nil?=>[Kernel], :====>[Kernel], :=~=>[Kernel], :!~=>[Kernel], :eql?=>[Kernel], :hash=>[Kernel], :<=>=>[Kernel], :class=>[Kernel], :singleton_class=>[Kernel], :clone=>[Kernel], :dup=>[Kernel], :taint=>[Kernel], :tainted?=>[Kernel], :untaint=>[Kernel], :untrust=>[Kernel], :untrusted?=>[Kernel], :trust=>[Kernel], :freeze=>[Kernel], :frozen?=>[Kernel], :to_s=>[Kernel], :inspect=>[Kernel], :methods=>[Kernel], :singleton_methods=>[Kernel], :protected_methods=>[Kernel], :private_methods=>[Kernel], :public_methods=>[Kernel], :instance_variables=>[Kernel], :instance_variable_get=>[Kernel], :instance_variable_set=>[Kernel], :instance_variable_defined?=>[Kernel], :remove_instance_variable=>[Kernel], :instance_of?=>[Kernel], :kind_of?=>[Kernel], :is_a?=>[Kernel], :tap=>[Kernel], :send=>[Kernel], :public_send=>[Kernel], :respond_to?=>[Kernel], :extend=>[Kernel], :display=>[Kernel], :method=>[Kernel], :public_method=>[Kernel], :define_singleton_method=>[Kernel], :object_id=>[Kernel], :to_enum=>[Kernel], :enum_for=>[Kernel], :===>[BasicObject], :equal?=>[BasicObject], :!=>[BasicObject], :!==>[BasicObject], :instance_eval=>[BasicObject], :instance_exec=>[BasicObject], :__send__=>[BasicObject], :__id__=>[BasicObject]} 
Hash[
  C.instance_methods.map {|m|
    [m, C.ancestors.select {|a| a.instance_methods(false).include?(m)}]}]
# => { x: [C, A, B] }

My first attempt was:

instance_method_inheritance_chain = {}

C.instance_methods(false).each do |m|
  (instance_method_inheritance_chain[m] ||= []) << C
end

C.ancestors.each do |a|
  a.instance_methods(false).each do |m|
    (instance_method_inheritance_chain[m] ||= []) << a unless instance_method_inheritance_chain[m] && instance_method_inheritance_chain[m].include?(a)
  end
end

instance_method_inheritance_chain

But, using Jörg's answer as a starting point to shorten my previous answer, I came up with:

result = Hash[C.instance_methods(false).map {|m| [m, [C]]}]
C.ancestors.each {|a| a.instance_methods(false).each {|m| (result[m] ||= []) << a } }
result

or as a method:

def method_inheritance(klass)
  result = Hash[klass.instance_methods(false).map {|m| [m, [klass]]}]
  klass.ancestors.each {|a| a.instance_methods(false).each {|m| (result[m] ||= []) << a } }
  result # and maybe .reject{|m| m == :method_inheritance} if needed
end

method_inheritance(C)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM