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.
No comments yet.
You must be logged in to add your own comment.