2.2. Understanding MVC

The MVC architectural pattern is one of the most well-known and adopted patterns in the history of software engineering. When Trygve Reenskaug, a Norwegian computer scientist, first described it back in 1979, it aimed at favoring the development and maintainability of Smalltalk GUI applications by a clear division of business logic and presentation in three distinct types of components: the model, the view, and the controller.

You can find the original and revised documents describing the pattern online at http://heim.ifi.uio.no/~trygver/themes/mvc/mvc-index.html.

The idea was general enough to be applied in any context that involved some form of user interaction. Thirty years later, the same architecture is in fact widely adopted for Web development. The monolithic approach taken by many in the early days of the Web was a regression over an older but well-proven pattern, especially if you consider that MVC is a particularly fitting abstraction of the underlying architecture provided by the Web. Although it's not the only valid option available, it is indisputably one of the most successful and, above all, the one chosen by the Rails framework.

Entire books could be written on the subject and some may like to indulge in sterile discussions about the purity and fidelity of the various MVC implementations when compared to the original idea. But this won't serve any concrete purpose and will not help you create better Rails applications. This section, therefore, concentrates on the MVC pattern as implemented by Rails.

2.2.1. Overview of the Architectural Pattern

As mentioned in the previous chapter, the model represents the data and the rules that guarantee the validity of the persisted data. The view handles the generation of the user interface, and the controller coordinates the application. It communicates with the model to retrieve and store data, while prompting the users with the right view based on their interaction with the Web application.

The real strength of the paradigm resides in the harmony of these three components, which is entirely handled by Rails for you, as long as you follow its sensible conventions. This facilitates development, testing, and maintenance, because you are able to focus on one component at a time. It also helps to separate the roles and responsibilities among the MVC components, clarifying what types of code belong where, as well as enabling developers to focus on the specific purpose of the code.

If you're not already familiar with the MVC architecture, the previous description may appear to be rather abstract, so let's move on to an example of the request/response life cycle as seen through the eyes of MVC. Figure 2-1 provides a high-level illustration of this process.

Figure 2.1. Figure 2-1

  1. The user issues a request through his browser at the URL: http://example.com/products/show/42

    Beautiful URLs

    As you learn more about Rails, you'll realize how beautiful URLs are characteristic of Rails applications. You won't see the following:

    http://example.com/main.asp?type=product&action=show&id=42

    or similarly ugly query strings very often in the Rails world.

    The issue of clean and beautiful URLs is not limited to aesthetics and Search Engine Optimization (SEO), but becomes a vital component of the application particularly when defining RESTful ones (which is a highly recommended style for CRUD-based applications) as explained in Chapters 5, 6 and 10.


  2. The Web Server forwards the request for a dynamic page to Rails' dispatcher.

  3. Rails' dispatcher invokes the routing component on the URL to determine what controller and action (Rails' speak for a public controller method) should handle the request. Routing uses a series of sensible conventions (configurable in config/routes.rb within a Rails application) to determine this information. By default, the /controller/action/id pattern applies; therefore, unless otherwise specified, the router determines that the request should be handled by the show action of the Products controller, using id 42 as a parameter.

  4. The dispatcher loads the proper Ruby file for the controller at hand (app/controllers/products_controller.rb) and creates an instance of the controller class (ProductsController).

  5. The Products controller's show action is called. This action typically interacts with the model layer by calling one or more methods of the Product model (note the singular form of the identifier).

  6. The model translates the Ruby code into SQL and executes the query in order to retrieve the record with id 42 from the proper table (products) in the database. The information for the product is then returned to the controller, which stores it in an instance variable that will be accessible from the view.

  7. The controller invokes the rendering of the proper view, which typically assembles an HTML template with the product information passed on by the controller.

  8. The Web server uses this data to formulate a proper HTTP response to send to the user's browser.

  9. Lastly, the user is presented with the page requested.

Of course many details are omitted from this simplified description, but this should suffice to provide you with an initial illustration of how model, view, and controller interact.

You may have noticed that despite the acronym MVC, the model, view, and controller don't work in that sequential order.

As you can see, each of these three components has a specific set of responsibilities and Rails handles their coordination and cooperation automatically for you. You might also notice that Rails makes several assumptions along the way. For instance, as long as you have created a Product model, Rails knows by convention how to find the actual table in the database (the table products, unless otherwise specified). The Model-View-Controller set of layers combined with good conventions are crucial to Rails' incredible productivity.

Rails doesn't simply promote the MVC pattern, it actually enforces it from the moment you generate an application, creating empty folders for you that are ready to contain models, controllers, and each of the several templates that are part of the view layer.

Contrary to what the fashion industry would like you to believe, you should have "fat models." Conversely, you should aim to code "skinny controllers" and "dumb views." Models should do the heavy lifting as much as possible. Controllers should be thin and contain the bare essentials required to coordinate the action. And finally, views shouldn't house any but the most trivial business logic required to present the contents (for example, looping through a collection of objects that need to be presented).

