Hoopla!

now with extra whiz-bang!

Hoopla!

RESTless design - do I need a session controller/model?

August 19, 2006 · 11 comments

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).

→ 11 comments Tags:

Rails: Custom 404 Pages

August 05, 2006 · 4 comments

I launched JCFootballProspects.com a while ago and things have been going smoothly but the site is stuck with the nasty black-on-white error messages when somebody types in a wrong address.

I’m now at the point where I want to give out some fancy 404 pages and I’ve been looking around at what other folks have done. I’m amazed to find that all the examples I can find have a 404.html page in the `#{RAILS_ROOT}/public` directory. I’m sure it’s effective to just have a plain-html file that gets served up for errors (and it’s certainly a lot better than the default ugly stuff) but I’m convinced there are advantages to having application-provided 404 pages as well.

There are three different kinds of errors I trap and respond to: * A user gives incorrect or insufficient parameters to a page view * A user attempts to access an action that doesn’t exist * A user attempts to access a controller that doesn’t exist

The first is easily handled – even the AWDWR book shows in an early example that you can output a flash message if something goes wrong with a request. The second is only slightly more complicated but, luckily, Ruby has a great way to respond to missing actions on controllers using method_missing. The solution to the third is provided by Rails’ routing capabilities.

How to hook up an app-driven 404 page: ==== The first thing I did was added a view to my default controller (mine is called ‘home’). I created ./app/views/home/404.rhtml and put just some basic stuff in it:
<%= content_tag 'h2', 'Whoops!' %>

<%= content_tag 'h3', 'Page not found' %>

[insert message here]

The next step was to add method_missing to my application controller:
1
2
3
4
5
6
7
8
9
10
11
class ApplicationController < ActionController::Base

  ...

  def method_missing(methodname, *args)
   @methodname = methodname
   @args = args
   render 'home/404', :status => 404
  end

end
And one super low priority route (put it at the very bottom) finishes off the job:
  map.error ':controllername',  :controller => 'home',
                              :action => '404'

→ 4 comments Tags: