- •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
HELPERS 332
The strange sequence %3cb%3ethere%3c/b%3e is a URL-encoded version of the HTML <b>there</b>. Our template will substitute this in, and the page will be displayed with the word there in bold.
This might not seem like a big deal, but at best it leaves your pages open to defacement. At worst, as we’ll see in Chapter 21, Securing Your Rails Application, on page 427, it’s a gaping security hole that makes your site vulnerable to attack and data loss.
Fortunately, the solution is simple. Always escape any text that you substitute into templates that isn’t meant to be HTML. rhtml templates come with a method to do just that. Its long name is html_escape( ), but most people just call it h( ).
The value of name is <%= h(params[:name]) %>
Get into the habit of typing h( immediately after you type <%=.
You can’t use the h( ) method if you need to substitute HTML-formatted text into a tempate, as the HTML tags will be escaped: the user will see <em>hello</em> rather than hello. However, you shouldn’t just take HTML created by someone else and display it on your page. As we’ll see in Chapter 21, Securing Your Rails Application, on page 427, this makes your application vulnerable to a number of attacks.
The sanitize( ) method offers some protection. It takes a string containing HTML and cleans up dangerous elements: <form> and <script> tags are escaped, and on= attributes and links starting javascript: are removed.
The product descriptions in our Depot application were rendered as HTML (that is, they were not escaped using the h( ) method). This allowed us to embed formatting information in them. If we allowed people outside our organization to enter these descriptions, it would be prudent to use the sanitize( ) method to reduce the risk of our site being attacked successfully.
17.4 Helpers
Earlier we said that it’s OK to put code in templates. Now we’re going to modify that statement. It’s perfectly acceptable to put some code in templates—that’s what makes them dynamic. However, it’s poor style to put too much code in templates.
There are two main reasons for this. First, the more code you put in the view side of your application, the easier it is to let discipline slip and start adding application-level functionality to the template code. This is definitely poor form; you want to put application stuff in the controller
Prepared exclusively for Rida Al Barazi
Report erratum
HELPERS 333
David Says. . .
Where’s the Template Language?
Many environments have stigmatized the idea of code in the view—for good reasons. Not all programming languages lend themselves well to dealing with presentational logic in a succinct and effective way. To cope, these environments come up with an alternative language to be used instead of the primary when dealing with the view. PHP has Smarty, Java has Velocity, Python has Cheetah.
Rails doesn’t have anything because Ruby is already an incredibly wellsuited language for dealing with presentational logic. Do you need to show the capitalized body of a post, but truncating it to 30 characters? Here’s the view code in Ruby.
<%= truncate(@post.body.capitalize, 30) %>
On top of being a good fit for presentation logic, using Ruby in the view cuts down on the mental overhead of switching between the different layers in the application. It’s all Ruby—for configuration, for the models, for the controllers, and for the view.
and model layers so that it is available everywhere. This will pay off when you add new ways of viewing the application.
The other reason is that rhtml is basically HTML. When you edit it, you’re editing an HTML file. If you have the luxury of having professional designers create your layouts, they’ll want to work with HTML. Putting a bunch of Ruby code in there just makes it hard to work with.
Rails provides a nice compromise in the form of helpers. A helper is simply a module containing methods that assist a view. Helper methods are output-centric. They exist to generate HTML (or XML)—a helper extends the behavior of a template.
By default, each controller gets its own helper module. It won’t be surprising to learn that Rails makes certain assumptions to help link the helpers into the controller and its views. If a controller is named BlogController, it will automatically look for a helper module called BlogHelper in the file blog_helper.rb in the app/helpers directory. You don’t have to remember all these details—the generate controller script creates a stub helper module automatically.
Prepared exclusively for Rida Al Barazi
Report erratum
HELPERS 334
For example, the views for our store controller might set the title of generated pages from the instance variable @page_title (which presumably gets set by the controller). If @page_title isn’t set, the template uses the text “Pragmatic Store.” The top of each view template might look like
<h3><%= @page_title || "Pragmatic Store" %></h3>
<!-- ... -->
We’d like to remove the duplication between templates: if the default name of the store changes, we don’t want to edit each view. So let’s move the code that works out the page title into a helper method. As we’re in the store controller, we edit the file store_helper.rb in app/helpers.
module StoreHelper def page_title
@page_title || "Pragmatic Store" end
end
Now the view code simply calls the helper method.
<h3><%= page_title %></h3>
<!-- ... -->
(We might want to eliminate even more duplication by moving the rendering of the entire title into a separate partial template, shared by all the controller’s views, but we don’t talk about them until Section 17.9, Partial Page Templates, on page 359.)
Sharing Helpers
Sometimes a helper is just so good that you have to share it among all your controllers. Perhaps you have a spiffy date-formatting helper that you want to use in all of your controllers. You have two options.
First, you could add the helper method to the file application_helper.rb in app/helpers. As its name suggests, this helper is global to the entire application, and hence its methods are available to all views.
Alternatively, you can tell controllers to include additional helper modules using the helper declaration. For example, if our date formatting helper was in the file date_format_helper.rb in app/helpers, we could load it and mix it into a particular controller’s set of views using
class ParticularController < ApplicationController
helper :date_format
# ...
You can include an already-loaded class as a helper by giving its name to the helper declaration.
Prepared exclusively for Rida Al Barazi
Report erratum
FORMATTING HELPERS 335
class ParticularController < ApplicationController
helper DateFormat
# ...
You can add controller methods into the template using helper_method. Think hard before doing this—you risk mixing business and presentation logic. See the documentation for helper_method for details.
17.5 Formatting Helpers
Rails comes with a bunch of built-in helper methods, available to all views. In this section we’ll touch on the highlights, but you’ll probably want to look at the Action View RDoc for the specifics—there’s a lot of functionality in there.
One set of helper methods deals with the formatting of dates, numbers, and text.
<%= distance_of_time_in_words(Time.now, Time.local(2005, 12, 25)) %>
248 days
<%= distance_of_time_in_words(Time.now, Time.now + 33, false) %>
1 minute
<%= distance_of_time_in_words(Time.now, Time.now + 33, true) %> half a minute
<%= time_ago_in_words(Time.local(2004, 12, 25)) %>
116 days
<%= human_size(123_456) %>
120.6 KB
<%= number_to_currency(123.45) %> $123.45
<%= number_to_currency(234.56, :unit => "CAN$", :precision => 0) %>
CAN$235.
<%= number_to_percentage(66.66666) %>
66.667%
<%= number_to_percentage(66.66666, :precision => 1) %>
66.7%
<%= number_to_phone(2125551212) %>
212-555-1212
Prepared exclusively for Rida Al Barazi
Report erratum
FORMATTING HELPERS 336
<%= number_to_phone(2125551212, :area_code => true, :delimiter => " ") %>
(212) 555 1212
<%= number_with_delimiter(12345678) %>
12,345,678
<%= number_with_delimiter(12345678, delimiter = "_") %>
12_345_678
<%= number_with_precision(50.0/3) %>
16.667
<%= number_with_precision(50.0/3, 1) %>
16.7
The debug( ) method dumps out its parameter using YAML and escapes the result so it can be displayed in an HTML page. This can help when trying to look at the values in model objects or request parameters.
<%= debug(params) %>
--- !ruby/hash:HashWithIndifferentAccess name: Dave
language: Ruby action: objects controller: test
Yet another set of helpers deal with text. There are methods to truncate strings and highlight words in a string (useful to show search results, perhaps).
<%= simple_format(@trees) %>
Formats a string, honoring line and paragraph breaks. You could give it the plain text of the Joyce Kilmer poem Trees and it would add the HTML to format it as follows:
<p> I think that I shall never see <br />A poem lovely as a tree.</p>
<p>A tree whose hungry mouth is prest
<br />Against the sweet earth’s flowing breast; </p>
<%= excerpt(@trees, "lovely", 8) %>
...A poem lovely as a tre...
<%= highlight(@trees, "tree") %>
I think that I shall never see
A poem lovely as a <strong class="highlight">tree</strong>.
Prepared exclusively for Rida Al Barazi
Report erratum