9

Method#unbind returns an UnboundMethod reference to the method, which can later be bound to another object using UnboundMethod#bind.

class Foo
  attr_reader :baz

  def initialize(baz)
    @baz = baz
  end
end

class Bar
  def initialize(baz)
    @baz = baz
  end
end

f = Foo.new(:test1)
g = Foo.new(:test2)
h = Bar.new(:test3)
f.method(:baz).unbind.bind(g).call # => :test2
f.method(:baz).unbind.bind(h).call # => TypeError: bind argument must be an instance of Foo

Initially, I thought this is incredibly awesome, because I expected it would work similarly to JavaScript's Function.prototype.call()/Function.prototype.apply(). However, the object to which you want to bind the method must be of the same class.

The only application I can think of is if you unbind a method, lose the original implementation (redefine the method in the original or singleton class) and then rebind and call it.

ndnenkov
  • 35,425
  • 9
  • 72
  • 104
  • 1
    I found a neat explanation here - http://blog.jayfields.com/2006/12/ruby-alias-method-alternative.html – Wand Maker Nov 14 '15 at 12:16
  • @WandMaker, neat idea. It falls in the category I described. I would be surprised if this is the only reason for it's existence. – ndnenkov Nov 14 '15 at 12:49

2 Answers2

2

I'll summarize the good uses I found so far. Neither uses unbind though.


Firstly, overwriting a method, using the old implementation. Source, thanks to @WandMaker.

Let say you want to do something like this:

class Foo
  alias old_bar bar

  def bar
    old_bar
    some_additional_processing
  end
end

This will work, but will leave behind old_bar, which is not something desirable. Instead, one could do:

class Foo
  old_bar = instance_method(:bar)

  define_method(:bar) do
    old_bar.bind(self).call
    some_additional_processing
  end
end

Secondly, calling another implementation of a method from the hierarchy. Source.

The very practical example given in the post was that often times during debugging, you want to find where a method was defined. Generally speaking you can do that by:

method(:foo).source_location

However, this won't work if the current instance implements a method method, like is the case with ActionDispatch::Request. In that case you can do:

Kernel.instance_method(:method).bind(self).call(:foo).source_location
ndnenkov
  • 35,425
  • 9
  • 72
  • 104
1

Method and UnboundMethod types expect that the bind target must be subclass of the original class where you have referenced the method. However the Method has a #to_proc method implemented and with that, you can get rid off the 'same class type' constraint.

You have to use #send method, as #define_method is private (you cannot call it directly).

class A
  def hoge ; "hoge" ; end
end

class B ; end

hoge = A.new.method(:hoge)

B.send(:define_method, :hoge_in_b, &hoge) #converting to proc

b = B.new
puts b.hoge_in_b
karatedog
  • 2,508
  • 19
  • 29
  • 1
    Firstly, this will evaluate the block in the context of the original object (aka the `A.new` object, not the `B.new` one). Secondly, this doesn't answer the question of what is the point of the unbinding mechanic. – ndnenkov Nov 15 '15 at 19:42
  • Of course it will evaluate it in the context of A.new, because it is a `closure`. There is no high level language that uses functions as first class objects but only use the variable's `name` and evaluate them in a different context (aside from `eval`). This way you could use a variable in context `A` that does not exist in context `B`. And the point if the unbinding mechanic? It is like the point of `Proc, Lambda or Block` implementations in Ruby. They do the same thing, only with small differences. UnboundMethod is just one of the many tool, with its own properties (and its shortcomings). – karatedog Nov 15 '15 at 20:24
  • there is no intrinsic need for closures to contain instance variables. Blocks and procs are very separate things. One is a syntax, the other is actual object. As for regular procs vs lambdas - I use both often. This means that even if differences are subtle, they are significant enough so that each construct has its own niche. You are correct that Ruby comes with a lot of mechanics that a person will pretty much never use in his production codebase. Still, even with things like throw/catch, fibers and global variables, I can at least see were they are coming from. – ndnenkov Nov 16 '15 at 22:39
  • The *definition* of closure is that it contains locally scoped variables (the references to them), when the closure is created. Without that, it is not closure. That piece of code wouldn't even work anymore if all the variables inside it are lost. – karatedog Nov 16 '15 at 23:20
  • Check the Method Wrapping pattern here: http://stackoverflow.com/a/4471202/216248. It uses the UnboundMethod to take hold of a method's old version so the 'actual' version can be modified (works as a local `super`) – karatedog Nov 19 '15 at 09:50