Today the underground team of rorBB (an as-yet-unannounced Rails BB) saw that the application was almost completely RESTful – except in it’s login/logout actions on the users controller. It was Ben who pointed out that it was the single thing destroying our otherwise pretty URLs and we decided to do something about it.
I admit that I’ve been slow to get on the REST bandwagon. Why is that you proverbially ask? To tell the truth it’s largely because I CAN’T FIND A DEFINITION ANYWHERE. I went to Wikipedia, oracle of all wisdom, only to be told how it differs from RPC. I have a long, rich history of coding in PHP. That means I dont know shit about real programming and I had to then go and lookup RPC.
So forgive me if I act like this is the biggest, most amazing discovery ever. I’m just really happy that I discovered it before everybody had moved on to the Next Big Thing©.
So, regarding our controllers: the code we had was fairly generic. On the users controller there were the standard `new`, `create`, `update`, `edit`, etc. actions but then we had two others:
UsersController with RESTless actions:
def login
if request.post?
user = User.authenticate(params[:name], params[:password])
unless user.nil?
session[:user_id] = user.id
redirect_to users_url
else
flash[:error] = 'Username or password is incorrect'
end
end
end
def logout
reset_session
redirect_to :action => 'login'
end
In other apps I’ve written this was pretty standard. Sometimes they’d be in a ‘users’ controller, sometimes in an ‘auth’ controller that contained just these two actions.
It wasn’t until I saw a lot of high-quality REST programming that I started to see there was a better way of doing things than just throwing actions in controllers that might fit.
So we talked about the possibility of overhauling our code to allow for a sessions controller that followed the basic verbs of REST. I figured it would take a while but might eventually pay off.
Boy was I surprised to find that even an idiot like me could put it together quickly. Here’s the final code from the sessions controller:
class SessionsController < ApplicationController
def create
user = User.authenticate(params[:name], params[:password])
unless user.nil?
session[:user_id] = user.id
redirect_to users_url
else
flash[:error] = 'Username or password is incorrect'
redirect_to new_session_url
end
end
def destroy
reset_session
flash[:notice] = "You are now logged out"
redirect_to new_session_url
end
end
If you ignore a couple of slight improvements in the second piece of code you might notice that I ONLY RENAMED THINGS. Can you believe it? Our whole app fit back into REST with some very minor changes.
So, what’s the benefit of the sessions controller? Well, besides getting a free API (though you may never need to build a session over API) the tests got simpler, the routes.rb file got WAY, WAY simpler (back to just one line of code for all our controllers), and our team got just a little bit happier.
And who doesn’t like to be happy?
Update: By request, here’s the original and the modified routes.rb file:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
ActionController::Routing::Routes.draw do |map| map.connect '', :controller => 'forums' map.resources *%w[forums topics posts users] map.resources :users, :collection => { :login => :get } map.resources :users, :collection => { :login => :post } map.resources :users, :collection => { :logout => :post } map.connect ':controller/:action/:id' end |
1 2 3 4 5 6 7 8 9 |
ActionController::Routing::Routes.draw do |map| map.home '', :controller => 'forums' map.resources *%w[forums topics posts users sessions] map.connect ':controller/:action/:id' end |
You might notice that even in the first file the routes are using the new map.resources command (normally used for this REST stuff).