绑定方法到实例

时间:2015-02-24 21:25:42

标签: ruby metaprogramming

如果方法和实例都作为符号传递给方法,如果实例不是符号,是否有办法将现有方法绑定到对象的现有实例?

例如:

    def some_method
      #do something
    end

    some_instance = Klass.new(something)

    def method_that_binds(:some_method, to: :some_instance)
      #how do I do that?
    end

4 个答案:

答案 0 :(得分:2)

您的要求有点不寻常,但可以按照您的说法进行操作:

class Person; end
harry = Person.new
barry = Person.new

def test
  puts 'It works!'
end

define_method :method_that_binds do |a_method, to|
  eval(to[:to].to_s).singleton_class.send(:define_method, a_method, &Object.new.method(a_method))
end

method_that_binds :test, to: :harry
harry.test
# It works! will be sent to STDOUT
barry.test
# undefined method 'test'

这实际上并不使用命名参数,而是接受带有to键的哈希,但是您可以看到可以按照您想要的方式调用它。它还假定您定义的方法是在Object上全局定义的。

答案 1 :(得分:1)

如果您要将方法 添加到some_instance,即它在Klass的其他实例中不可用,那么可以这样做使用define_singleton_method(文档here。)

some_instance.define_singleton_method(:some_method, method(:some_method))

此处首次使用符号:some_method是您希望some_instance上的方法的名称,而第二次使用method的参数正在创建一个

。来自现有方法的Method对象。

如果您想使用与现有方法相同的名称,可以将其包装在您自己的方法中,如:

def add_method(obj, name)
  obj.define_singleton_method(name, method(name))
end

答案 2 :(得分:1)

您想要的API不容易使用,因为您必须知道要访问本地变量的范围。我不清楚为什么要传递局部变量的名称而不是传递局部变量的内容...毕竟,局部变量 出现在调用站点。

无论如何,如果你传入范围以及名称,这可以很容易地完成:

def some_method(*args)
  puts args
  puts "I can access some_instance's ivar: #@private_instance_var"
end

class Foo; def initialize; @private_instance_var = :foo end end

some_instance = Foo.new

def method_that_binds(meth, to:, within:, with: [])
  self.class.instance_method(meth).bind(within.local_variable_get(to)).(*with)
end

method_that_binds(:some_method, to: :some_instance, within: binding, with: ['arg1', 'arg2'])
# arg1
# arg2
# I can access some_instance's ivar: foo

如您所见,我还添加了一种将参数传递给方法的方法。没有这种扩展,它变得更加简单:

def method_that_binds(meth, to:, within:)
  self.class.instance_method(meth).bind(within.local_variable_get(to)).()
end

但是你必须将范围(Binding)传递给方法。

答案 3 :(得分:0)

假设我们有一个类A,其中包含方法a和本地变量c

class A
  def a; 10 end
end

c = '5'

我们希望将方法A#a添加到c

这就是它可以做到的方式

c.singleton_class.send :define_method, :b, &A.new.method(:a)
p c.b # => 10

说明

将方法添加到对象实例而不是其类的一种方法是在其单例类(每个ruby对象都有)中定义它。

我们可以通过调用相应的方法c来获取c.signleton_class的单例类。

接下来我们需要在其类中动态定义一个方法,这通常可以通过使用define_method来实现,:b将方法名称作为其第一个参数(在我们的例子中为Method)和一个块。现在,将方法转换为块可能看起来有点棘手,但这个想法相对简单:我们首先通过调用Object#method然后通过放置&将方法转换为A.new.method(:a)实例在to_proc之前,我们告诉解释器在我们的对象上调用Method方法(因为我们的返回对象是define_method的一个实例,Method#to_proc将被调用)之后,返回的proc将被转换为{{1}}期望作为其第二个参数的块。