1
= Action Pack -- On rails from request to response
3
Action Pack splits the response to a web request into a controller part
4
(performing the logic) and a view part (rendering a template). This two-step
5
approach is known as an action, which will normally create, read, update, or
6
delete (CRUD for short) some sort of model part (often backed by a database)
7
before choosing either to render a template or redirecting to another action.
9
Action Pack implements these actions as public methods on Action Controllers
10
and uses Action Views to implement the template rendering. Action Controllers
11
are then responsible for handling all the actions relating to a certain part
12
of an application. This grouping usually consists of actions for lists and for
13
CRUDs revolving around a single (or a few) model objects. So ContactsController
14
would be responsible for listing contacts, creating, deleting, and updating
15
contacts. A WeblogController could be responsible for both posts and comments.
17
Action View templates are written using embedded Ruby in tags mingled in with
18
the HTML. To avoid cluttering the templates with code, a bunch of helper
19
classes provide common behavior for forms, dates, and strings. And it's easy
20
to add specific helpers to keep the separation as the application evolves.
22
Note: Some of the features, such as scaffolding and form building, are tied to
23
ActiveRecord[http://activerecord.rubyonrails.org] (an object-relational
24
mapping package), but that doesn't mean that Action Pack depends on Active
25
Record. Action Pack is an independent package that can be used with any sort
26
of backend (Instiki[http://www.instiki.org], which is based on an older version
27
of Action Pack, used Madeleine for example). Read more about the role Action
28
Pack can play when used together with Active Record on
29
http://www.rubyonrails.org.
31
A short rundown of the major features:
33
* Actions grouped in controller as methods instead of separate command objects
34
and can therefore share helper methods
36
CustomersController < ActionController::Base
38
@customer = find_customer
42
@customer = find_customer
43
@customer.attributes = params[:customer]
45
redirect_to(:action => "show") :
46
render(:action => "edit")
50
def find_customer() Customer.find(params[:id]) end
53
{Learn more}[link:classes/ActionController/Base.html]
56
* Embedded Ruby for templates (no new "easy" template language)
58
<% for post in @posts %>
59
Title: <%= post.title %>
62
All post titles: <%= @posts.collect{ |p| p.title }.join ", " %>
64
<% unless @person.is_client? %>
65
Not for clients to see...
68
{Learn more}[link:classes/ActionView.html]
71
* Builder-based templates (great for XML content, like RSS)
73
xml.rss("version" => "2.0") do
75
xml.title(@feed_title)
77
xml.description "Basecamp: Recent items"
81
for item in @recent_items
83
xml.title(item_title(item))
84
xml.description(item_description(item))
85
xml.pubDate(item_pubDate(item))
86
xml.guid(@recent_items.url(item))
87
xml.link(@recent_items.url(item))
93
{Learn more}[link:classes/ActionView/Base.html]
96
* Filters for pre and post processing of the response (as methods, procs, and classes)
98
class WeblogController < ActionController::Base
99
before_filter :authenticate, :cache, :audit
100
after_filter { |c| c.response.body = Gzip::compress(c.response.body) }
101
after_filter LocalizeFilter
104
# Before this action is run, the user will be authenticated, the cache
105
# will be examined to see if a valid copy of the results already
106
# exists, and the action will be logged for auditing.
108
# After this action has run, the output will first be localized then
109
# compressed to minimize bandwidth usage
114
# Implement the filter with full access to both request and response
118
{Learn more}[link:classes/ActionController/Filters/ClassMethods.html]
121
* Helpers for forms, dates, action links, and text
123
<%= text_field "post", "title", "size" => 30 %>
124
<%= html_date_select(Date.today) %>
125
<%= link_to "New post", :controller => "post", :action => "new" %>
126
<%= truncate(post.title, :length => 25) %>
128
{Learn more}[link:classes/ActionView/Helpers.html]
131
* Layout sharing for template reuse (think simple version of Struts
132
Tiles[http://jakarta.apache.org/struts/userGuide/dev_tiles.html])
134
class WeblogController < ActionController::Base
135
layout "weblog_layout"
141
Layout file (called weblog_layout):
142
<html><body><%= yield %></body></html>
144
Template for hello_world action:
147
Result of running hello_world action:
148
<html><body><h1>Hello world</h1></body></html>
150
{Learn more}[link:classes/ActionController/Layout/ClassMethods.html]
153
* Routing makes pretty urls incredibly easy
155
map.connect 'clients/:client_name/:project_name/:controller/:action'
157
Accessing /clients/37signals/basecamp/project/dash calls ProjectController#dash with
158
{ "client_name" => "37signals", "project_name" => "basecamp" } in params[:params]
160
From that URL, you can rewrite the redirect in a number of ways:
162
redirect_to(:action => "edit") =>
163
/clients/37signals/basecamp/project/dash
165
redirect_to(:client_name => "nextangle", :project_name => "rails") =>
166
/clients/nextangle/rails/project/dash
168
{Learn more}[link:classes/ActionController/Base.html]
171
* Javascript and Ajax integration
173
link_to_function "Greeting", "alert('Hello world!')"
174
link_to_remote "Delete this post", :update => "posts",
175
:url => { :action => "destroy", :id => post.id }
177
{Learn more}[link:classes/ActionView/Helpers/JavaScriptHelper.html]
180
* Easy testing of both controller and rendered template through ActionController::TestCase
182
class LoginControllerTest < ActionController::TestCase
183
def test_failing_authenticate
184
process :authenticate, :user_name => "nop", :password => ""
185
assert flash.has_key?(:alert)
186
assert_redirected_to :action => "index"
190
{Learn more}[link:classes/ActionController/TestCase.html]
193
* Automated benchmarking and integrated logging
195
Processing WeblogController#index (for 127.0.0.1 at Fri May 28 00:41:55)
196
Parameters: {"action"=>"index", "controller"=>"weblog"}
197
Rendering weblog/index (200 OK)
198
Completed in 0.029281 (34 reqs/sec)
200
If Active Record is used as the model, you'll have the database debugging
203
Processing PostsController#create (for 127.0.0.1 at Sat Jun 19 14:04:23)
204
Params: {"controller"=>"posts", "action"=>"create",
205
"post"=>{"title"=>"this is good"} }
206
SQL (0.000627) INSERT INTO posts (title) VALUES('this is good')
207
Redirected to http://example.com/posts/5
208
Completed in 0.221764 (4 reqs/sec) | DB: 0.059920 (27%)
210
You specify a logger through a class method, such as:
212
ActionController::Base.logger = Logger.new("Application Log")
213
ActionController::Base.logger = Log4r::Logger.new("Application Log")
216
* Caching at three levels of granularity (page, action, fragment)
218
class WeblogController < ActionController::Base
220
caches_action :account
223
# the output of the method will be cached as
224
# ActionController::Base.page_cache_directory + "/weblog/show/n.html"
225
# and the web server will pick it up without even hitting Rails
229
# the output of the method will be cached in the fragment store
230
# but Rails is hit to retrieve it, so filters are run
234
List.update(params[:list][:id], params[:list])
235
expire_page :action => "show", :id => params[:list][:id]
236
expire_action :action => "account"
237
redirect_to :action => "show", :id => params[:list][:id]
241
{Learn more}[link:classes/ActionController/Caching.html]
244
* Powerful debugging mechanism for local requests
246
All exceptions raised on actions performed on the request of a local user
247
will be presented with a tailored debugging screen that includes exception
248
message, stack trace, request parameters, session contents, and the
249
half-finished response.
251
{Learn more}[link:classes/ActionController/Rescue.html]
254
* Scaffolding for Active Record model objects
256
class AccountController < ActionController::Base
260
The AccountController now has the full CRUD range of actions and default
261
templates: list, show, destroy, new, create, edit, update
263
{Learn more}[link:classes/ActionController/Scaffolding/ClassMethods.html]
266
* Form building for Active Record model objects
268
The post object has a title (varchar), content (text), and
273
...will generate something like (the selects will have more options, of
276
<form action="create" method="POST">
279
<input type="text" name="post[title]" value="<%= @post.title %>" />
283
<textarea name="post[content]"><%= @post.title %></textarea>
286
<b>Written on:</b><br/>
287
<select name='post[written_on(3i)]'><option>18</option></select>
288
<select name='post[written_on(2i)]'><option value='7'>July</option></select>
289
<select name='post[written_on(1i)]'><option>2004</option></select>
292
<input type="submit" value="Create">
295
This form generates a params[:post] array that can be used directly in a save action:
297
class WeblogController < ActionController::Base
299
post = Post.create(params[:post])
300
redirect_to :action => "show", :id => post.id
304
{Learn more}[link:classes/ActionView/Helpers/ActiveRecordHelper.html]
307
* Runs on top of WEBrick, Mongrel, CGI, FCGI, and mod_ruby
310
== Simple example (from outside of Rails)
312
This example will implement a simple weblog system using inline templates and
313
an Active Record model. So let's build that WeblogController with just a few
316
require 'action_controller'
319
class WeblogController < ActionController::Base
320
layout "weblog/layout"
323
@posts = Post.find(:all)
327
@post = Post.find(params[:id])
335
@post = Post.create(params[:post])
336
redirect_to :action => "show", :id => @post.id
340
WeblogController::Base.view_paths = [ File.dirname(__FILE__) ]
341
WeblogController.process_cgi if $0 == __FILE__
343
The last two lines are responsible for telling ActionController where the
344
template files are located and actually running the controller on a new
345
request from the web-server (like to be Apache).
347
And the templates look like this:
349
weblog/layout.html.erb:
354
weblog/index.html.erb:
355
<% for post in @posts %>
356
<p><%= link_to(post.title, :action => "show", :id => post.id) %></p>
359
weblog/show.html.erb:
361
<b><%= @post.title %></b><br/>
362
<b><%= @post.content %></b>
368
This simple setup will list all the posts in the system on the index page,
369
which is called by accessing /weblog/. It uses the form builder for the Active
370
Record model to make the new screen, which in turn hands everything over to
371
the create action (that's the default target for the form builder when given a
372
new model). After creating the post, it'll redirect to the show page using
373
an URL such as /weblog/5 (where 5 is the id of the post).
378
The latest version of Action Pack can be found at
380
* http://rubyforge.org/project/showfiles.php?group_id=249
382
Documentation can be found at
384
* http://api.rubyonrails.com
389
You can install Action Pack with the following command.
391
% [sudo] ruby install.rb
393
from its distribution directory.
398
Action Pack is released under the MIT license.
403
The Action Pack homepage is http://www.rubyonrails.org. You can find
404
the Action Pack RubyForge page at http://rubyforge.org/projects/actionpack.
405
And as Jim from Rake says:
407
Feel free to submit commits or feature requests. If you send a patch,
408
remember to update the corresponding unit tests. If fact, I prefer
409
new feature to be submitted in the form of new unit tests.