When REST doesn't fit

Posted by Jason King 10 months ago

So, those of you that know me know that I’m no REST zealot. I think there are many times when it’s better to use a non-REST controller. Simplicity of URLs, succinctness of controller, etc..

One example that I’ve just come across is a questionnaire which doesn’t really map to REST. For a start I never want to expose this controller via an API, the process flow also doesn’t follow REST at all, sure I want a new questionnaire completed, but that’s it. I don’t want to be able to show, edit/update or delete.

I also want some nice and simple URLs, so I don’t want /questionnaire/new for a new questionnaire, I just want something very simple like /q. And then I want a thank-you page at the end of the questionnaire, with a message about the email confirmation that I’ve sent to the participant. Nothing here sounds RESTy, and I’d encourage you to just depart from REST when this happens - don’t feel locked in.

So, the controller will be familiar, and I’m not really interested in repeating here what I’ve done there. It will be very similar to other RESTful controllers, bar a few bits. What I *am* interested in demonstrating here is what to do in your routes.

Routes, more than controllers, are where a lot of REST magic is rolled up into some very concise helpers. So, when we move away from REST how do we make our routes nice and simple, concise, and DRY?

Well, let me just spill the beans on what I have for my questionnaire:

map.with_options :controller => 'q' do |c|
  c.with_options :conditions => { :method => :get } do |cget|
    cget.q 'q'
    cget.q_thanks 'q/thanks', :action => 'thanks'
  end
  c.with_options :conditions => { :method => :post } do |cpost|
    cpost.connect 'q', :action => 'create'
  end
end

Isn’t that with_options a nice little tool. So what are we doing here?

Well, we want all our routes to be for our new Q controller, so we open up a block with the :controller => 'q' options sending that to the map receiver. Seriously cool.

So then within that enclosing block we have our GET and POST URLs separated. So we have two GET URLs defined (both as named routes), one for the (implicit) index action with the q helper, and one for the thanks action with the q_thanks helper. Then we have a single POST URLs defined for the creation which we have named q_create. So now, what does our form look like?

<% form_for @questionnaire, :url => q_path do |f| %>

Very neat and tidy, we just use our q helper and we know that forms will have POST action, which will mean it will be routed to our create action. Then in our create action in our controller the successful response will be something like this:

flash[:success] = 'Questionnaire was successfully created.'
format.html { redirect_to( q_thanks_path ) }

Which is good enough for me.

Hierarchy: previous, next

Comments

There are 0 comments on this post. Post yours →

Post a comment

Required fields look like this.

Markdown enabled. See the syntax rules for help.