Motivation

If you’re tired of creating APIs like this:

MyApp.configure do |c|
  c.description "My app's Description"
end

and you long to create APIs like this:

MyApp.configure do
  description "My app's Description"
end

Then look no further than this gem.

Installation

# gem install binder

Use

In truth, there’s no magic to this. If you want to change the context in which a block gets evaluated, you can use a combination of instance_eval and the fact that you can pass a proc to a method as if it were a block by prefixing it with an “&”:

class Dog
  def do_tricks(&block)
    instance_eval(&block)
  end

  def speak
    puts "ruff!"
  end

  def fetch
    puts "fetching...."
  end
end

Dog.new.do_tricks do
  speak
  fetch
end

If you’re creating a large API like the Rails 3 router, you may find yourself writing a whole lot of mehods that run the block passed to it in some other context.

To dry up the process of creating these methods, you can use the binder gem:

require 'binder'

class Dog
  extend Binder
  bind :do_tricks, :self

  def speak
    puts "ruff!"
  end

  def fetch
    puts "fetching...."
  end
end

Dog.new.do_tricks do
  speak
  fetch
end

First, we passed to bind the name of the method that we wanted to pass our block to; the second argument represents the context in which we want our block evaluated. :self, in this case, represents the instance of the Dog that we’re calling “do_tricks” on. We could have alternatively passed a symbol representing an instance method or an instance variable.

If you wanted to bind class method instead of instance methods, you simply have to extend the singleton class:

require 'binder'

class Dog
  class << self
    extend Binder
    bind :do_tricks, :self

    def speak
      puts "ruff!"
    end

    def fetch
      puts "fetching...."
    end
  end
end

Dog.do_tricks do
  speak
  fetch
end

If you’d rather not have to extend all of your classes with “Binder” everytime you intend to use the “bind” class method, you can require ‘binder/pervasive’ instead. Note, however, that this will pollute your namespace, so if you’ve happened to define a “bind” method in any of your existing classes, you may run into issues.

require 'binder/pervasive'

class Dog
  bind :do_tricks, :self

  def speak
    puts "ruff!"
  end

  def fetch
    puts "fetching...."
  end
end

Dog.new.do_tricks do
  speak
  fetch
end

Proc#bind_to

In addition to the “bind” method, binder also adds a “bind_to” instance method to the Proc class. It allows you to change the context in which the proc is run:

require 'binder'

def speak
  "why should i?"
end

class Dog
  def speak
      "ruff!"
    end
end

command = proc { speak }  

command.call
# ==> "why should i?"  

command.bind_to(Dog.new).call
# ==> "ruff!"

“Tell”

The “Tell” method is essentially short hand for an instance_eval on an object - it simply presents you with a more human readable way of using this language feature:

require 'binder/tell'

class Dog
  def speak
    puts "ruff!"
  end
end

fido = Dog.new

fido.instance_eval do
  speak
end

# ==> would print "ruff!"

Tell fido do
  speak
end

# or 

Tell(fido) { speak }

# or

Tell(fido, :to) { speak }

# ==> would all print "ruff!" - and these are all equivalent to the instance eval above

commands = proc { speak }

fido.instance_eval(&commands) 
# ==> would print "ruff"

Tell fido, commands 
# ==> would also print "ruff!"