When you pass a block to a method, you expect the method to call back to the block with yield. A twist on callbacks is that an object can also pass itself to the block. Let’s see how this can be useful.
In the Faraday HTTP library, you typically initialize an HTTP connection with a URL and a block:
| require 'faraday' |
| |
| conn = Faraday.new("https://twitter.com/search") do |faraday| |
| faraday.response :logger |
| faraday.adapter Faraday.default_adapter |
| faraday.params["q"] = "ruby" |
| faraday.params["src"] = "typd" |
| end |
| |
| response = conn.get |
| response.status # => 200 |
This code sets the parameters for the connection. If you wish, you can get the same results by passing a hash of parameters to Faraday.new—but the block-based style has the advantage of making it clear that all the statements in the block are focusing on the same object. If you like this style, you might want to peek inside Faraday’s source code and see how it is implemented. Faraday.new actually creates and returns a Faraday::Connection object:
| module Faraday |
| class << self |
| def new(url = nil, options = {}) |
| # ... |
| Faraday::Connection.new(url, options, &block) |
| end |
| |
| # ... |
The interesting stuff happens in Faraday::Connection#initialize. This method accepts an optional block and yields the newly created Connection object to the block:
| module Faraday |
| class Connection |
| def initialize(url = nil, options = {}) |
| # ... |
| yield self if block_given? |
| # ... |
| end |
| |
| # ... |
This simple idiom is known as a Spell: . Self Yields are pretty common in Ruby—even instance_eval and class_eval optionally yield self to the block, although this feature is rarely used in practice:
| String.class_eval do |klass| |
| klass # => String |
| end |
For a more creative example of a Self Yield, you can check out the tap method.
In Ruby, it’s common to find long chains of method calls such as this:
| ['a', 'b', 'c'].push('d').shift.upcase.next # => "B" |
Chains of calls are frowned upon in most languages (and sometimes referred to as “train wrecks”). Ruby’s terse syntax makes call chains generally more readable, but they still present a problem: if you have an error somewhere along the chain, it can be difficult to track down the error.
For example, maybe you’re worried that the call to shift is not returning what you expect. To confirm your suspicions, you break the chain and print out the result of shift (or set a breakpoint in your debugger):
| temp = ['a', 'b', 'c'].push('d').shift |
| puts temp |
| x = temp.upcase.next |
<= | a |
This is a clumsy way to debug your code. If you don’t want to split the call chain, you can use the tap method to slip intermediate operations into the middle of a call chain:
| ['a', 'b', 'c'].push('d').shift.tap {|x| puts x }.upcase.next |
<= | a |
The tap method already exists on Kernel. However, it’s a good exercise to imagine how you would write it yourself if it weren’t already provided by Ruby:
| class Object |
| def tap |
| yield self |
| self |
| end |
| end |