~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/actionpack/README

  • Committer: Richard Lee (Canonical)
  • Date: 2010-10-15 15:17:58 UTC
  • mfrom: (190.1.3 use-case-mapper)
  • Revision ID: richard.lee@canonical.com-20101015151758-wcvmfxrexsongf9d
Merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
= Action Pack -- On rails from request to response
2
 
 
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.
8
 
 
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.
16
 
 
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.
21
 
 
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.
30
 
 
31
 
A short rundown of the major features:
32
 
 
33
 
* Actions grouped in controller as methods instead of separate command objects
34
 
  and can therefore share helper methods
35
 
 
36
 
    CustomersController < ActionController::Base
37
 
      def show
38
 
        @customer = find_customer
39
 
      end
40
 
      
41
 
      def update
42
 
        @customer = find_customer
43
 
        @customer.attributes = params[:customer]
44
 
        @customer.save ? 
45
 
          redirect_to(:action => "show") :
46
 
          render(:action => "edit")
47
 
      end
48
 
      
49
 
      private
50
 
        def find_customer() Customer.find(params[:id]) end
51
 
    end
52
 
 
53
 
  {Learn more}[link:classes/ActionController/Base.html]
54
 
 
55
 
 
56
 
* Embedded Ruby for templates (no new "easy" template language)
57
 
 
58
 
    <% for post in @posts %>
59
 
      Title: <%= post.title %>
60
 
    <% end %>
61
 
 
62
 
    All post titles: <%= @posts.collect{ |p| p.title }.join ", " %>
63
 
 
64
 
    <% unless @person.is_client? %>
65
 
      Not for clients to see...
66
 
    <% end %>
67
 
  
68
 
  {Learn more}[link:classes/ActionView.html]
69
 
 
70
 
 
71
 
* Builder-based templates (great for XML content, like RSS)
72
 
 
73
 
    xml.rss("version" => "2.0") do
74
 
      xml.channel do
75
 
        xml.title(@feed_title)
76
 
        xml.link(@url)
77
 
        xml.description "Basecamp: Recent items"
78
 
        xml.language "en-us"
79
 
        xml.ttl "40"
80
 
 
81
 
        for item in @recent_items
82
 
          xml.item do
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))
88
 
          end
89
 
        end
90
 
      end
91
 
    end
92
 
 
93
 
  {Learn more}[link:classes/ActionView/Base.html]
94
 
 
95
 
 
96
 
* Filters for pre and post processing of the response (as methods, procs, and classes)
97
 
 
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
102
 
      
103
 
      def index
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.
107
 
        
108
 
        # After this action has run, the output will first be localized then 
109
 
        # compressed to minimize bandwidth usage
110
 
      end
111
 
      
112
 
      private
113
 
        def authenticate
114
 
          # Implement the filter with full access to both request and response
115
 
        end
116
 
    end
117
 
  
118
 
  {Learn more}[link:classes/ActionController/Filters/ClassMethods.html]
119
 
  
120
 
 
121
 
* Helpers for forms, dates, action links, and text
122
 
 
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) %>
127
 
 
128
 
  {Learn more}[link:classes/ActionView/Helpers.html]
129
 
 
130
 
 
131
 
* Layout sharing for template reuse (think simple version of Struts 
132
 
  Tiles[http://jakarta.apache.org/struts/userGuide/dev_tiles.html])
133
 
 
134
 
    class WeblogController < ActionController::Base
135
 
      layout "weblog_layout"
136
 
      
137
 
      def hello_world
138
 
      end
139
 
    end
140
 
 
141
 
    Layout file (called weblog_layout):
142
 
      <html><body><%= yield %></body></html>
143
 
    
144
 
    Template for hello_world action:
145
 
      <h1>Hello world</h1>
146
 
    
147
 
    Result of running hello_world action:
148
 
      <html><body><h1>Hello world</h1></body></html>
149
 
 
150
 
  {Learn more}[link:classes/ActionController/Layout/ClassMethods.html]
151
 
 
152
 
 
153
 
* Routing makes pretty urls incredibly easy
154
 
 
155
 
    map.connect 'clients/:client_name/:project_name/:controller/:action'
156
 
 
157
 
    Accessing /clients/37signals/basecamp/project/dash calls ProjectController#dash with
158
 
    { "client_name" => "37signals", "project_name" => "basecamp" } in params[:params]
159
 
    
160
 
    From that URL, you can rewrite the redirect in a number of ways:
161
 
    
162
 
    redirect_to(:action => "edit") =>
163
 
      /clients/37signals/basecamp/project/dash
164
 
 
165
 
    redirect_to(:client_name => "nextangle", :project_name => "rails") =>
166
 
      /clients/nextangle/rails/project/dash
167
 
 
168
 
  {Learn more}[link:classes/ActionController/Base.html]
169
 
 
170
 
 
171
 
* Javascript and Ajax integration
172
 
 
173
 
    link_to_function "Greeting", "alert('Hello world!')"
174
 
    link_to_remote "Delete this post", :update => "posts", 
175
 
                   :url => { :action => "destroy", :id => post.id }
176
 
  
177
 
  {Learn more}[link:classes/ActionView/Helpers/JavaScriptHelper.html]
178
 
 
179
 
 
180
 
* Easy testing of both controller and rendered template through ActionController::TestCase
181
 
 
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"
187
 
      end
188
 
    end
189
 
 
190
 
  {Learn more}[link:classes/ActionController/TestCase.html]
191
 
 
192
 
 
193
 
* Automated benchmarking and integrated logging
194
 
 
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)
199
 
 
200
 
    If Active Record is used as the model, you'll have the database debugging
201
 
    as well:
202
 
 
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%)
209
 
 
210
 
    You specify a logger through a class method, such as:
