Project

binder

0.0
No commit activity in last 3 years
No release in over 3 years
Binder allows you to evaluate a proc in a closure other than the one in which it was created. It also includes a method :bind to DRY up code for anyone creating a domain specific language.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies
 Project Readme

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!"