Hiding Setup and Cleanup in a Block Method

Problem

You have a setup method that always needs to run before custom code, or a cleanup method that needs to run afterwards. You don't trust the person writing the code (possibly yourself) to remember to call the setup and cleanup methods.

Solution

Create a method that runs the setup code, yields to a code block (which contains the custom code), then runs the cleanup code. To make sure the cleanup code always runs, even if the custom code throws an exception, use a begin/finally block.

	def between_setup_and_cleanup
	 setup
	 begin
	 yield
	 finally
	 cleanup
	 end
	end

Here's a concrete example. It adds a DOCTYPE and an HTML tag to the beginning of an HTML document. At the end, it closes the HTML tag it opened earlier. This saves you a little bit of work when you're generating HTML files.

	def write_html(out, doctype=nil)
	 doctype ||= %{}
	 out.puts doctype
	 out.puts '

' begin yield out ensure out.puts '

' end end write_html($stdout) do |out| out.puts '

Sorry, the Web is closed.

' end # #

#

Sorry, the Web is closed.

#

Discussion

This useful technique shows up most often when there are scarce resources (such as file handles or database connections) that must be closed when you're done with them, lest they all get used up. A language that makes the programmer remember these resources tends to leak those resources, because programmers are lazy. Ruby makes it easy to be lazy and still do the right thing.

You've probably used this technique already, with the the Kernel#open and File#open methods for opening files on disk. These methods accept a code block that manipulates an already open file. They open the file, call your code block, and close the file once you're done:

	open('output.txt', 'w') do |out|
	 out.puts 'Sorry, the filesystem is also closed.'
	end

Ruby's standard cgi module takes the write_html example to its logical conclusion.[5] You can construct an entire HTML document by nesting blocks inside each other. Here's a small Ruby CGI that outputs much the same document as the write_html example above.

[5] But your code will be more maintainable if you do HTML with templates instead of writing it in Ruby code.

	#!/usr/bin/ruby

	# closed_cgi.rb
	require 'cgi'
	c = CGI.new("html4")

	c.out do
	 c.html do
	 c.h1 { 'Sorry, the Web is closed.' }
	 end
	end

Note the multiple levels of blocks: the block passed into CGI#out simply calls CGI#html to generate the DOCTYPE and the

tags. The

tags contain the result of a call to CGI#h1, which encloses some plain text in

tags. The program produces this output:

	Content-Type: text/html
	Content-Length: 137

	
	

Sorry, the Web is closed.

The XmlMarkup class in Ruby's builder gem works the same way: you can write Ruby code that resembles the structure of the document it creates:

	require 'rubygems'
	require 'builder'
	xml = Builder::XmlMarkup.new.message('type' => 'apology') do |b|
	 b.content('Sorry, Web Services are closed.')
	end
	puts xml
	# 
	# Sorry, Web Services are closed.
	# 

 

See Also

  • Recipe 6.13, "Locking a File," uses this technique to create a method that locks a file, and automatically unlocks it when you're done using it
  • Recipe 11.9, "Creating and Modifying XML Documents"
  • Recipe 20.11, "Avoiding Deadlock," uses this technique to have your thread lock multiple resources in the right order, and unlock them when you're done using them


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net