- •Contents
- •Preface to the Second Edition
- •Introduction
- •Rails Is Agile
- •Finding Your Way Around
- •Acknowledgments
- •Getting Started
- •The Architecture of Rails Applications
- •Models, Views, and Controllers
- •Active Record: Rails Model Support
- •Action Pack: The View and Controller
- •Installing Rails
- •Your Shopping List
- •Installing on Windows
- •Installing on Mac OS X
- •Installing on Linux
- •Development Environments
- •Rails and Databases
- •Rails and ISPs
- •Creating a New Application
- •Hello, Rails!
- •Linking Pages Together
- •What We Just Did
- •Building an Application
- •The Depot Application
- •Incremental Development
- •What Depot Does
- •Task A: Product Maintenance
- •Iteration A1: Get Something Running
- •Iteration A2: Add a Missing Column
- •Iteration A3: Validate!
- •Iteration A4: Prettier Listings
- •Task B: Catalog Display
- •Iteration B1: Create the Catalog Listing
- •Iteration B4: Linking to the Cart
- •Task C: Cart Creation
- •Sessions
- •Iteration C1: Creating a Cart
- •Iteration C2: A Smarter Cart
- •Iteration C3: Handling Errors
- •Iteration C4: Finishing the Cart
- •Task D: Add a Dash of AJAX
- •Iteration D1: Moving the Cart
- •Iteration D3: Highlighting Changes
- •Iteration D4: Hide an Empty Cart
- •Iteration D5: Degrading If Javascript Is Disabled
- •What We Just Did
- •Task E: Check Out!
- •Iteration E1: Capturing an Order
- •Task F: Administration
- •Iteration F1: Adding Users
- •Iteration F2: Logging In
- •Iteration F3: Limiting Access
- •Iteration F4: A Sidebar, More Administration
- •Task G: One Last Wafer-Thin Change
- •Generating the XML Feed
- •Finishing Up
- •Task T: Testing
- •Tests Baked Right In
- •Unit Testing of Models
- •Functional Testing of Controllers
- •Integration Testing of Applications
- •Performance Testing
- •Using Mock Objects
- •The Rails Framework
- •Rails in Depth
- •Directory Structure
- •Naming Conventions
- •Logging in Rails
- •Debugging Hints
- •Active Support
- •Generally Available Extensions
- •Enumerations and Arrays
- •String Extensions
- •Extensions to Numbers
- •Time and Date Extensions
- •An Extension to Ruby Symbols
- •with_options
- •Unicode Support
- •Migrations
- •Creating and Running Migrations
- •Anatomy of a Migration
- •Managing Tables
- •Data Migrations
- •Advanced Migrations
- •When Migrations Go Bad
- •Schema Manipulation Outside Migrations
- •Managing Migrations
- •Tables and Classes
- •Columns and Attributes
- •Primary Keys and IDs
- •Connecting to the Database
- •Aggregation and Structured Data
- •Miscellany
- •Creating Foreign Keys
- •Specifying Relationships in Models
- •belongs_to and has_xxx Declarations
- •Joining to Multiple Tables
- •Acts As
- •When Things Get Saved
- •Preloading Child Rows
- •Counters
- •Validation
- •Callbacks
- •Advanced Attributes
- •Transactions
- •Action Controller: Routing and URLs
- •The Basics
- •Routing Requests
- •Action Controller and Rails
- •Action Methods
- •Cookies and Sessions
- •Caching, Part One
- •The Problem with GET Requests
- •Action View
- •Templates
- •Using Helpers
- •How Forms Work
- •Forms That Wrap Model Objects
- •Custom Form Builders
- •Working with Nonmodel Fields
- •Uploading Files to Rails Applications
- •Layouts and Components
- •Caching, Part Two
- •Adding New Templating Systems
- •Prototype
- •Script.aculo.us
- •RJS Templates
- •Conclusion
- •Action Mailer
- •Web Services on Rails
- •Dispatching Modes
- •Using Alternate Dispatching
- •Method Invocation Interception
- •Testing Web Services
- •Protocol Clients
- •Secure and Deploy Your Application
- •Securing Your Rails Application
- •SQL Injection
- •Creating Records Directly from Form Parameters
- •Avoid Session Fixation Attacks
- •File Uploads
- •Use SSL to Transmit Sensitive Information
- •Knowing That It Works
- •Deployment and Production
- •Starting Early
- •How a Production Server Works
- •Repeatable Deployments with Capistrano
- •Setting Up a Deployment Environment
- •Checking Up on a Deployed Application
- •Production Application Chores
- •Moving On to Launch and Beyond
- •Appendices
- •Introduction to Ruby
- •Classes
- •Source Code
- •Resources
- •Index
- •Symbols
Appendix A
Introduction to Ruby
Ruby is a fairly simple language. Even so, it isn’t really possible to do it justice in a short appendix such as this. Instead, we hope to explain enough Ruby that the examples in the book make sense. This chapter draws heavily from material in Chapter 2 of Programming Ruby [TFH05].1
A.1 Ruby Is an Object-Oriented Language
Everything you manipulate in Ruby is an object, and the results of those manipulations are themselves objects.
When you write object-oriented code, you’re normally looking to model concepts from the real world. Typically during this modeling process you’ll discover categories of things that need to be represented. In an online store, the concept of a line item could be such a category. In Ruby, you’d define a class to represent each of these categories. A class is a combination of state (for example, the quantity and the product id) and methods that use that state (perhaps a method to calculate the line item’s total cost). We’ll show how to create classes on page 634.
Once you’ve defined these classes, you’ll typically want to create instances of each of them. For example, in a store, you have separate LineItem instances for when Fred orders a book and when Wilma orders a PDF. The word object is used interchangeably with class instance (and since we’re lazy typists, we’ll use the word object).
Objects are created by calling a constructor, a special method associated with a class. The standard constructor is called new. So, given a class called LineItem, you could create line item objects as follows.
1. At the risk of being grossly self-serving, we’d like to suggest that the best way to learn Ruby, and the best reference for Ruby’s classes, modules, and libraries, is Programming Ruby [TFH05] (also known as the PickAxe book). Welcome to the Ruby community.
RUBY NAMES 631
line_item_one = LineItem.new
line_item_one.quantity |
= 1 |
|
line_item_one.sku |
= "AUTO_B_00" |
|
line_item_two = LineItem.new |
||
line_item_two.quantity |
= |
2 |
line_item_two.sku |
= |
"RUBY_P_00" |
These instances are both derived from the same class, but they have unique characteristics. In particular, each has its own state, held in instance variables. Each of our line items, for example, will probably have an instance variable that holds the quantity.
Within each class, you can define instance methods. Each method is a chunk of functionality that may be called from within the class and (depending on accessibility constraints) from outside the class. These instance methods in turn have access to the object’s instance variables and hence to the object’s state.
Methods are invoked by sending a message to an object. The message contains the method’s name, along with any parameters the method may need.2 When an object receives a message, it looks into its own class for a corresponding method.
This business of methods and messages may sound complicated, but in practice it is very natural. Let’s look at some method calls.
"dave".length line_item_one.quantity -1942.abs
cart.add_line_item(next_purchase)
Here, the thing before the period is called the receiver, and the name after the period is the method to be invoked. The first example asks a string for its length (4). The second asks a line item object to return its quantity. The third line has a number calculate its absolute value. The final line shows us adding a line item to a shopping cart.
A.2 Ruby Names
Local variables, method parameters, and method names should all start with a lowercase letter or with an underscore: order, line_item, and xr2000 are all valid. Instance variables (which we talk about on page 635) begin with an “at” sign (@), such as @quantity and @product_id. The Ruby convention is to use underscores to separate words in a multiword method or variable name (so line_item is preferable to lineItem).
2. This idea of expressing method calls in the form of messages comes from Smalltalk.
Report erratum
METHODS 632
Class names, module names, and constants must start with an uppercase letter. By convention they use capitalization, rather than underscores, to distinguish the start of words within the name. Class names look like Object,
PurchaseOrder, and LineItem.
Rails makes extensive use of symbols. A symbol looks like a variable name, but it’s prefixed with a colon. Examples of symbols include :action, :line_items, and :id. You can think of symbols as string literals that are magically made into constants. Alternatively, you can consider the colon to mean “thing named” so :id is “the thing named id.”
Rails uses symbols to identify things. In particular, it uses them as keys when naming method parameters and looking things up in hashes. For example:
redirect_to :action => "edit", :id => params[:id]
A.3 Methods
Let’s write a method that returns a cheery, personalized greeting. We’ll invoke that method a couple of times.
def say_goodnight(name)
result = "Good night, " + name return result
end
# Time for bed...
puts say_goodnight("Mary-Ellen" ) puts say_goodnight("John-Boy" )
You don’t need a semicolon at the end of a statement as long as you put each statement on a separate line. Ruby comments start with a # character and run to the end of the line. Indentation is not significant (but two-character indentation is the de facto Ruby standard).
Methods are defined with the keyword def, followed by the method name (in this case, say_goodnight) and the method’s parameters between parentheses. Ruby doesn’t use braces to delimit the bodies of compound statements and definitions (such as methods and classes). Instead, you simply finish the body with the keyword end. The first line of the method’s body concatenates the literal string "Good night, " and the parameter name, and it assigns the result to the local variable result. The next line returns that result to the caller. Note that we didn’t have to declare the variable result; it sprang into existence when we assigned to it.
Having defined the method, we call it twice. In both cases, we pass the result to the method puts, which outputs to the console its argument followed by a newline (moving on to the next line of output). If we’d stored this program in the file hello.rb, we could run it as follows.
Report erratum
METHODS 633
work> ruby hello.rb
Good night, Mary-Ellen Good night, John-Boy
The line puts say_goodnight("John-Boy") contains two method calls, one to the method say_goodnight and the other to the method puts. Why does one method call have its arguments in parentheses while the other doesn’t? In this case it’s purely a matter of taste. The following lines are equivalent.
puts say_goodnight("John-Boy" ) puts(say_goodnight("John-Boy" ))
In Rails applications, you’ll find that most method calls involved in larger expressions will have parentheses, while those that look more like commands or declarations tend not to have them.
This example also shows some Ruby string objects. One way to create a string object is to use string literals: sequences of characters between single or double quotation marks. The difference between the two forms is the amount of processing Ruby does on the string while constructing the literal. In the singlequoted case, Ruby does very little. With a few exceptions, what you type into the single-quoted string literal becomes the string’s value.
In the double-quoted case, Ruby does more work. First, it looks for substitu- tions—sequences that start with a backslash character—and replaces them with some binary value. The most common of these is \n, which is replaced with a newline character. When you write a string containing a newline to the console, the \n forces a line break.
Second, Ruby performs expression interpolation in double-quoted strings. In the string, the sequence #{expression} is replaced by the value of expression. We could use this to rewrite our previous method.
def say_goodnight(name)
result = "Good night, #{name}" return result
end
puts say_goodnight('Pa')
When Ruby constructs this string object, it looks at the current value of name and substitutes it into the string. Arbitrarily complex expressions are allowed in the #{...} construct. Here we invoke the capitalize method, defined for all strings, to output our parameter with a leading uppercase letter.
def say_goodnight(name)
result = "Good night, #{name.capitalize}" return result
end
puts say_goodnight('uncle')
Report erratum