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