调用基于超级的__callee__值

时间:2018-06-20 12:16:43

标签: ruby-on-rails ruby super

方法super调用基于__method__的父方法。 如何基于__callee__调用父方法?

class SomeLogic
  DICTIONARY = {
    new_method_1: 'dictionary value 1',
    new_method_2: 'dictionary value 2'
  }

  def initialize(method_name)
    @method_name = method_name
  end

  def call
    DICTIONARY[@method_name]
  end

end

module M

  extend ActiveSupport::Concern

  def base_method
    logic = SomeLogic.new(__callee__).call

    if logic
      logic
    elsif defined?(__callee__)
      super # here analogue of super needed
    else
      {}
    end
  end

  alias_method :new_method_1, :base_method
  alias_method :new_method_2, :base_method
  alias_method :new_method_3, :base_method
end


class A

  prepend M

  def new_method_3
    'foo'
  end

end

预期结果:

A.new.new_method_1 # dictionary value 1
A.new.new_method_2 # dictionary value 2
A.new.new_method_3 # foo

当前结果:

A.new.new_method_1 # dictionary value 1
A.new.new_method_2 # dictionary value 2
A.new.new_method_3 # no superclass method `base_method' for A

2 个答案:

答案 0 :(得分:1)

如果您更改

class A
  prepend M

class A
  include M

您将获得所需的输出

答案 1 :(得分:0)

好的,这非常麻烦,但是可以按要求工作

class SomeLogic
  DICTIONARY = {
    new_method_1: 'dictionary value 1',
    new_method_2: 'dictionary value 2'
  }

  def initialize(method_name)
    @method_name = method_name
  end

  def call
    DICTIONARY[@method_name]
  end

end
module M

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

  def base_method(&block)
    SomeLogic.new(__callee__).call || 
    block&.call || # ruby < 2.3 block && block.call
    {}
  end

  module ClassMethods
    def method_added(method_name)
      if M.instance_methods.include?(method_name)
        orig = M.instance_method(method_name)
        M.remove_method(method_name)
        new = self.instance_method(method_name)
        self.prepend(Module.new do 
          define_method(method_name) do
            result = orig.bind(self).call(&new.bind(self))
          end   
        end)
        M.define_method(method_name, &orig.bind(M))  
      end
    end
  end

  alias_method :new_method_1, :base_method
  alias_method :new_method_2, :base_method
  alias_method :new_method_3, :base_method
  alias_method :new_method_4, :base_method
end

class A
  prepend M
  def new_method_3
    'foo'
  end
end

class B 
  prepend M 
end

因此,当您定义一个新方法时,我们将检查该方法是否已由M定义。如果是这样,那么我们捕获到UnboundMethodM中删除方法,然后捕获新定义为UnboundMethod,然后在匿名Module中将新方法定义为{{ 1}}实现将新方法实现作为proc传递给M实现,并将该模块放在定义的M之前,然后我们将Class的实现放回到{{1} },因此其他类别仍然有效。

结果:

M

如果您更喜欢M而不是p A.new.new_method_1 #=> 'dictionary value 1' p A.new.new_method_2 #=> 'dictionary value 2' p A.new.new_method_3 #=> 'foo' p A.new.new_method_4 #=> {} p B.new.new_method_3 #=> {} ,那么include可能看起来像

prepend