Agile Web Development With Rails: Extracted From
Agile Web Development With Rails: Extracted From
This PDF file contains pages extracted from Agile Web Development with Rails, published by the Pragmatic Bookshelf. For more information or to purchase a paperback or PDF copy, please visit http://www.pragprog.com . Note: This extract contains some colored text (particularly in code listing). This is available only in online versions of the books. The printed versions are black and white. Pagination might vary between the online and printer versions; the content is otherwise identical.
Copyright 2010 The Pragmatic Programmers, LLC. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior consent of the publisher.
with Leon Breedt Mike Clark James Duncan Davidson Justin Gehtland Andreas Schwarz
Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in this book, and The Pragmatic Programmers, LLC was aware of a trademark claim, the designations have been printed in initial capital letters or in all capitals. The Pragmatic Starter Kit, The Pragmatic Programmer, Pragmatic Programming, Pragmatic Bookshelf, PragProg and the linking g device are trademarks of The Pragmatic Programmers, LLC. Every precaution was taken in the preparation of this book. However, the publisher assumes no responsibility for errors or omissions, or for damages that may result from the use of information (including program listings) contained herein. Our Pragmatic courses, workshops, and other products can help you and your team create better software and have more fun. For more information, as well as the latest Pragmatic titles, please visit us at http://pragprog.com.
No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior consent of the publisher. Printed in the United States of America. ISBN-13: 978-1-934356-54-8 Printed on acid-free paper. Book version: P3.1February 2013
One of the interesting features of Rails is that it imposes some fairly serious constraints on how you structure your web applications. Surprisingly, these constraints make it easier to create applicationsa lot easier. Lets see why.
3.1
Browser sends request Controller interacts with model Controller invokes view View renders next browser screen
Controller
View
Model
Database
was like constructing a skyscraper with the girders already in placeit was a lot easier to hang the rest of the pieces with a structure already there. During the development of our application, we will be making heavy use of Rails ability to generate scaffolding for our application. Ruby on Rails is an MVC framework, too. Rails enforces a structure for your applicationyou develop models, views, and controllers as separate chunks of functionality, and it knits them all together as your program executes. One of the joys of Rails is that this knitting process is based on the use of intelligent defaults so that you typically dont need to write any external configuration metadata to make it all work. This is an example of the Rails philosophy of favoring convention over configuration. In a Rails application, an incoming request is first sent to a router, which works out where in the application the request should be sent and how the request itself should be parsed. Ultimately, this phase identifies a particular method (called an action in Rails parlance) somewhere in the controller code. The action might look at data in the request, it might interact with the model, and it might cause other actions to be invoked. Eventually the action prepares information for the view, which renders something to the user. Rails handles an incoming request as shown in Figure 5, Rails and MVC, on page 7. In this example, the application has previously displayed a product catalog page, and the user has just clicked the Add to Cart button next to one of the products. This button posts to http://localhost:3000/line_items?product_id=2, where line_items is a resource in our application and 2 is our internal id for the selected product. The routing component receives the incoming request and immediately picks it apart. The request contains a path (/line_items?product_id=2) and a method (this
Routing
http://my.url/line_items?product_id=2 Controller interacts with model Controller invokes view View renders next browser screen
Database
button does a POST operation; other common methods are GET, PUT, and DELETE). In this simple case, Rails takes the first part of the path, line_items, as the name of the controller and the product_id as the id of a product. By convention, POST methods are associated with create() actions. As a result of all this analysis, the router knows it has to invoke the create() method in the controller class LineItemsController (well talk about naming conventions in Section 18.2, Naming Conventions, on page ?). The create() method handles user requests. In this case, it finds the current users shopping cart (which is an object managed by the model). It also asks the model to find the information for product 2. It then tells the shopping cart to add that product to itself. (See how the model is being used to keep track of all the business data? The controller tells it what to do, and the model knows how to do it.) Now that the cart includes the new product, we can show it to the user. The controller invokes the view code, but before it does, it arranges things so that the view has access to the cart object from the model. In Rails, this invocation is often implicit; again, conventions help link a particular view with a given action. Thats all there is to an MVC web application. By following a set of conventions and partitioning your functionality appropriately, youll discover that your code becomes easier to work with and your application becomes easier to extend and maintain. Seems like a good trade. If MVC is simply a question of partitioning your code a particular way, you might be wondering why you need a framework such as Ruby on Rails. The
answer is straightforward: Rails handles all of the low-level housekeeping for youall those messy details that take so long to handle by yourselfand lets you concentrate on your applications core functionality. Lets see how.
3.2
Object-Relational Mapping
ORM libraries map database tables to classes. If a database has a table called orders, our program will have a class named Order. Rows in this table correspond to objects of the classa particular order is represented as an object of class Order. Within that object, attributes are used to get and set the individual columns. Our Order object has methods to get and set the amount, the sales tax, and so on. In addition, the Rails classes that wrap our database tables provide a set of class-level methods that perform table-level operations. For example, we might need to find the order with a particular id. This is implemented as a class method that returns the corresponding Order object. In Ruby code, this might look like this:
order = Order.find(1) puts "Customer #{order.customer_id}, amount=$#{order.amount}"
1.
SQL, referred to by some as Structured Query Language, is the language used to query and update relational databases.
Finally, the objects corresponding to individual rows in a table have methods that operate on that row. Probably the most widely used is save(), the operation that saves the row to the database:
Order.where(name: 'dave').each do |order| order.pay_type = "Purchase order" order.save end
So, an ORM layer maps tables to classes, rows to objects, and columns to attributes of those objects. Class methods are used to perform table-level operations, and instance methods perform operations on the individual rows. In a typical ORM library, you supply configuration data to specify the mappings between entities in the database and entities in the program. Programmers using these ORM tools often find themselves creating and maintaining a boatload of XML configuration files.
Active Record
Active Record is the ORM layer supplied with Rails. It closely follows the standard ORM model: tables map to classes, rows to objects, and columns to object attributes. It differs from most other ORM libraries in the way it is configured. By relying on convention and starting with sensible defaults, Active Record minimizes the amount of configuration that developers perform. To illustrate this, heres a program that uses Active Record to wrap our orders table:
require 'active_record' class Order < ActiveRecord::Base end order = Order.find(1) order.pay_type = "Purchase order" order.save
This code uses the new Order class to fetch the order with an id of 1 and modify the pay_type. (Weve omitted the code that creates a database connection for now.) Active Record relieves us of the hassles of dealing with the underlying database, leaving us free to work on business logic.
10
But Active Record does more than that. As youll see when we develop our shopping cart application, starting in Chapter 5, The Depot Application, on page ?, Active Record integrates seamlessly with the rest of the Rails framework. If a web form sends the application data related to a business object, Active Record can extract it into our model. Active Record supports sophisticated validation of model data, and if the form data fails validations, the Rails views can extract and format errors. Active Record is the solid model foundation of the Rails MVC architecture.
3.3
View Support
In Rails, the view is responsible for creating either all or part of a response to be displayed in a browser, processed by an application or sent as an email. At its simplest, a view is a chunk of HTML code that displays some fixed text. More typically youll want to include dynamic content created by the action method in the controller. In Rails, dynamic content is generated by templates, which come in three flavors. The most common templating scheme, called Embedded Ruby (ERb), embeds snippets of Ruby code within a view document, in many ways similar to the way it is done in other web frameworks, such as PHP or JSP. Although this approach is very flexible, some are concerned that it violates the spirit of MVC. By embedding code in the view, we risk adding logic that should be in the model or the controller. As with everything, while judicious use in moderation is healthy, overuse can become a problem. Maintaining a clean separation of concerns is part of the job of the developer. (We look at HTML templates in Section 24.2, Generating HTML with ERb, on page ?.) You can also use ERb to construct JavaScript fragments on the server that are then executed on the browser. This is great for creating dynamic Ajax
11
interfaces. We talk about these starting in Section 11.2, Iteration F2: Creating an Ajax-Based Cart, on page ?. Rails also provides XML Builder to construct XML documents using Ruby codethe structure of the generated XML will automatically follow the structure of the code. We discuss xml.builder templates starting in Section 24.1, Generating XML with Builder, on page ?.