- •Introduction
- •Rails Is Agile
- •Finding Your Way Around
- •Acknowledgments
- •Getting Started
- •Models, Views, and Controllers
- •Installing Rails
- •Installing on Windows
- •Installing on Mac OS X
- •Installing on Unix/Linux
- •Rails and Databases
- •Keeping Up-to-Date
- •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 A4: Prettier Listings
- •Task B: Catalog Display
- •Iteration B1: Create the Catalog Listing
- •Iteration B2: Add Page Decorations
- •Task C: Cart Creation
- •Sessions
- •More Tables, More Models
- •Iteration C1: Creating a Cart
- •Iteration C3: Finishing the Cart
- •Task D: Checkout!
- •Iteration D2: Show Cart Contents on Checkout
- •Task E: Shipping
- •Iteration E1: Basic Shipping
- •Task F: Administrivia
- •Iteration F1: Adding Users
- •Iteration F2: Logging In
- •Iteration F3: Limiting Access
- •Finishing Up
- •More Icing on the Cake
- •Task T: Testing
- •Tests Baked Right In
- •Testing Models
- •Testing Controllers
- •Using Mock Objects
- •Test-Driven Development
- •Running Tests with Rake
- •Performance Testing
- •The Rails Framework
- •Rails in Depth
- •Directory Structure
- •Naming Conventions
- •Active Support
- •Logging in Rails
- •Debugging Hints
- •Active Record Basics
- •Tables and Classes
- •Primary Keys and IDs
- •Connecting to the Database
- •Relationships between Tables
- •Transactions
- •More Active Record
- •Acts As
- •Aggregation
- •Single Table Inheritance
- •Validation
- •Callbacks
- •Advanced Attributes
- •Miscellany
- •Action Controller and Rails
- •Context and Dependencies
- •The Basics
- •Routing Requests
- •Action Methods
- •Caching, Part One
- •The Problem with GET Requests
- •Action View
- •Templates
- •Builder templates
- •RHTML Templates
- •Helpers
- •Formatting Helpers
- •Linking to Other Pages and Resources
- •Pagination
- •Form Helpers
- •Layouts and Components
- •Adding New Templating Systems
- •Introducing AJAX
- •The Rails Way
- •Advanced Techniques
- •Action Mailer
- •Sending E-mail
- •Receiving E-mail
- •Testing E-mail
- •Web Services on Rails
- •Dispatching Modes
- •Using Alternate Dispatching
- •Method Invocation Interception
- •Testing Web Services
- •Protocol Clients
- •Securing Your Rails Application
- •SQL Injection
- •Cross-Site Scripting (CSS/XSS)
- •Avoid Session Fixation Attacks
- •Creating Records Directly from Form Parameters
- •Knowing That It Works
- •Deployment and Scaling
- •Picking a Production Platform
- •A Trinity of Environments
- •Iterating in the Wild
- •Maintenance
- •Finding and Dealing with Bottlenecks
- •Case Studies: Rails Running Daily
- •Appendices
- •Introduction to Ruby
- •Ruby Names
- •Regular Expressions
- •Source Code
- •Cross-Reference of Code Samples
- •Resources
- •Index
Chapter 13
Rails in Depth
Having survived our Depot project, now seems like a good time to dig deeper into Rails. For the rest of the book, we’ll go through Rails topic by topic (which pretty much means module by module).
This chapter sets the scene. It talks about all the high-level stuff you need to know to understand the rest: directory structures, configuration, environments, support classes, and debugging hints. But first, we have to ask an important question....
13.1 So Where’s Rails?
One of the interesting things about Rails is how componentized it is. From a developer’s perspective, you spend all your time dealing with high-level things such as Active Record and Action View. There is a component called Rails, but it sits below the other components, silently orchestrating what they do and making them all work together seamlessly. Without the Rails component, not much would happen. But at the same time, only a small part of this underlying infrastructure is relevant to developers in their day- to-day work. We’ll cover the things that are relevant in the rest of this chapter.
13.2 Directory Structure
Rails assumes a certain runtime directory layout. Figure 13.1, on the following page, shows the top-level directories created if you run the command rails my_app. Let’s look at what goes into each directory (although not necessarily in order).
Prepared exclusively for Rida Al Barazi
DIRECTORY STRUCTURE 174
app/
components/
config/
db/
doc/
lib/
my_app/ |
|
log/ |
|
public/
Rakefile
script/
test/
vendor/
Model, View, and Controller files go in subdirectories of app/
Reusable components
Configuration information database connection params
Schema information
Autogenerated documentation
Shared code
Log files produced by the running application
The web-accessible directory. It appears as if your application runs from here
Build script for documentation and tests
Utility scripts
Unit tests, functional tests, mocks, and fixtures
Third-party code
Figure 13.1: Result of rails my_app Command
Prepared exclusively for Rida Al Barazi
Report erratum
DIRECTORY STRUCTURE 175
layouts
layouts
layouts
layouts
layouts
application.rb
store_controller.rb
application_helper.rb
store_helper.rb
product.rb
layouts
add_to_cart.rhtml
layouts index.rhtml
Figure 13.2: The app/ Directory
Most of our work takes place in the app and test directories. The main code for the application lives below the app directory, as shown in Figure 13.2
. We’ll talk more about the structure of the app directory as we look at Active Record, Action Controller, and Action View in more detail later in the book. We might also write code in the components directory (we talk about components starting on page 356).
The doc directory is used for application documentation, produced using RDoc. If you run rake appdoc, you’ll end up with HTML documentation in the directory doc/app. You can create a special first page for this documentation by editing the file doc/README_FOR_APP. Figure 11.1, on page 129, shows the top-level documentation for our store application.
Prepared exclusively for Rida Al Barazi
Report erratum
DIRECTORY STRUCTURE 176
The lib and vendor directories serve similar purposes. Both hold code that’s used by the application but that doesn’t belong exclusively to the application. The lib directory is intended to hold code that you (or your company) wrote, while vendor is for third-party code. If you are using the Subversion tool, you can use the svn:externals property to include code into these directories. In the pre-Gems days, the Rails code itself would be stored in vendor. These vestigial directories are automatically included in the load path to retain backward compatibility.
Rails generates its runtime log files into the log directory. You’ll find a log file in there for each of the Rails environments (development, test, and production). The logs contain more than just simple trace lines; they also contain timing statistics, cache information, and expansions of the database statements executed. We talk about using these log files starting on page 460.
The public directory is the external face of your application. The web server takes this directory as the base of the application. Much of the deployment configuration takes place here, so we’ll defer talking about it until Chapter 22, Deployment and Scaling, on page 440.
The scripts directory holds programs that are useful for developers. Run any of these scripts with no arguments to get usage information.
benchmarker
Get performance benchmarks on one or more methods in your application.
breakpointer
A client that lets you interact with running Rails applications. We talk about this starting on page 187.
console
Allows you to use irb to interact with your Rails application methods.
destroy
Removes autogenerated files created by generate.
generate
load path
→ page 480
irb
→ page 478
A code generator. Out of the box, it will create controllers, mailers, models, scaffolds, and web services. You can also download additional generator modules from the Rails web site.1
1http://wiki.rubyonrails.com/rails/show/AvailableGenerators
Prepared exclusively for Rida Al Barazi
Report erratum
RAILS CONFIGURATION 177
profiler
Creates a runtime-profile summary of a chunk of code from your application.
runner
Executes a method in your application outside the context of the web. You could use this to invoke cache expiry methods from a cron job or handle incoming e-mail.
server
A WEBrick-based server that will run your application. We’ve been using this in our Depot application during development.
The top-level directory also contains a Rakefile. You can use it to run tests (described in Section 12.6, Running Tests with Rake, on page 165), create documentation, extract the current structure of your schema, and more. Type rake - -tasks at a prompt for the full list.
The directories config and db require a little more discussion, so each gets its own section.
13.3 Rails Configuration
Rails runtime configuration is controlled by files in the config directory. These files work in tandem with the concept of runtime environments.
Runtime Environments
The needs of the developer are very different when writing code, testing code, and running that code in production. When writing code, you want lots of logging, convenient reloading of changed source files, in-your-face notification of errors, and so on. In testing, you want a system that exists in isolation so you can have repeatable results. In production, your system should be tuned for performance, and users should be kept away from errors.
To support this, Rails has the concept of runtime environments. Each environment comes with its own set of configuration parameters; run the same application in different environments, and that application changes personality.
The switch that dictates the runtime environment is external to your application. This means that no application code needs to be changed as you
Prepared exclusively for Rida Al Barazi
Report erratum
RAILS CONFIGURATION |
178 |
move from development through testing to production. The way you specify the runtime environment depends on how you run the application. If you’re using script/server, you use the -e option.
depot> ruby script/server -e development | test | production
If you’re using Apache or lighttpd, you set the RAILS_ENV environment variable. This is described on page 449.
If you have special requirements, you can create your own environments. You’ll need to add a new section to the database configuration file and a new file to the config/environments directory. These are described next.
Configuring Database Connections
The file config/database.yml configures your database connections. You’ll find it contains three sections, one for each of the runtime environments. Figure 6.1, on page 52 shows a typical database.yml file
Each section must start with the environment name, followed by a colon. The lines for that section should follow. Each will be indented and contain a key, followed by a colon and the corresponding value. At a minimum, each section has to identify the database adapter (MySQL, Postgres, and so on) and the database to be used. Adapters have their own specific requirements for additional parameters. A full list of these parameters is shown in Figure 14.2, on page 200.
If you need to run your application on different database servers, you have a couple of configuration options. If the database connection is the only difference, you can create multiple sections in database.yml, each named for the environment and the database. You can then use YAML’s aliasing feature to select a particular database.
# Change the following line to point to the right database development: development_sqlite
development_mysql: adapter: mysql
database: depot_development
host: localhost username:
password:
development_sqlite: adapter: sqlite dbfile: my_db
If changing to a different database also changes other things in your application’s configuration, you can create multiple sets of environments (development-mysql, development-postgres, and so on) and create appropriate sections in the database.yml file. You’ll also need to add corresponding files under the environments directory.
Prepared exclusively for Rida Al Barazi
Report erratum
RAILS CONFIGURATION 179
As we’ll see on page 199, you can also reference sections in database.yml when making connections manually.
Environments
The runtime configuration of your application is performed by two files. One, config/environment.rb, is environment independent—it is used regardless of the setting of RAILS_ENV. The second file does depend on the environment: Rails looks for a file named for the current environment in the directory config/environments and loads it during the processing of environment.rb. The standard three environments (development.rb, production.rb, and test.rb) are included by default. You can add your own file if you’ve defined new environment types.
Environment files typically do three things.
•They set up the Ruby load path. This is how your application can find things such as models and views when it’s running.
•They create resources used by your application (such as the logger).
•They set various configuration options, both for Rails and for your application.
The first two of these are normally application-wide and so are done in environment.rb. The configuration options often vary depending on the environment and so are likely to be set in the environment-specific files in the environments directory.
The Load Path
The standard environment automatically includes the following directories (relative to your application’s base directory) into your application’s load path.
•test/mocks/environment. As these are first in the load path, classes defined here override the real versions, enabling you to replace live functionality with stub code during testing. This is described starting on page 161.
•All directories whose names start with an underscore or a lowercase letter under app/models and components.
•The directories app, app/models, app/controllers, app/helpers, app/apis, components, config, lib, vendor, and vendor/rails/*.
Each of these directories is added to the load path only if it exists.
Prepared exclusively for Rida Al Barazi
Report erratum