On 3/14/07, Benjohn B. [email protected] wrote:
I think I must be getting too old, or I’m being a spring time Scrouge…
While it’s neat, and it’s cool Ruby can do that stuff, I’ve got to
ask “Why?”.
I’ve come across this used in DSLs, but I generally think, “I’m sure
you’re going to get in to trouble down the line. Why don’t you just
use the regular syntax?”
Yes it’s powerful, and it’s useful, but it needs to be done with
thought and care. We used to call similar things like this in
Smalltalk “stupid Smalltalk tricks.” It’s like any other tool, it can
be used for good or evil, it’s the workman who determines this.
I just blogged about some of the ramifications of this this morning.
(see my signature for the URL of my blog).
Rails makes use of this and similar techniques a lot. Two examples,
which I’ve recently encountered reading the rails code:
-
Named routes. Normally you set up routes which represent mappings
between urls and actions and their parameters with the connect method
of an internal class called ActionController::Routing::RouteSet in
code like this, in your config/routs.rb file:
ActionController::Routing::Routes.draw do | map |
map.connect ‘:controller/xyz/:arg1/:arg2’
end
Routes are used both to map an incoming url to an controller, action
and parameters, and also to map the other way to generate a url.
Routes defined above are anonymous, when asked to generate a url it
picks which route to use itself. But you can also name routes so that
you can explicitly give a particular route to use when generating a
url. Let’s say you want to
name that route above xyz. To do this you use the method xyz instead
of connect:
map.xyz 'controller/xyz/:arg1/:arg1'
which does the same thing as above but also defines a helper method
called xyz_url.
Of course the mechanism used to do this is to catch :xyz with
method_missing and turn it into a call to named_route passing the name
(:xyz) and any other parameters, which in turn invokes connect and
then creates the helper.
-
mime content negotiation
An action might be able to respond to a request with more than one
mime-type. An HTTP request can give a prioritized lists of mime-types
which the client will accept. The way to handle this negotiation in
an action is to use the respond_to method you might have code in an
action like:
respond_to do |wants|
wants.html { redirect_to(person_list_url) }
wants.js
wants.xml { render :xml => @person.to_xml(:include =>
@company) }
end
What this code says is something like:
if the requester wants html redirect to the person_list_url
if the requester wants java script perform the normal rendering
for javascript (such as look for an rjs template with the right name
and render that)
etc.
It’s kind of like a case statement, except what really happens also
depends on which of those alternatives came first in the requests list
of acceptable types.
Now what happens under the covers is that the respond_to method
creates an ActionController::MimeResponds::Responder object which
becomes map in the code above. Then it yields to the block. The html
method adds html to the list of available mime types, along with the
block, or as in the case of the invocation of the js method where no
block is given, a default block. Then once the block given to
respond_to returns, the Responder is asked to respond by executing the
first block found using the list of requested mime-types.
In Rails 1.2, ActionController::MimeResponds:Responder uses
method__missing to support new mime types not ‘baked in’ to Rails.
These are just two examples from Rails, there are many more.
It’s good to read such carefully crafted code and understand the
techniques and the possibilities, but I’d caution about getting too
carried away with such things before you are ready, and most folks
thing they are ready before they really are.
–
Rick DeNatale
My blog on Ruby
http://talklikeaduck.denhaven2.com/