The following is an excerpt from a Short Cut published by one of the Pearson Education imprints.
Short Cuts are short, concise, PDF documents designed specifically for busy technical professionals like you.
We’ve provided this excerpt to help you review the product before you purchase.Please note, the hyperlinks contained within this excerpt have been deactivated.
Tap into learning—NOW!
Visit www.informit.com/shortcuts for a complete list of Short Cuts.
Your Short Cut to Knowledge
shortcut
Rails Refactoring to Resources
Using CRUD and REST in Your Rails Application
Addison-Wesley Professional Ruby Series
shortcut
Trotter Cashion
www.awprofessional.com/ruby
Section 1:What This Short Cut Covers . . . . . . 3
Section 2:What Is REST? . . . . . . . . . . . . . . . . . . . 6
Section 3:Refactorings . . . . . . . . . . . . . . . . . . . 10
Section 4:RESTful Controllers . . . . . . . . . . . . . 31
Section 5:RESTful Routes . . . . . . . . . . . . . . . . . 48
Section 6:RESTful Views . . . . . . . . . . . . . . . . . . 54
Section 7:RESTful Tests . . . . . . . . . . . . . . . . . . . 59
Section 8:RESTful Authentication . . . . . . . . . 61
Section 9:Consuming RESTful APIs . . . . . . . . 63
Resources . . . . . . . . . . . . . . . . . . . . . . 72
About the Author . . . . . . . . . . . . . . 73
SECTION 1
What This Short Cut Covers
This short cut shows you how to incorporate the changes thatRepresentational State Transfer, better known as REST, has brought toRails in your application. It starts off slowly with an introduction to theshort cut as a whole and a look at the benefits of REST. It then buildsmomentum with three refactorings that are essential in any Rails devel-oper’s toolkit. From there, we look at the changes that REST has broughtto Rails, focusing on specific pieces of Rails one at a time. Finally, wefinish with a look at the consumption of RESTful APIs usingActiveResource and the venerable cURL. Learning new programmingparadigms is always tricky, and REST is no exception. I hope this shortcut makes your transition a little easier. If you have any questions, orwould just like to let me know how much the short cut sucks (hopefullynot many feel this way), email me at [email protected].
Representational State Transfer, better known as REST, is an architectureformulated by Roy Fielding during his work on the HTTP specification.In his PhD dissertation [Fielding], he put the REST architecture intowords, explaining both its benefits and shortcomings. To summarize hisdissertation, REST is the use of URLs to represent conceptual resources,
3
Section 1:What This Short Cut Covers
1.1 Target Audience . . . . . . . . . . . . . . . . . . . . . . 5
1.2 Rails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
What This Short Cut Covers
physical representations of which can be retrieved through content type negotiation. So,http://wesellinfo.example/top-ten-books will always return the top-ten books, and content typenegotiation will be used to determine whether that list is returned as HTML, XML, or some otherformat. Though REST involves more than merely content type negotiation and conceptualresources, this short cut will not attempt to thoroughly defend REST. Instead, I will assume that youare either already convinced or intrigued enough to want to use some of the idioms that REST isinjecting into the Rails culture.
Regardless of your feelings about REST, the refactorings that I detail in the beginning of this shortcut can help create a better application. The first two (respond_to and crud) do not require thatyou adopt REST. The third (resource) requires only that you use Rails’s REST concept in onecontroller and is helpful for deciding whether REST feels right to you. These refactorings will helpyou clean your application code using small, easy-to-follow steps that minimize the number offailing tests you experience during the process.
The remaining content in this short cut is divided into sections that correspond to the parts ofRails that the RESTful mind-set touches. Though not all the information requires that you adoptREST, it does support the use of REST in Rails.
4 Rails Refactoring to ResourcesBy Trotter Cashion
© 2007, Pearson Education, Inc. All rights reserved.This publication is protected by copyright. Please see page 2 for more details.
SECTION 1
1.2 Rails
1.1 Target AudienceThis short cut is written for the developer who is already familiar with performing basic tasks inRails. A few sections, marked accordingly, involve in-depth Ruby magic. The novice can skip themore difficult sections without missing any of the core concepts this short cut addresses. However,careful study of the more difficult sections, possibly with help from the Pickaxe Book [Thomas],will greatly increase the novice’s appreciation of Ruby and Rails.
1.2 RailsMost of the information in this short cut is written on the assumption that you are using Rails 1.2.If you cannot upgrade to Rails 1.2 for any reason, the first two refactorings will still be useful toyou. However, some pieces will not work, so be sure to have good tests to let you know if any ofyour changes cause your application to break. Hopefully, you’re using Subversion so that thesemistakes do no irreparable harm.
5 Rails Refactoring to ResourcesBy Trotter Cashion
© 2007, Pearson Education, Inc. All rights reserved.This publication is protected by copyright. Please see page 2 for more details.
Section 4:RESTful Controllers
4.1 A RESTful Controller . . . . . . . . . . . . . . . . . 31
4.2 Automating RESTful Controller Generation . . . . . . . . . . . . . . . 34
4.3 respond_to . . . . . . . . . . . . . . . . . . . . . . . 40
4.4 How Content Type Negotiation Works . . . . . . . . . . . . . . . . . . 46
SECTION 4
RESTful Controllers
4.1 A RESTful ControllerWhen writing RESTful Rails applications, the unifying theme is theCRUD controller architecture, which dictates that controllers have thefollowing methods with corresponding URLs.
class TasksController < ApplicationController
def index; end # /tasks
def new; end # /tasks/new
def create; end # /tasks
def show; end # /tasks/1
def edit; end # /tasks/1;edit
def update; end # /tasks/1
def destroy; end # /tasks/1
end
Of course, the preceding code references multiple actions at one URL,something that cannot be done without RESTful Rails. Multiplemethods on one URL are accomplished with the new map.resourcescommand in routes.rb. This command enables method choice, not
31
SECTION 4
4.1 A RESTful Controller
just on URL, but also on HTTP method type, using the four methods: GET, POST, PUT, and DELETE.3
Therefore, the previous controller’s routes look more like the following.
class TasksController < ApplicationController
def index; end # GET /tasks
def new; end # GET /tasks/new
def create; end # POST /tasks
def show; end # GET /tasks/1
def edit; end # GET /tasks/1;edit
def update; end # PUT /tasks/1
def destroy; end # DELETE /tasks/1
end
Each HTTP method and route combination is unique, which allows routes.rb to choose the appro-priate method to handle the request. It is important to understand why each HTTP method is usedin each circumstance. GET is used to retrieve a resource; POST is used to create a resource; PUT is usedto update a resource; and DELETE is used to remove a resource. When using GET, we should expectno change to the data in our database. All other methods should result in a change to data.
When developing a large RESTful Rails application, all controllers tend to assume the same basicshape.
class TasksController < ApplicationController
def index
@tasks = Task.find(:all)
end
32 Rails Refactoring to ResourcesBy Trotter Cashion
© 2007, Pearson Education, Inc. All rights reserved.This publication is protected by copyright. Please see page 2 for more details.
3 Major browsers do not currently support HTTP methods other than POST or GET. The Rails method of faking PUT and DELETE is explained in moredetail in Section 5.2.
SECTION 4
4.1 A RESTful Controller
def new
@task = Task.new
end
def create
@task = Task.new(params[:task])
if @task.save
redirect_to :action => :show, :id => @task.id
else
render :action => :new
end
end
def show
@task = Task.find(params[:id])
end
def edit
@task = Task.find(params[:id])
end
def update
@task = Task.find(params[:id])
if @task.update_attributes(params[:task])
redirect_to :action => :show, :id => @task.id
else
33 Rails Refactoring to ResourcesBy Trotter Cashion
© 2007, Pearson Education, Inc. All rights reserved.This publication is protected by copyright. Please see page 2 for more details.
SECTION 4
4.2 Automating RESTful Controller Generation
render :action => :new
end
end
def destroy
@task = Task.find(params[:id])
@task.destroy
render :action => :index
end
end
4.2 Automating RESTful Controller GenerationAlthough simple to do, writing RESTful controllers by hand is time consuming. Rather than letprogrammers be wasteful, Rails and Ruby provide ways to automate creating RESTful controllers.Rails provides two resource generators, similar to the scaffolding generator that ships with Rails.Ruby, through inheritance, allows us to inherit from a CrudController class that contains basic RESTfunctionality. Both of these methods require adding the necessary resource to routes.rb by hand.
4.2.1 The Resource GeneratorsRails provides two similar resource generators. The first, scaffold_resource, generates full scaffoldingof model, view, helper, tests, and controller for a resource. As arguments, it takes the model name (insingular form) and an optional list of property names and data types for the model. Thescaffold_resource generator is run using
ruby script/generate scaffold_resource task name:string description:text due_date:date
34 Rails Refactoring to ResourcesBy Trotter Cashion
© 2007, Pearson Education, Inc. All rights reserved.This publication is protected by copyright. Please see page 2 for more details.
SECTION 4
4.2 Automating RESTful Controller Generation
The second generator, resource, provides the same functionality as scaffold_resource, but does not create any views. Its syntax is identical to the scaffold_resource syntax. The resource generator is useful when views written by scaffold_resource will be overwritten immediately with customviews.
Both generators create controller code that is similar to the code shown earlier in theTasksController. In addition, the generators create a model with an appropriate migration tocreate the fields specified in the command line. These two tools are useful for cranking out manyskeleton resources at the beginning of a project.
4.2.2 The CrudControllerThe repetition in RESTful controllers is readily apparent. For this reason, Jake Howerton created acentral CRUD controller from which all his controllers inherit. Through creative use of meta-programming, this controller greatly simplifies your application. A light variation on his originaldesign follows.4
class CrudController < ApplicationController
before_filter :find_object, :only => [:show, :edit, :update, :destroy]
def index
instance_variable_set(“@#{controller_name.pluralize}”, current_model.find(:all))
end
35 Rails Refactoring to ResourcesBy Trotter Cashion
© 2007, Pearson Education, Inc. All rights reserved.This publication is protected by copyright. Please see page 2 for more details.
4 Though messaging is left out of the CrudController, often it is not necessary because the page to which the user returns is their created orupdated object, or a list of objects with their deleted object missing. If messaging is required, you can implement it in your subclassed controller usingafter-filters.
SECTION 4
4.2 Automating RESTful Controller Generation
def new
set_singular(current_model.new)
end
def create
set_singular(current_model.new(params_hash))
if get_singular.save
redirect_to :action => :show, :id => get_singular.id
else
render :action => :new
end
end
def show
end
def edit
end
def update
if get_singular.update_attributes(params_hash)
redirect_to :action => :show, :id => get_singular.id
else
render :action => :edit
end
end
36 Rails Refactoring to ResourcesBy Trotter Cashion
© 2007, Pearson Education, Inc. All rights reserved.This publication is protected by copyright. Please see page 2 for more details.
SECTION 4
4.2 Automating RESTful Controller Generation
def destroy
get_singular.destroy
redirect_to :action => :index
end
private
def find_object
set_singular(current_model.find(params[:id]))
end
def current_model
Object.const_get controller_name.singularize.classify
end
def params_hash
params[controller_name.singularize.to_sym]
end
def set_singular(rvalue)
instance_variable_set(“@#{controller_name.singularize}”, rvalue)
end
def get_singular
instance_variable_get(“@#{controller_name.singularize}”)
end
end
37 Rails Refactoring to ResourcesBy Trotter Cashion
© 2007, Pearson Education, Inc. All rights reserved.This publication is protected by copyright. Please see page 2 for more details.
SECTION 4
4.2 Automating RESTful Controller Generation
Putting this code into lib/crud_controller.rb allows TasksController to become the following.
class TasksController < CrudController
end
Your controllers are now dead simple. In fact, you will find that many of your controllers begin tolook like the preceding code, with only those that need extra functionality redefining the methodsfrom CrudController.
4.2.3 BelongsToCrudControllerAnother version of the CrudController is the BelongsToCrudController. The BelongsToCrudControlleris used when the model belongs to another model, and the belongs_to association is required. Thefollowing code explains this more clearly.
class BelongsToCrudController < CrudController
before_filter :find_parent
def index
instance_variable_set(“@#{controller_name.pluralize}”, get_parents_children)
end
def create
set_singular(current_model.new(params_hash))
if get_singular.save
get_parents_children << get_singular
redirect_to :action => :show, :id => get_singular.id
else
38 Rails Refactoring to ResourcesBy Trotter Cashion
© 2007, Pearson Education, Inc. All rights reserved.This publication is protected by copyright. Please see page 2 for more details.
SECTION 4
4.2 Automating RESTful Controller Generation
render :action => :new
end
end
private
def self.parent_model(klass)
define_method(:parent_model) { klass }
private :parent_model
end
def find_parent
if get_singular
get_singular.send(parent_model.table_name.singularize.to_sym).id
else
params[:”#{parent_model.table_name.singularize)_id”]
end
set_parent(id)
end
def set_parent(rvalue)
instance_variable_set(“@#{parent_model.table_name.singularize}”, rvalue)
end
def get_parent
instance_variable_get(”@#{parent_model.table_name.singularize}”)
end
39 Rails Refactoring to ResourcesBy Trotter Cashion
© 2007, Pearson Education, Inc. All rights reserved.This publication is protected by copyright. Please see page 2 for more details.
SECTION 4
4.3 respond_to
def get_parents_children
get_parent.send(controller_name.pluralize.to_sym)
end
end
With the preceding code in /lib/belongs_to_crud_controller.rb, a TaskController with parentprojects looks like
class TasksController < BelongsToCrudController
parent_model Project
end
Other than the parent_model method called in the child class controller, using BelongsToCrudControlleris similar to using the CrudController.
4.3 respond_toContent type negotiation, which is discussed in more detail in Section 4.4, is a critical componentof RESTful design. Because each URL represents a conceptual resource, not its physical manifesta-tion, content type negotiation is necessary to retrieve the appropriate document. Web browserswant HTML; however, many other users and agents want RSS or other varieties of XML.
To manage content type negotiation, Rails provides the respond_to method. For those interested insource diving, this method is found in actionpack/lib/action_controller/mime_responds.rb. Forthose not interested in digging through code, respond_to is a method called from your controllersthat takes either a block or a list of content types. The block format, which takes a block thatdefines actions to perform for each content type, is used most frequently.
40 Rails Refactoring to ResourcesBy Trotter Cashion
© 2007, Pearson Education, Inc. All rights reserved.This publication is protected by copyright. Please see page 2 for more details.