Create code dynamically with eval and here documents

Here documents, sometimes called "heredocs," can be a powerful way to spruce up a call to eval without declaring a method on a single line. Take, for instance, this code, which might reside in your Rails project's routes.rb file:

map.with_options(:controller => "items") do |obj|
  obj.items("/items", :action => "list")
  obj.item("/items/:id", :action => "show", :id => /\d+/)
end

map.with_options(:controller => "people") do |obj|
  obj.people("/people", :action => "list")
  obj.person("/people/:id", :action => "show", :id => /\d+/)
end

The above code uses the Object#with_options method, which makes it easy to extract out common parameters in method calls. Without the call to with_options, it would have been necessary to provide the ":controller => "items"" parameter in each of the route calls.

Anyway, back to the point. After creating the above code on one controller, you might want to do it for a second, and third, etc. That quickly becomes unmanageable. Enter eval and here documents:

named_routes = [:item, :person]

named_routes.each do |controller|
  eval <<-STR
    map.with_options(:controller => "#{controller.pluralize}") do |obj|
      obj.#{controller.pluralize}("/#{controller.pluralize}", :action => "list")
      obj.#{controller}("/#{controller.pluralize}/:id", :action => "show", :id => #{'/\d+/'})
    end
  STR
end

The here document is specified by providing "<<-STR." You can name your heredoc whatever you'd like, as long as you close it using the same name. You can even nest heredocs, but we're not focused on that right now. Combining eval and here documents makes your code more reusable and more maintainable, not to mention easier to read.

Leave a Reply