简体   繁体   中英

How to call super on dynamicly defined methods in sub-classes in Ruby?

I have this metaprogramming scenario that touches some of the finer features of Ruby that I am not quite sure how to solve.

Everyone can easilly program the example below to do what its supposed to do, but I need to define methods dynamicly in both super-classes and derived sub-classes.

In the example the base class has a dynamicly defined method :foo and of cource it is exposed to the sub-class B. However, when I call :foo on a instance of the sub-class B, which I detect in the dynamicly defined method I cant seem to pass it down from the B class instance down to the A class instance that of cource is what I want.

Sort of wierd ruby defines the method in the sub-class too like that, but anyways, thats what I am trying to work around.

Any clues?

class A
  def self.some_method method,*_params
    puts "#{name}: defining #{method}"
    define_method method do |*args|
      puts "#{self.class.name}: called #{method}"
      super *args unless self.class.instance_methods(false).include?(method)
    end
  end
end

class B < A
end

A.some_method :foo

B.new.foo

Output

A: defining foo
B: called foo
NoMethodError: super: no superclass method `foo' for #<B:0x007f9eab85bfa0>

I think there is a flaw in your logic.

You define the dynamic method to the superclass A . Calling B.new.foo find no method in B so it goes up the inheritance line. It finds the foo method in A so it uses that one.
That means that the

super *args unless self.class.instance_methods(false).include?(method)
part is not needed at all.

The way I see it, classic method inheritance is all you need!

The logic where indeed flawed. The need where of course to access the class that has the method defined, and super can't help you there.

Following is the solution to the problem:

class A
  def self.some_method method,*_params
    puts "#{name}: defining #{method}"
    define_method method do |*args|
      puts "#{self.class.name}: called #{method}"

      klass = self.class
      klass = klass.superclass while !klass.instance_methods(false).include?(method)

      # klass is now where the instance method is defined
      # and you can now access its class variables etc.
      puts "#{klass.name}: has method #{method}"
    end
  end
end

class B < A
end

A.some_method :foo

B.new.foo

Output

A: defining foo
B: called foo
A: has method foo

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