Книга: Metaprogramming Ruby 2
Назад: The Day of the Blocks
Дальше: Blocks Are Closures

Quiz: Ruby#

Where you’re challenged to do something useful with blocks.

Bill shares a little secret: “You know, a few years ago I was making a living out of writing C# code. I must admit that C# did have a few nice features. Let me show you one of those.”

The using Keyword

Imagine that you’re writing a C# program that connects to a remote server and you have an object that represents the connection:

 
RemoteConnection conn = new RemoteConnection(​"my_server"​);
 
String stuff = conn.ReadStuff();
 
conn.Dispose(); ​// close the connection to avoid a leak

This code correctly disposes of the connection after using it. However, it doesn’t deal with exceptions. If ReadStuff throws an exception, then the last line is never executed, and conn is never disposed of. What the code should do is manage exceptions, disposing of the connection regardless of whether an exception is thrown. C# provides a keyword named using that goes through the whole process for you:

 
RemoteConnection conn = new RemoteConnection(​"some_remote_server"​);
 
using (conn)
 
{
 
conn.ReadData();
 
DoMoreStuff();
 
}

The using keyword expects that conn has a method named Dispose. This method is called automatically after the code in the curly braces, regardless of whether an exception is thrown.

The Challenge

To refresh the basics of blocks, Bill throws a challenge at you: write a Ruby version of using. Make sure it passes this test:

 
require ​'test/unit'
 
require_relative ​'using'
 
 
class​ TestUsing < Test::Unit::TestCase
 
class​ Resource
 
def​ dispose
 
@disposed = true
 
end
 
 
def​ disposed?
 
@disposed
 
end
 
end
 
 
def​ test_disposes_of_resources
 
r = Resource.new
 
using(r) {}
 
assert r.disposed?
 
end
 
 
def​ test_disposes_of_resources_in_case_of_exception
 
r = Resource.new
 
assert_raises(Exception) {
 
using(r) {
 
raise Exception
 
}
 
}
 
assert r.disposed?
 
end
 
end

Quiz Solution

Take a look at this solution to the quiz:

 
module​ Kernel
 
def​ using(resource)
 
begin
 
yield
 
ensure
 
resource.dispose
 
end
 
end
 
end

You can’t define a new keyword, but you can fake it with a Kernel Method ()Kernel#using takes the managed resource as an argument. It also takes a block, which it executes. Regardless of whether the block completes normally, the ensure clause calls dispose on the resource to release it cleanly. There is no rescue clause, so any exception is still propagated to the code that calls Kernel#using.

Now that you’ve reviewed block basics, you can move to the second item on the list from : closures.

Назад: The Day of the Blocks
Дальше: Blocks Are Closures