5.3. A RESTful Application

The file config outes.rb defines how URLs are mapped to controllers and their methods (actions, in Rails speak). For example, in the previous paragraph you mapped the controller Articles with the root of your Rails application.

In traditional Rails applications — if the word "traditional" can be used for such a young framework — URLs have the following format by default :controller/:action/:id, as briefly discussed in Chapter 2. So /articles/show/3 would trigger the show action defined within the ArticlesController and pass a parameter id to it, whose value is 3. Similarly, /articles/edit/3 would trigger the edit action of the same controller on the same object, and /articles/update/3 would do the same for the update action.

Defining URLs in this manner is still possible today with the current version of Rails, but it means thinking in terms of pages that you access and upon which you perform certain actions that send a request and receive a response back. This approach works and it's the traditional way of doing Web development. Even the ASP.NET event-based model, despite its many differences, essentially leads developers to think in terms of pages (WebForms).

5.3.1. What's REST?

Rails has clearly embraced "the REST way" of doing Web development. REST stands for REpresentational State Transfer and it's a set of principles that define the style of software architecture for distributed networks such as the Web. What this means in practice is that you think in terms of resources that are accessible by a Uniform Resource Identifier (URI). Each resource is the source of the information, and it exposes a series of functions to the Web clients, so that they can read and manipulate the resource. The communication between the server and the client happens over the HTTP protocol.

The server determines what to do based on the identifier of the resource and the HTTP method (often referred to as verb) that's being used for the request. Resources in Rails are usually exposed by seven CRUD actions defined in the controller, but you can define additional custom actions when needed.

