I attended Jim Weirich’s Dependency Injection session at OSCON 2005 last week and over the weekend I started thinking about how I could create dynamic controller action names in Ruby on Rails using the method_missing technique he discussed.
I’m working on a new application in Rails, and if a user forgets their password I would like them to just click a single link to get their password emailed to them. Having them fill out yet another form is just uncalled for and passing the username as a get variable is just plain ugly.
What I want is a dynamic method name, so that instead of the link going to a generic action name like:
http://www.infused.org/account/send_password
It would go to an action name specific to the user:
http://www/infused.org/account/send_password_to_bob
To accomplish this, I override the default method_missing method in the account controller:
def method_missing(m)
send_passwd($1) if m.id2name =~ /^send_password_to_(.+)$/
redirect_to :action => 'login'
end
def send_passwd(login)
# send the password here
flash['notice'] = “Your password has been emailed to you.�
end
The method_missing method will catch any action names that don’t exist, so it first checks to see if the method name matches the regular expression /^send_password_to_(.+)$/ and if so, passes the matched username to the send_password method.
The final redirect to the login action will get called whether or not send_password was called. Otherwise, a missing template error would be displayed for anything not matching the send_password regular expression. I would rather raise the standard ActionController::UnknownAction error if the method name doesn’t match the send_password criteria, but there appears to be a bug in Rails 0.13.1 that prevents it from working. The ticket shows that this should be fixed by 1.0, which is right around the corner.
I think this is an elegant way to handle the problem without passing an ugly get parameter (or worse). Doesn’t it make more sense to say send_password_to_bob than it does to say send_password?to=bob?
Does anyone know of any problems I’m creating for myself by using this approach?