SignOut: Part 2
Here we go, continuing the RESTful refactoring of Randy Schmidt’s “SignOut” application. In Part 1 I talked at a high level about how the existing application could be viewed as an interface to several different resources. In this article, I’ll show you how to implement the “employees” resource. I’ll be doing this by just showing you heavily commented code snippets.
Note that, for this article, I am not using the SimplyHelpful plugin. This is mostly so that I can demonstrate how this all would work with current Rails, without all the convenience magic that SimplyHelpful adds in. Once you understand how this all fits together, though, SimplyHelpful is an enormous time saver.
Firstly, we define the routes we need:
1 2 3 4 5 6 7 8 9 |
ActionController::Routing::Routes.draw do |map| # So far, our RESTful implementation has only a single resource defined, # employees. We use map.resources to set up all the routes (named and # otherwise) that we will need. This assumes the existence of a controller # named EmployeesController. map.resources :employees end |
Then, we implement our controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# This replaces the AdminController in the original implementation. It # provides an interface to the employees resource, allowing employees to be # listed, edited, created, and destroyed. # # Note a few things about this implementation: # # * I'm using respond_to in a few of the actions, to demonstrate how trivial # it is to make your application into a web-service. There's really very # little reason _not_ to do it. # # * I'm not bothering with any kind of authorization mechanism, because the # original wasn't either. However, in practice, I'd be using HTTP # authentication in a before_filter to make sure only people authorized # as administrators were accessing this resource. # # * Note the extensive use of named routes; map.resources sets up a bunch for # you, for free, and they work WONDERS in cleaning up both your controllers # and your views. class EmployeesController < ApplicationController # Rather than doing Employee.find(params[:id]) in all the actions that need # to, just use a before filter. This makes it obvious which actions need # an employee ID, and keeps things DRY. before_filter :find_employee, :only => %w(show update destroy) # Return a list of all employees currently in the system. def index @employees = Employee.find(:all) respond_to do |format| # If we don't give a block, the default behavior is used, which (for # HTML) is to render the "employees/index.rhtml" template. format.html # We specify the :root option here, because if the list of employees # is empty, you'd otherwise get tags like <NilClass>. format.xml { render :xml => @employees.to_xml(:root => "employees") } end end # We could technically leave this action out completely, since Rails will # find the new.rhtml template and render it just fine, but it's nice to be # able to tell by looking at the controller what actions are defined. def new end # Creates a new employee. It expects a hash to come in with a single # :employee key, which points to a subhash of the attributes to use to # create the employee. def create @employee = Employee.create(params[:employee]) respond_to do |format| # If we're in HTML mode, redirect back to the master list. format.html { redirect_to(employees_path) } # If we're in XML mode, just return a 201 Created response. format.xml { head :created, :location => employee_path(@employee) } end end # Display the requested employee record. For this app, we just use this # to display the form for modifying the employee. def show respond_to do |format| format.html format.xml { render :xml => @employee.to_xml } end end # Update the specified employee record. Expects the same input format as # the #create action. def update @employee.update_attributes(params[:employee]) respond_to do |format| format.html { redirect_to(employee_path(@employee)) } # "head" is a wildly useful little method. It just returns a blank # HTTP response, which is often what you want when dealing with XML # requests. Here, we just say to return a "200 OK" response with no # body. format.xml { head :ok } end end # Destroy the specified employee record. def destroy @employee.destroy respond_to do |format| format.html { redirect_to(employees_path) } format.xml { head :ok } end end private def find_employee @employee = Employee.find(params[:id]) end end |
And, lastly, we implement our RHTML views:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<!-- employees/index.rhtml Pretty much the same as the original, but this one makes use of the named routes provided by map.resources. Also note that for the "Delete" link, you have to set the :method to :delete, since RESTful actions use the same URL with different HTTP verbs to differentiate the actions. --> <p><%= link_to "Add", new_employee_path, :class => "Add" %></p> <ul> <% @employees.each do |employee| %> <li> <%= employee.last_first %> <%= link_to "Edit", employee_path(employee), :class => "Edit" %> <%= link_to "Delete", employee_path(employee), :method => :delete, :confirm => "Are you sure you want to delete #{employee.name}?", :class => "Delete" %> </li> <% end %> </ul> <!-- employees/new.rhtml We set up the form to POST to the /employees resource, which will create the new record. Note the use of form_for, which yields an instance of FormBuilder. This lets us do things like "f.text_field(:firstname)" and have the text field set up with the correct name ("employee[firstname]"). This makes it dead simple to get the kind of nested hashes that RESTful actions expect (see #create and #update). --> <% form_for :employee, Employee.new, :url => employees_path do |f| %> <%= render :partial => "employees/form", :locals => { :form => f } %> <% end %> <!-- employees/show.rhtml Pretty much the same as new.rhtml, but this time we post to the URI for a specific employee. We also set the HTTP verb to PUT, via the _method parameter. --> <% form_for :employee, @employee, :url => employee_path(@employee) do |f| %> <%= hidden_field :_method, :put %> <%= render :partial => "employees/form", :locals => { :form => f } %> <% end %> <!-- employees/_form.rhtml This expects a local variable 'form' to exist, which points to a FormBuilder instance. --> <dl> <dt><label for="employee_firstname">First Name:</label></dt> <dd><%= form.text_field :firstname, :size => '30', :maxlength => '100' %></dd> <dt><label for="employee_initials">Initials:</label></dt> <dd><%= form.text_field :initials, :size => '5', :maxlength => '5' %></dd> <!-- etc, etc, etc. --> </dl> |
And that’s the first resource! In the next article, I’ll illustrate the StatusesController.


What about validation in the create/update actions? It looks like those actions will automatically redirect to another path regardless of whether the create/update was successful.
Shalev, I knew someone would ask about that. :) Because the original app didn’t do anything with validations, I didn’t bother putting it in the refactoring, either. However, if your app needs to handle validations, then your update/create actions will naturally be a bit more complex.
Jamis, I think Shalev is right about validation in the create/update actions. A lot of people seek to write good Rails code and see examples on this site as “The” right way. For some Rails newbie, seeking to try that RESTful thing, looking at that code may seem, that it is OK to omit validation. Overall, thanks for You effort. You make our code prettier, and us (programmers) happier =).
Using save! and update_attribute! can be a really nice way of implementing validation without giving up on the simple structure of the controller. The ActiveRecord::RecordInvalid exception can then be rescued in the application controller:
The idea is taken from the beast or mephisto source code.
Excellent article as always.
Where are new_employee_path and employee_path defined. Are these automatically created helpers created by rails?
Jacob: they are created by the map.resources call in the routes file.
Tim, right on. That’s generally how I recommend doing it, too.
Thank you for an excellent article. I always wonder about testing when I read these articles. Are there tests or is the controller code simple enough that it doesn’t really need testing?
Thanks,
Anthony
Thank you for refactoring my little app into a RESTful app! I have been wanting to get my hands dirty with REST but was uncertain of the implementation.
Tim: I’ll have to give that a try. On other projects I always just did
if !@person.update_attributes(params[:person]) redirect_to :back flash[:notice] = “something” else ... end
But that wasn’t very DRY
So… I’ve heard that SimplyHelpful is awesome for RESTful design—what does it do? Other than the code itself, it doesn’t look like there’s any documentation.
How would it simplify this refactoring?
Nate, it’s mostly a bunch of little enhancements to existing helpers. Like
form_for. The form_for calls above could be changed (using SimplyHelpful) into merely:and
Sorry to get a little off topic, but what does it take to get line numbers to work in the syntax library?
I’ve been doing something a little bit different with including the form partial
(_form is identicle)Is there a reason not to rely on the :object to partial-name assignment? I find using the locals hash to be much more cumbersome
Chris, I actually hadn’t ever thought of doing it that way, but I think I like it better than what I demonstrated in the article. Thanks for pointing that out!
Through this series of articles and other things I have read, I understand that using REST simplifies web applications in that everything is reduced to CRUD. However, there are some other things that are related to sites larger than SignOut that I haven’t seen anything on.
The first was how to create a separate Admin interface for apps where there IS authentication/authorization. This was partly answered by Geoffrey Grosenbach in his peepcode screencast about RESTful applications in that you can display a separate layout for the administrator. Are people doing this differently? Is there a better way?
The second is how to manage/display auxiliary content such as “recently added” or “most popular”. Will it be frowned upon if I add ”@recently_added = Person.recently_added” to the index action in one of my resource controllers and then use it and a partial in the view?
Last (I think), when you have an admin interface that is separate from the public interface and you go to /users, you want to see two different things. Does this get handled in the controllers and you just serve up different content based on the visitor’s credentials?
Last (I lied), how many levels deep can you go with nested resources? I have only ever seen people go one level deep, however, I have an app in the works that would really want to go 4 levels deep (groups have meetings that have segments that have comments). Is this how it would be done or is there a better way?
I apologize if these questions don’t directly relate to SignOut or refactoring it to REST, however, I think there needs to be answers on how to have the same front-end to an app that we are used to but converting the back-end to REST.
Almost everything I have read about REST has had the same situation, a one-to-one mapping of models and controllers. What I really like about this set of articles is it shows how resources may not map directly to models (I’ve also seen how it works by picking through the restful_authentication plugin). I’m just trying to wrap my head around how everything is supposed to work with REST so I can use it in a project coming up.
Thanks again for taking the time to put together all of this great stuff at The Rails Way!
I really dug simply_helpful when it first surfaced, I was writing my form partials like this:
<%= form_for @employee do |f| %> <%= f.text_field :firstname, :size => ‘30’, :maxlength => ‘100’ %> <%= f.text_field :initials, :size => ‘5’, :maxlength => ‘5’ %> <% end %>
and just rendering that in my new and edit views directly: <%= render :partial => “form” %>
It really cut down on where i needed to visit to change things. But this method breaks down when your resources are nested. Using the resource_feeder plugin to generate ‘free’ feeds with nested resources blows up as well.
Is this supported yet? Does anyone have a work around for it? Its kind of a shame, I fell back to using the same method chris does on everything, just to keep a convention.
Thank you for an excellent example of RESTful Rails; I was particularly interested in seeing examples of resources which weren’t ActiveRecords.
Jamis, at this end of this article you mentioned that you’d cover the StatusesController next; did this article get lost by the wayside? I’ve always learned a lot from the examples here and would be interested to see this last segment.
I agree with Ryan, more information about the topics he comments on would be nice.
Thanks for the excellent article.
<% form_for :employee, Employee.new, :url => employees_path do |f| %>
putting Employee.new here and not in controller causes problems upon failed validation. my example, using simply_helpful:
<%= submit_tag “Create” %>
<% end %>Jon, take a look at http://railswatcher.com/past/2007/3/30/resource_feeder_and_nested_resource/