在ruby中引用define_singleton_method中的外部方法

时间:2017-11-04 20:14:18

标签: ruby metaprogramming

我的代码中存在一些范围问题,我真的不知道如何解决它们。

我有这段代码

class MainClass

    def methodOfMainClass
        #do something
    end

    def test obj
        obj.instance_variables.map do |attribute|
            obj.define_singleton_method(:something)  do |arg|
            self.methodOfMainClass()
        end
    end
end

当我执行它时,引发 NoMethodError :未定义的方法`methodOfMainClass'为

    #<Obj:0x0035f6274c9g12>

但是methodOfMainClass不是来自Obj,而是来自MainClass。如何在 define_singleton_method 方法中引用正确的类?

1 个答案:

答案 0 :(得分:2)

在ruby中self是一种方法,而不是参考。在调用self然后运行它的对象中创建单例方法时,它将在运行时引用该对象,而不是创建该方法的对象。

这里有一些能够满足你所要求的东西,尽管我无法想象它的用例。使您想要在MainClass中调用的方法成为类的单例方法(类方法),然后调用它从另一个对象的单例方法命名该类。

class OtherClass
  def initialize(ivar1: nil, ivar2: nil)
    @ivar1 = ivar1
    @ivar2 = ivar2
  end
end


class MainClass

  def self.methodOfMainClass
    puts "method #{__method__} called"
  end

  def test(obj)
    obj.instance_variables.map do |attribute|
      method_name = attribute.to_s.gsub(/@/,'')
      obj.define_singleton_method(method_name) do |arg|
        puts "#{__method__} called with #{arg}"
        MainClass.methodOfMainClass
      end
    end
  end

end

my_instance = MainClass.new
other = OtherClass.new(ivar1: 'hello', ivar2: 'world')

my_instance.test(other)
other.ivar1('something')
other.ivar2('else')

输出:

ivar1 called with something
method methodOfMainClass called
ivar2 called with else
method methodOfMainClass called

或者,如果您因某些原因不想创建类方法,可以在单例方法定义之外创建对self的引用并在其中使用:

class OtherClass
  def initialize(ivar1: nil, ivar2: nil)
    @ivar1 = ivar1
    @ivar2 = ivar2
  end
end


class MainClass

  def methodOfMainClass
    puts "method #{__method__} called"
  end

  def test(obj)
    ref = self
    obj.instance_variables.map do |attribute|
      method_name = attribute.to_s.gsub(/@/,'')
      obj.define_singleton_method(method_name) do |arg|
        puts "#{__method__} called with #{arg}"
        ref.methodOfMainClass
      end
    end
  end

end

my_instance = MainClass.new
other = OtherClass.new(ivar1: 'hello', ivar2: 'world')

my_instance.test(other)
other.ivar1('something')
other.ivar2('else')

输出与之前相同,但只要MainClass的实例超出范围,这将失败。