- •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
FINISHING UP 129
Figure 11.1: Our Application’s Internal Documentation
We’ll then use a before filter to call this method on every action apart from index.
before_filter :find_cart, :except => :index
This lets us remove the five assignments to @cart in the action methods. The final listing is shown starting on page 491.
11.4 Finishing Up
The coding is over, but we can still do a little more tidying before we deploy the application into production.
We might want to check out our application’s documentation. As we’ve been coding, we’ve been writing brief but elegant comments for all our classes and methods. (We haven’t shown them in the code extracts in this book because we wanted to save space.) Rails makes it easy to run Ruby’s RDoc utility on all the source files in an application to create good-looking
RDoc
→ page 480
Prepared exclusively for Rida Al Barazi
Report erratum
MORE ICING ON THE CAKE 130
programmer documentation. But before we generate that documentation, we should probably create a nice introductory page so that future generations of developers will know what our application does. To do this, edit the file doc/README_FOR_APP and enter anything you think might be useful. This file will be processed using RDoc, so you have a fair amount of formatting flexibility.
You can generate the documentation in HTML format using the rake command.
depot> rake appdoc
This generates documentation into the directory doc/app. Figure 11.1, on the page before shows the initial page of the output generated.
11.5 More Icing on the Cake
Although it was fun writing our own login code, and we learned a lot about Rails along the way, in a real-life project we might well have taken a different route.
The Rails generator facility can be extended—folks can create new generators for others to use. If you look at the page that lists these add-ons,3 you’ll see at least two off-the-shelf login controllers, both with a lot more functionality than the one we just wrote. It might be prudent to experiment with these before creating your own user management system.
If you do decide to stick with a roll-your-own login controller, you might be interested in a simple trick suggested by Erik Hatcher. The authorize( ) method that we wrote is invoked before any incoming request. Should it decide that the user isn’t logged in, it redirects to the login action.
Erik suggests extending it to save the incoming request parameters in the session before redirecting to log the user in.
def authorize
unless session[:user_id] flash[:notice] = "Please log in"
#save the URL the user requested so we can hop back to it
#after login
session[:jumpto] = request.parameters
redirect_to(:controller => "login", :action => "login") end
end
3http://wiki.rubyonrails.com/rails/show/AvailableGenerators
Prepared exclusively for Rida Al Barazi
Report erratum
MORE ICING ON THE CAKE 131
Then, once the login is successful, use these saved parameters in a redirect to take the browser to the page the user originally intended to visit.
def login
if request.get? session[:user_id] = nil @user = User.new
else
@user = User.new(params[:user_id]) logged_in_user = @user.try_to_login
if logged_in_user
session[:user_id] = logged_in_user
jumpto = session[:jumpto] || { :action => "index" } session[:jumpto] = nil
redirect_to(jumpto) else
flash[:notice] = "Invalid user/password combination" end
end end
What We Just Did
By the end of this session we’ve done the following.
•We used hook methods in the User model to map the password from plain text on the application side to a hashed form in the database. We also used a hook to remove the plain-text password from the user object once the hashed version had been saved.
•We moved some application-wide controller helper methods into the
ApplicationController class in the file application.rb in app/controllers.
•We used a new style of interaction between action methods and views, where a single action uses the request type to determine if it should display a new view or capture data from an existing one.
•We controlled access to the administration functions using before filters to invoke an authorize( ) method.
•We used a before_destroy( ) hook in the User model to prevent the last user row from being deleted from the database.
•We made the menu in the sidebar dynamic, displaying only if an administrator is logged in.
•We saw how to generate the documentation for our application.
Prepared exclusively for Rida Al Barazi
Report erratum