2.2.2. Defining Models

In a typical Rails application, each model is a Ruby class that represents a table in the database. Rails provides a useful abstraction that enables you to think in terms of concepts and business objects, rather than tables and rows. To accomplish this goal, Rails employs ActiveRecord, an easy-to-use Object-Relational Mapper (ORM), which was briefly introduced in the previous chapter.

ActiveRecord is a powerful abstraction that greatly simplifies the development process. As an ORM, ActiveRecord maps a model to a table, the columns of the table to the model attributes, and the records within a table to instances of the corresponding model.

Martin Fowler described the Active Record ORM pattern as follows: "An object that wraps a row in a database table or view, encapsulates the database access, and adds domain logic on that data."

One characteristic of ActiveRecord is that it works by making assumptions. Unless you need to overwrite these assumptions, ActiveRecord won't require any configuration. This is in stark contrast with some other ORMs from the .NET and Java worlds, where the configuration is often performed within XML files, which is quite a tedious and time-consuming task.

When you create a Product model, ActiveRecord will automatically assume that it represents the table products in the database. You'll only have to specify otherwise if Product needs map to a different table, and even then this is done in Ruby within the model class with a single line of code: set_table_name "myprodtable. "

ActiveRecord really simplifies working with databases. It provides a series of useful methods for querying tables, without having to specify any SQL code (in most cases), making basic CRUD (Create, Read, Update, and Delete) operations a breeze. ActiveRecord also handles the relationships between models and even validates the data, ensuring the correctness of the persisted data and disallowing the storage of information that doesn't respect the business rules that you've defined.

With a single line it's possible to retrieve the product with id 42 (to re-use the example mentioned previously) by running:

Product.find(42)

Behind the scenes, the find method searches for the product by issuing the query SELECT * FROM "products" WHERE ("products"."id" = 42). If the product exists, an instance of the Product class is returned. This contains the information you are looking for. Often Rails developers are particularly fond of the notion of using Ruby, a single language, for all or at least most of their application. Therefore, minimizing the amount of SQL that needs to be written is seen as a good thing.

Similarly, in this hypothetical scenario, it's possible to obtain a list of all products that are no longer in stock, by running:

Product.find_all_by_quantity(0)

ActiveRecord is very flexible when it comes to providing you with finder methods to query the database. Likewise, assuming that the products table has name, quantity, and price columns, you can create a new record just as easily by running:

Product.create(:name => "iPhone", :quantity => 2000, :price => "199.99")

For the moment, don't worry about Ruby's syntax for hashes; it is thoroughly explained in Chapter 3.

And of course, ActiveRecord has analogous methods for updating and deleting records, and even for specifying entirely customized SQL statements.

These methods are database agnostic, given that ActiveRecord uses the adapter for the database in use, in order to translate and execute the code into the specific SQL syntax requested. Aside from simplifying the development process, this implies that it's possible to switch from one database to another without changing any of the application's code.

In practice, there are more considerations to be made, due to the different levels of support for ActiveRecord's features provided by each adapter. But overall, a good code base can be switched from one adapter to another with minimal hassle.

Working with ActiveRecord models lets you focus on the domain logic without worrying about how the information is displayed. In fact, a model is entirely independent from the view and controller layers. The process of developing a model in Rails requires that you determine its name, its attributes/fields, and its associations with other model objects. It also requires that you define validations to ensure the validity of your data, and the definition of custom methods that encapsulate your domain logic and add the behavior you need to the model.

The model generator, scaffold generator, and migrations are all useful tools provided by Rails to facilitate this process by generating a skeleton and letting you fill the blanks to phase out the tedious work. You should take care to pay a lot of attention when developing your models, given that they'll end up being the heart of your applications.

What's Scaffolding?

It is worth spending a few moments to introduce this tool called scaffold generator, which aids you in creating Rails applications.

The scaffold generator is a tool that allows the automatic generation of a basic Web application for performing CRUD operations (as seen in Chapter 1). As a developer all you have to do is provide the specification, by indicating the name of the model (which in turn defines the name of the actual table in the database), its fields, and their data types.

Scaffolding takes care of the rest by generating the proper migration files (which define the table definition in Ruby code), the model, controller, view templates, routes, skeletons for the tests, and so on. The end result is the creation of a basic Web interface that can be used to insert, show, modify, and delete records from the database. All this without writing a single line of code.

Scaffolding is useful for rapid prototyping and for when you want to have a solid base upon which to customize and build more powerful features. It usually wows beginners because it gets you up and running in no time and the generated code is easy to understand and modify.

One of the easiest ways to understand how Rails applications work is in fact to analyze how the code generated by scaffolding works. When you create an actual application in Chapter 5, you'll take this approach and start by generating the basic components of the application by scaffolding.