The URI also provides the representation requested for the resource (essentially its format). The actual information could be stored in the database within a table, but the client ignores all this. For example, the client will send a request for a given URI (for example, http://localhost:3000/articles/1.xml), with a certain HTTP verb (for example, GET) and will obtain the resource associated with that URI in the XML format back in return.

As you'll see in a moment, Rails knows that a GET request with that URI should be handled by the index action of ArticlesController and will respond to the request with XML data.

If you omit the .xml part, the HTML representation will be transferred to the client instead. It's important to understand that even though HTML and XML are the most common formats for most requests, a resource could be programmed to present the information in a number of representations, including, but not limited to, JavaScript Object Notation (JSON), plain text, one of the many image formats, a comma-separated list of values, or iCalendar. The information in the model remains the same, but its representation (which is sent to the client) varies depending on the URL of the request. You'll see later on what formats are available out of the box in Rails and how to define custom ones.

Applications that follow REST principles are commonly known as RESTful. And because Rails is continually becoming a more RESTful framework (a trend that started in 2006), you'll develop your blog the REST way. As a matter of fact, given that you employed the scaffold generator as the base of your application, you've already started doing RESTful development.

You may have noted that I initially, somewhat casually, used the word "resource" to identify the article argument that I passed to the scaffold generator. Now you know that scaffolding effectively builds a basic resource-based application, which exposes the seven actions mentioned previously.

5.3.2. Mapping Routes to Actions

In an earlier section, you saw a few CRUD operations, which are made available by scaffolding. These were possible because the scaffold generator added map.resources :articles in config outes.rb. That single line defines a series of named routes and maps pairs of URLs and HTTP verbs to actions within the ArticlesController.

What's more, that line also instructs Rails to provide you with a series of helper methods, so that you can easily refer to these named routes from the controller and the view layer.

The following table shows you the mapping of a URL and an HTTP method to a controller action for all seven of the CRUD actions of the Articles controller. The number 1 is used as an example of the id parameter.

URLHTTP MethodController Action
/articlesGETindex
/articlesPOSTcreate
/articles/newGETnew
/articles/1GETshow
/articles/1PUTupdate
/articles/1DELETEdestroy
/articles/1/editGETedit

As you can see, four HTTP verbs are employed: GET, POST, PUT, and DELETE. When you send a GET request for the path /articles, the index action of ArticlesController is executed. When you send a POST request for the resource located at /articles, the create action is run instead; and so on for the other five actions.

In earlier versions of Rails, the path mapping to the edit action was /articles/1;edit. Nobody really liked this though and it caused problems when dealing with caching, so a forward slash was employed instead of a semicolon.

If you are familiar with the HTTP methods and current Web browsers, you may spot a problem with this, though. In fact, though it's not hard to create a client for a Web Service that sends PUT or DELETE requests, the major browsers support only two verbs: GET and POST.

If REST is going to be the future for many Web projects, it is likely that the PUT and DELETE methods will see their way into the browser — eventually. In the meantime though, Rails cheats. PUT and DELETE requests in the browser are handled by placing a hidden _method field. When Rails sees that field, it correctly interprets the request as if it was a genuine PUT or DELETE request. And with Rails being Rails, specifying that a link should send the request in a particular HTTP method is a snap, as you'll see later on in this chapter.

There are seven default CRUD actions, but some of the routes also accept an optional format. In fact, a single resource can have many representations. When you factor this in, remembering all the routes may be a bit tricky. That's where the routes task comes in handy. If you run rake routes in your project, you will obtain the following output:

C:projectslog> rake routes
(in C:/projects/blog)
                  root        /
{:controller=>"articles", :action=>"index"}
              articles GET    /articles
{:controller=>"articles", :action=>"index"}
    formatted_articles GET    /articles.:format
{:controller=>"articles", :action=>"index"}
                       POST   /articles
{:controller=>"articles", :action=>"create"}
                       POST   /articles.:format
{:controller=>"articles", :action=>"create"}
           new_article GET    /articles/new
{:controller=>"articles", :action=>"new"}
 formatted_new_article GET    /articles/new.:format
{:controller=>"articles", :action=>"new"}
          edit_article GET    /articles/:id/edit
{:controller=>"articles", :action=>"edit"}
formatted_edit_article GET    /articles/:id/edit.:format
{:controller=>"articles", :action=>"edit"}
               article GET    /articles/:id
{:controller=>"articles", :action=>"show"}
     formatted_article GET    /articles/:id.:format
{:controller=>"articles", :action=>"show"}
                       PUT    /articles/:id
{:controller=>"articles", :action=>"update"}
                       PUT    /articles/:id.:format
{:controller=>"articles", :action=>"update"}
                       DELETE /articles/:id
{:controller=>"articles", :action=>"destroy"}
                       DELETE /articles/:id.:format
{:controller=>"articles", :action=>"destroy"}
                              /:controller/:action/:id
                              /:controller/:action/:id.:format

The output will appear with the same line wraps, unless you set the width of your command prompt to be very large.

Despite the output being poorly formatted (due to the limited width of the page), you can see how each line maps an HTTP verb and a URL to a controller and an action. These are all RESTful routes, except for three routes for which no HTTP verb is specified:

root / {:controller=>"articles", :action=>"index"}
/:controller/:action/:id
/:controller/:action/:id.:format

All the routes but these three were defined by that map.resources :articles line in config outes.rb, which was placed there by the scaffold generator. So where do these three non-RESTful routes come from? The first of the three is the root named route. You defined it earlier in the chapter in order to map / with the index action of the Articles controller. To find out more about the other two, take a look at the routes file once again (stripped of most of its comments for brevity):

ActionController::Routing::Routes.draw do |map|
  map.root :controller => "articles"
  map.resources :articles

  # Install the default routes as the lowest priority.
  map.connect ':controller/:action/:id'
  map.connect ':controller/:action/:id.:format'
end

Notice how the last two highlighted lines define the two routes you were looking for. These are default routes and are defined by three parameters: a :controller, an :action, and an :id. In the case of the second route, there is also a :format parameter. The existence of those two map.connect calls in the config outes.rb file implies that traditional non-REST mapping is still allowed.

So if you were to point your browser to http://localhost:3000/articles/show/1 (or 1.xml) you'd still get the desired outcome. Even the controller is a parameter in these default routes; therefore if you reach http://localhost:3000/account/list, Rails tries to map that URL with the list action of the Account controller. In this case, there is no Account controller, so an ActionController::RoutingError exception is raised.

The sample blog is going to be a purely RESTful one, so you can go ahead and comment those two lines out (never expose more than you need to):

ActionController::Routing::Routes.draw do |map|
  map.root :controller => "articles"
  map.resources :articles

  # Install the default routes as the lowest priority.
  #map.connect ':controller/:action/:id'
  #map.connect ':controller/:action/:id.:format'
end

5.3.3. Named route Helpers

The following is one of the routes printed out by the rake routes command:

articles GET /articles    {:controller=>"articles", :action=>"index"}

articles represents the named route, which is also the stem for a series of handy helpers. Appending _url or _path to a named route will give you a method that returns that route's address. For example, given that you have a named route articles, you can use articles_url and articles_path, which will return http://localhost:3000/articles and /articles, respectively. The difference between the two pretty much means that helper methods suffixed with _url are often used within the controller, and those postfixed by _path are often employed in the view layer.

From the output of the rake routes command, you'll notice that :id and :format are often parameters. :id identifies the resource needed by its id (for example, 1), and :format is an optional parameter that specifies the representation required (for example, .xml). For instance, when you send a GET request for the URL http://localhost:3000/articles/1.xml, the params hash is:

{ "format"=>"xml", "action"=>"show", "id"=>"1", "controller"=>"articles" }

Named routes that allow you to specify a format begin with the word formatted. In our case, these are: formatted_articles, formatted_new_article, formatted_edit_article, and formatted_article. The presence of that initial token affects the name of the dynamically generated helpers.

For example, you can have articles_url and articles_path as you saw before, as well as their "formatted" counterparts, such as formatted_articles_url and formatted_articles_path. These formatted helpers accept the format requested as an argument. For example, if in a view you were to use formatted_articles_path(:xml), the value returned by the helper would be the string literal "/articles.xml," which would invoke the index method of the Articles controller, and respond to the request with an XML representation of the resources (rather than the default HTML that's returned if no other format is explicitly requested).

Likewise, in the view layer you could use formatted_article_path(@article, :xml), where @article is an instance variable that is defined in the show action of ArticlesController, and it refers to an instance of the Article model. The helper is smart enough to extract the id attribute from the @article object and assign it to the id parameter. It also assigns :xml as the value for the key :format in the parameters hash. The end result would be the path: /articles/1.xml.

After this overview of the config outes.rb file, which was generated through scaffolding, you can move on to briefly analyzing the code in the model, controller, and view, which allows you to perform CRUD operations of the articles table.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.136.142