211
 
 
212
 
    ActionController::Base.logger = Logger.new("Application Log")
213
 
    ActionController::Base.logger = Log4r::Logger.new("Application Log")
214
 
 
215
 
 
216
 
* Caching at three levels of granularity (page, action, fragment)
217
 
 
218
 
    class WeblogController < ActionController::Base
219
 
      caches_page :show
220
 
      caches_action :account
221
 
      
222
 
      def show
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
226
 
      end
227
 
      
228
 
      def account
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
231
 
      end
232
 
      
233
 
      def update
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]
238
 
      end
239
 
    end
240
 
 
241
 
  {Learn more}[link:classes/ActionController/Caching.html]
242
 
 
243
 
 
244
 
* Powerful debugging mechanism for local requests
245
 
 
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.
250
 
 
251
 
  {Learn more}[link:classes/ActionController/Rescue.html]
252
 
 
253
 
 
254
 
* Scaffolding for Active Record model objects
255
 
 
256
 
    class AccountController < ActionController::Base
257
 
      scaffold :account
258
 
    end
259
 
    
260
 
    The AccountController now has the full CRUD range of actions and default
261
 
    templates: list, show, destroy, new, create, edit, update
262
 
    
263
 
  {Learn more}[link:classes/ActionController/Scaffolding/ClassMethods.html]
264
 
 
265
 
 
266
 
* Form building for Active Record model objects
267
 
 
268
 
    The post object has a title (varchar), content (text), and 
269
 
    written_on (date)
270
 
 
271
 
    <%= form "post" %>
272
 
    
273
 
    ...will generate something like (the selects will have more options, of
274
 
    course):
275
 
    
276
 
    <form action="create" method="POST">
277
 
      <p>
278
 
        <b>Title:</b><br/> 
279
 
        <input type="text" name="post[title]" value="<%= @post.title %>" />
280
 
      </p>
281
 
      <p>
282
 
        <b>Content:</b><br/>
283
 
        <textarea name="post[content]"><%= @post.title %></textarea>
284
 
      </p>
285
 
      <p>
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>
290
 
      </p>
291
 
 
292
 
      <input type="submit" value="Create">
293
 
    </form>
294
 
 
295
 
    This form generates a params[:post] array that can be used directly in a save action:
296
 
    
297
 
    class WeblogController < ActionController::Base
298
 
      def create
299
 
        post = Post.create(params[:post])
300
 
        redirect_to :action => "show", :id => post.id
301
 
      end
302
 
    end
303
 
 
304
 
  {Learn more}[link:classes/ActionView/Helpers/ActiveRecordHelper.html]
305
 
 
306
 
 
307
 
* Runs on top of WEBrick, Mongrel, CGI, FCGI, and mod_ruby
308
 
 
309
 
 
310
 
== Simple example (from outside of Rails)
311
 
 
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
314
 
methods:
315
 
 
316
 
  require 'action_controller'
317
 
  require 'post'
318
 
 
319
 
  class WeblogController < ActionController::Base
320
 
    layout "weblog/layout"
321
 
  
322
 
    def index
323
 
      @posts = Post.find(:all)
324
 
    end
325
 
    
326
 
    def show
327
 
      @post = Post.find(params[:id])
328
 
    end
329
 
    
330
 
    def new
331
 
      @post = Post.new
332
 
    end
333
 
    
334
 
    def create
335
 
      @post = Post.create(params[:post])
336
 
      redirect_to :action => "show", :id => @post.id
337
 
    end
338
 
  end
339
 
 
340
 
  WeblogController::Base.view_paths = [ File.dirname(__FILE__) ]
341
 
  WeblogController.process_cgi if $0 == __FILE__
342
 
 
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).
346
 
 
347
 
And the templates look like this:
348
 
 
349
 
  weblog/layout.html.erb:
350
 
    <html><body>
351
 
    <%= yield %>
352
 
    </body></html>
353
 
 
354
 
  weblog/index.html.erb:
355
 
    <% for post in @posts %>
356
 
      <p><%= link_to(post.title, :action => "show", :id => post.id) %></p>
357
 
    <% end %>
358
 
 
359
 
  weblog/show.html.erb:
360
 
    <p>
361
 
      <b><%= @post.title %></b><br/>
362
 
      <b><%= @post.content %></b>
363
 
    </p>
364
 
 
365
 
  weblog/new.html.erb:
366
 
    <%= form "post" %>
367
 
  
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).
374
 
 
375
 
 
376
 
== Download
377
 
 
378
 
The latest version of Action Pack can be found at
379
 
 
380
 
* http://rubyforge.org/project/showfiles.php?group_id=249
381
 
 
382
 
Documentation can be found at 
383
 
 
384
 
* http://api.rubyonrails.com
385
 
 
386
 
 
387
 
== Installation
388
 
 
389
 
You can install Action Pack with the following command.
390
 
 
391
 
  % [sudo] ruby install.rb
392
 
 
393
 
from its distribution directory.
394
 
 
395
 
 
396
 
== License
397
 
 
398
 
Action Pack is released under the MIT license.
399
 
 
400
 
 
401
 
== Support
402
 
 
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:
406
 
 
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.