- •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
ADDING NEW TEMPLATING SYSTEMS |
370 |
ActionController::Caching::Fragments::FileStore.new(path) |
|
Keeps cached fragments in the directory path. |
|
ActionController::Caching::Fragments::DRbStore.new(url) |
|
Stores cached fragments in an external DRb server. |
|
ActionController::Caching::Fragments::MemCachedStore.new(host) |
|
Stores fragments in a memcached server. |
|
17.11 Adding New Templating Systems
At the start of this chapter we explained that Rails comes with two templating systems, but that it’s easy to add your own. This is more advanced stuff, and you can safely skip to the start of the next chapter without losing your Rails merit badge.
A template handler is simply a class that meets two criteria.
• Its constructor must take a single parameter, the view object.
• It implements a single method, render( ), that takes the text of the template and a hash of local variable values and returns the result of rendering that template.
|
Let’s start with a trivial template. The RDoc system, used to produce |
|
|
documentation from Ruby comments, includes a formatter that takes text |
|
|
in a fairly straightforward plain-text layout and converts it to HTML. Let’s |
|
|
use it to format template pages. We’ll create these templates with the file |
|
|
extension .rdoc. |
|
|
The template handler is a simple class with the two methods described |
|
|
previously. We’ll put it in the file rdoc_template.rb in the lib directory. |
|
File 185 |
require 'rdoc/markup/simple_markup' |
|
|
require 'rdoc/markup/simple_markup/inline' |
|
|
require 'rdoc/markup/simple_markup/to_html' |
|
|
class RDocTemplate |
|
|
def initialize(view) |
|
|
@view = view |
|
|
end |
|
|
def render(template, assigns) |
|
|
markup |
= SM::SimpleMarkup.new |
generator = SM::ToHtml.new markup.convert(template, generator)
end end
Now we need to register the handler. This can go in your environment file, or you can set it up in application.rb in the app/controllers directory.
Prepared exclusively for Rida Al Barazi
Report erratum
|
ADDING NEW TEMPLATING SYSTEMS |
371 |
File 157 |
require "rdoc_template" |
|
|
ActionView::Base.register_template_handler("rdoc", RDocTemplate) |
|
|
The registration call says that any template file whose name ends with |
|
|
.rdoc will be handled by the RDocTemplate class. We can test this by creat- |
|
|
ing a template called example.rdoc and accessing it via a freshly generated |
|
|
test controller. |
|
RDocTemplate
= Greetings from RDoc
Let's see if we're doing
_real_ formatting...
* This should be * A bullet list
all nicely formatted
Making Dynamic Templates
The rhtml and rxml templates share their environment with the controller— they have access to the controller instance variables. They can also get passed local variables if they’re invoked as partials. We can give our own templates the same privileges. Just how you achieve this depends on what you want your template to do. Here we’ll construct something fairly artificial: a reval template that contains lines of Ruby code. When rendered, each line is displayed, along with its value. So, if a template called test.reval contains
a = 1 3 + a
@request.path
we might expect to see the output
a |
= |
1 |
=> |
1 |
3 |
+ |
a |
=> |
4 |
@request.path => /text/example1
Note how the template has access to the @request variable. We achieve this piece of magic by creating a Ruby binding (basically a scope for variable values) and populating it with the values of instance and local variables set into the view by the controller. Note that the renderer also sets the response content type to text/plain; we don’t want our result interpreted
Prepared exclusively for Rida Al Barazi
Report erratum
ADDING NEW TEMPLATING SYSTEMS 372
as HTML. We could also have defined an acessor method called request( ), which would make our template handler more like Rails’ built-in ones.
File 184 |
class EvalTemplate |
def initialize(view) @view = view
end
def render(template, assigns)
#create an anonymous object and get its binding env = Object.new.send(:binding)
bind = env.send(:binding)
#Add in the instance variables from the view
@view.assigns.each do |key, value|
env.instance_variable_set("@#{key}", value) end
# and local variables if we're a partial assigns.each do |key, value|
eval("#{key} = #{value}", bind) end
@view.controller.headers["Content-Type"] ||= 'text/plain'
#evaluate each line and show the original alongside
#its value
template.split(/\n/).map do |line|
line + " => " + eval(line, bind).to_s end.join("\n")
end end
Prepared exclusively for Rida Al Barazi
Report erratum
This chapter was written by Thomas Fuchs (http://mir.aculo.us/ ), a software architect from Vienna, Austria. He’s been hacking on web applications since 1996. He contributed the autocompleting text fields, came up with most of the visual effects, and laid the groundwork for the file uploads with progress information extension.
Chapter 18
The Web, V2.0
Two things have plagued application developers since the day the Web was born.
•The statelessness of HTTP connections
•The fact we can’t call the server between page views
The problems caused by the lack of state were quickly addressed by using cookies for identifying the user and by having server-stored sessions. Rails has the session object for this.
The second problem wasn’t as easy to address. The <frameset> and <frame> tags were a partial solution, but their downsides drove many web developers to near insanity. Someone invented the <iframe>, but it didn’t solve the problem either.
In a time where OpenGL-accelerated graphical user interfaces rule the desktop, most web applications look like they’re running on dumb terminals from the 1960s.
Well, that’s all over now. The plague has lifted.
Welcome to the Web, version 2.0.
18.1 Introducing AJAX
AJAX (short for Asynchronous JavaScript and XML)1 is a technique that AJAX extends the traditional web application model to allow for in-page server requests.
1The term was coined by Adaptive Path. For more information, see Jesse James Garrett’s
essay at http://www.adaptivepath.com/publications/essays/archives/000385.php.
Prepared exclusively for Rida Al Barazi
INTRODUCING AJAX 374
Browser |
Server |
Traditional
Web Application
AJAX'd
Web Application
Request a page
HTTP GET or POST
Return HTML
Render page
Wait for events
Ajax calls
HTTP GET
or POST Return HTML
fragments; script and/ or data
Do something with data, render page fragments, execute scripts
Figure 18.1: AJAX—In-Page Server Requests for Web Applications
What does this mean in practice? It allows you to do the kinds of things you might see when you use GMail, Google Maps or Google Suggest. Here are web pages that work just like a desktop application. How can this be? Normally the server sends down a page, you click something that sends a request to the server, and then the server sends down another page.
With AJAX, this changes. You can make on-the-fly requests from your web client to the server, and the server can respond with all sorts of useful things.
•HTML fragments;
•Scripts to be executed by the client;
•Arbitrary data
By returning HTML fragments from the server, AJAX allows us to replace or add to existing page elements. You can replace a paragraph or a product
Prepared exclusively for Rida Al Barazi
Report erratum
INTRODUCING AJAX 375
image here or add some rows to a table over there. This cuts down on bandwidth used and makes your pages feel zippier.
By executing scripts (JavaScript, that is) returned by the server, you can completely change the look, content, and behavior of the currently displayed HTML page.
Finally, you can return arbitrary data to be processed by JavaScript on the client.2
XMLHttpRequest
AJAX uses a feature that was first implemented in Microsoft’s Internet |
|
Explorer but has since come to all major web browsers (those based on |
|
Gecko such as Firefox and Mozilla and others such as Opera and Apple’s |
|
Safari). This feature is a JavaScript object called XMLHttpRequest. |
XMLHttpRequest |
This object allows you to construct HTTP calls from the client to the server. |
|
It also lets you access and process data sent by the server in response to |
|
that request. |
|
Note that the XML part of the name XMLHttpRequest is there for historical |
|
reasons (so even new stuff has a history!)—you’re not in any way required |
|
to use XML with it. Just forget about the XML stuff for now and see it for |
|
what it is—a wrapper for HTTP requests. |
|
The A in AJAX
AJAX calls are asynchronous, or nonblocking.3 After you send your request to the server, the main browser event loop will start listening for an event raised by your instance of XMLHttpRequest. Any other browser events, such as users clicking links, will continue to work.
This means that AJAX data returned from the server to the client is just another event on your page. Anything can happen between the sending of the request and the data returning. Keep that in mind as your applications become more complex.
2It was expected that this is where the XML part of AJAX would come into play—servers were supposed to send XML messages back to the clients. But there’s nothing that says you have to do this. You can send back JavaScript code fragments, plain text, or even YAML.
3In fact, you can also do synchronous calls, but it’s a very, very bad idea. Your browser
will stop responding to user actions until the request has been processed—probably leading the user to believe that the browser has crashed.
Prepared exclusively for Rida Al Barazi
Report erratum