Chapters 5, 6, and 7 provide you with more insight into model development and ActiveRecord.

2.2.3. Designing Views

The view represents the user interface. Of the three roles, the view is the one that requires the least amount of programming, and is in fact often delegated to the Web designer. It is important, though, not to underestimate the significance of this component in Rails applications.

The interface defines your software in the eyes of the user, given that it's the only layer the user will interact with directly. It is therefore your specification and a lot of attention should be paid to ensuring that the user is able to retrieve and store the information through a well-designed, intuitive, and logical UI.

A classic book on the subject of usability and Web design UI is Steve Krug's, Don't Make Me Think! A Common Sense Approach to Web Usability (Que 2000). This title is highly recommended for programmers and designers alike.

The view layer renders the information to the user by assembling templates with the actual dynamic content provided by the action within the controller, which in turn obtains it (in most cases) directly from the model.

Rails offers three types of templates:

  • ERb: Templates that embed Ruby code similarly to how it's done in ASP classic, PHP, or JSP. ERb templates aren't just used to generate XHTML, but can be employed so as to render emails, CSV files, and so on.

  • XML Builder: Templates that are used to generate XML documents from Ruby code. They are often employed when creating Atom and RSS feeds.

  • RJS: Templates employed to generate JavaScript from Ruby code. These, coupled with Rails' inclusion of the Prototype framework and the script.aculo.us library, enable you to create Ajax-powered user interfaces.

NOTE

The view is not the spot to place application logic, so the presence of Ruby code should be kept to a minimum and strictly limited to what's required in order to properly present the data.

ERb templates are the ones that you'll use most often, but the other two will be illustrated as well, particularly in Chapter 9.

The following code is an example of an ERb template (produced by the scaffold generator). The Ruby code is contained within the <% and %> tags:

<h1>Listing products</h1>

<table>
<tr>
<th>Name</th>
<th>Price</th>
<th>Quantity</th>
<th>Info</th>
</tr>

<% for product in @products %>
<tr>
<td><%=h product.name %></td>
<td><%=h product.price %></td>
<td><%=h product.quantity %></td>
<td><%=h product.info %></td>
<td><%= link_to 'Show', product %></td>
<td><%= link_to 'Edit', edit_product_path(product) %></td>
<td><%= link_to 'Destroy', product, :confirm => 'Are you sure?', :method => :delete
%></td>
</tr>
<% end %>
</table>

<br />

<%= link_to 'New product', new_product_path %>

Chapter 5 analyzes the code generated by scaffold in detail, so don't worry if the code doesn't make much sense to you at this stage. I only provided it as an example to help you picture what embedding Ruby code into XHTML can look like.

Ruby Template Engines

ERb is not the only Ruby template engine. There are in fact more than a dozen available choices that you can adopt in your Rails applications.

ERb is the default, mostly because it gets the job done and ships with Ruby, so no external dependencies are required. Two common alternatives are Erubis and Haml.

Erubis (http://www.kuwata-lab.com/erubis/) is an ERb-like engine whose main selling point is its rendering speed. According to a few benchmarks available on the official site, Erubis can be several times faster than ERb, a desirable trait for Web applications.

Haml (http://haml.hamptoncatlin.com/), on the other hand, is often adopted for its compact and beautiful style, which makes it very different (in a good way) from ERb. It's not everyone's cup of tea, but many developers and designers have expressed enthusiasm toward it.


2.2.4. Managing Controllers

Controllers coordinate the application. The importance of the controller in a Rails application can't be overstated, given that the interaction between the user and the view and model layers is controlled and coordinated by the controller.

A controller receives requests from the client, determines which action needs to handle the request, interacts with models to retrieve and store data in the database, and then invokes the view layer so as to render the results back to the user.

The areas of responsibility for the controller are far reaching and not limited to this; in fact, they include processing parameters, redirecting, handling errors, providing helpers for the view, managingcaching for drastically improving performance, sessions for maintaining the user status as they interact with the application, and many other small errands that are further explained, predominantly, in Chapters 5 and 8.

A non-trivial Rails application has several controllers, each dedicated to a given area of functionality. For instance, an online store may have controllers for handling user accounts, displaying products, managing the inventory, and so on.

A controller Products for performing CRUD operations could have the following structure:

class ProductsController<ApplicationController
def index
    # code for showing a list of products
end


def show
    # code for showing a given product
end


def new
    # code for the empty form for a new product
end


def edit
    # code for the editing form for a given product
end


def create
    # code for the creation of a new product
end


def update
    # code for updating a given product

End


def destroy
    # code for deleting a given product
end
end

Each of the public methods defined in the controller represents an action. When the routing component determines that the given request corresponds to a certain action, the Ruby code for that action is executed. From the controller, instance variables used by the view layer are set and the view itself is invoked, either explicitly or implicitly (in that case by a convention based on the action name).

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

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