Книга: Metaprogramming Ruby 2
Назад: Nil Guards
Дальше: Symbol#to_proc()

Self Yield

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.

The Faraday Example

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.

The tap() Example

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
Назад: Nil Guards
Дальше: Symbol#to_proc()