Ticket #7869: improved_actionpack_readme.diff
| File improved_actionpack_readme.diff, 23.2 kB (added by jeremymcanally, 2 years ago) |
|---|
-
actionpack/README
old new 1 = Action Pack -- On rails from request to response1 = Action Pack -- On Rails from request to response 2 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. 3 Action Pack splits the response mechanism for web requests into two parts: 4 a controller portion (i.e., classes to handle controlling logic such as routing and execution of 5 requests) and a view portion (i.e., rendering a template). This two-step 6 process is known as an action. In an action, the controller will normally create, read, update, or 7 delete (CRUD for short) some sort of model (often backed by a database and ActiveRecord) 8 before choosing either to render a template or redirect to another action. 9 These actions are implemented as public methods on Action Controllers 10 and use Action Views to implement the template rendering. 8 11 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 ContactController 14 would be responsible for listing contacts, creating, deleting, and updating 15 contacts. A WeblogController could be responsible for both posts and comments. 12 == Action Controller 16 13 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. 14 Action Controllers are responsible for handling all the controlling logic for 15 actions, which is accomplished through two main mechanisms: routing and controllers. Routing 16 is a subsystem that allows you to create custom routes for your controller's actions. Controllers 17 are classes that inherit from ActionController::Base, which, like "normal" classes in an application, 18 should group methods and objects that are somehow related. For example, <tt>ContactController</tt> would be responsible 19 for listing contacts, creating, deleting, and updating contacts. A <tt>WeblogController</tt> could be 20 responsible for both posts and comments. All actions on a single controller need to be related some form. 21 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. 22 === Routing 30 23 31 A short rundown of the major features: 24 Routing is the mechanism that allows Rails applications to have URLs like http://www.railsapp.com/myaction/name/13 25 rather than http://www.railsapp.com/myaction?param=name&id=13. When you make a request to a Rails application, the 26 request is broken down and then fed to the routing mechanism; it then decides which controller class to send the request to 27 for handling. Routes are defined in config/routes.rb, and look something like the following. 32 28 33 * Actions grouped in controller as methods instead of separate command objects 34 and can therefore share helper methods.29 # http://www.railsapp.com/ => controller: main, action: introduction 30 map.connect '', :controller => "main", :action => "introduction" 35 31 36 BlogController < 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 => "display") : 46 render(:action => "edit") 47 end 48 49 private 50 def find_customer() Customer.find(params[:id]) end 51 end 32 # http://www.railsapp.com/faq => controller: info, action: faq 33 map.connect 'faq', :controller => "info", :action => "faq" 52 34 53 {Learn more}[link:classes/ActionController/Base.html] 35 For more documentation, check out ActionController::Base and ActionController::Routing. 54 36 37 === Controllers 55 38 56 * Embedded Ruby for templates (no new "easy" template language) 39 Once a route has been recognized (i.e., the routing subsystem knows which controller to hand the 40 request off to), the controller takes the request and looks for an action (i.e., a public method) on 41 itself that matches the name of the action requested. If the action is not found, then <tt>method_missing</tt> 42 is called and (usually) an exception is raised (which results in a "404 Not Found" error in production mode). 43 If it is found, then the controller proceeds to either render the action's template (or some other output) or redirect 44 to another action. 57 45 58 <% for post in @posts %> 59 Title: <%= post.title %> 60 <% end %> 46 ==== Rendering and Redirecting 61 47 62 All post titles: <%= @post.collect{ |p| p.title }.join ", " %> 48 Typically when a controller has finished its logic and is ready to hand the control off to the view, it will 49 call <tt>render</tt>. This call may be explicit (e.g., you may want to render a different template than the one specified 50 for that action) or implicit, which means that if your action is on the controller <tt>AccountController</tt> and 51 is named login, then the template app/views/account/login.erb will be rendered. 63 52 64 <% unless @person.is_client? %> 65 Not for clients to see... 66 <% end %> 67 68 {Learn more}[link:classes/ActionView.html] 53 You can render other output also, such as XML, text, a partial template, or nothing. For more information, see 54 the ActionController::Base or the ActionController::Base#render documentation. 69 55 56 Other times, rather than render, you may want to redirect to another action. Rails handles this for you (using HTTP headers) 57 so you don't have to worry about sending the headers yourself or whether or not your users have JavaScript enabled. Simply 58 put something like the following in your controller... 70 59 71 * Builder-based templates (great for XML content, like RSS) 60 redirect_to :action => 'thanks' 72 61 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" 62 ...and you should have a redirect. See the ActionController::Base documentation or the Action::Controller::Base#redirect_to 63 documentation for more information. 80 64 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 65 ==== Filters 92 66 93 {Learn more}[link:classes/ActionView/Base.html] 67 Action Controller also supports filters, which can be used to execute logic before and/or after an action is executed. 68 So, for example, let's say you want to allow users to upload files, but only if they're logged in. On the upload action, you could 69 a <tt>before_filter</tt> to check their login credentials and an <tt>after_filter</tt> to move their uploaded file. 94 70 71 See the ActionController::Base or ActionController::Filters::ClassMethods documentation for more information and examples. 95 72 96 * Filters for pre and post processing of the response (as methods, procs, and classes) 73 ==== Environment 97 74 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 75 Controllers also have access to parts of the request environment. First, can manipulate and access the CGI session. This allows 76 you to store values on a per-session basis; this means that data such as logins and authentication information can be properly disposed 77 of at end of a user's session (i.e., when they close their browser). Currently, the Rails default is to store these sessions in cookies, 78 but you can also store them in the database and (though it's not preferred) on the filesystem. Check out the ActionController::Sessions 79 documentation for a closer look. Action Controller classes can also access a special hash called <tt>flash</tt>; think of <tt>flash</tt> 80 as a session hash that only lasts through the next request. You can use it to send data to the next action such as error or confirmation 81 messages, which will be disposed of after that request. For more information on <tt>flash</tt>, check out the ActionController::Base 82 documentation or the ActionController::Flash::FlashHash documentation. 120 83 121 * Helpers for forms, dates, action links, and text 84 An Action Controller class also has access to much of the CGI environment, including objects related to the request, the application's 85 response, cookies, and more. See the ActionController::Base documentation for more. 122 86 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, 25) %> 127 128 {Learn more}[link:classes/ActionView/Helpers.html] 87 === Scaffolding 129 88 89 Another feature of Action Controller is scaffolding, a simple CRUD interface to your ActiveRecord 90 models. Simply drop one line of code into your controller... 130 91 131 * Layout sharing for template reuse (think simple version of Struts 132 Tiles[http://jakarta.apache.org/struts/userGuide/dev_tiles.html]) 92 scaffold 'model_name' 133 93 134 class WeblogController < ActionController::Base 135 layout "weblog_layout" 136 137 def hello_world 138 end 139 end 94 ...and you now have a full (yet simple) CRUD interface to your model. This method generates the scaffolding 95 dynamically (i.e., you can't edit it because Rails generates it on the fly), but you can also generate 96 static code files for a scaffolding. See the ActionController::Scaffolding documentation for more information 97 on that. 140 98 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> 99 == Action View 149 100 150 {Learn more}[link:classes/ActionController/Layout/ClassMethods.html] 101 Action View is the templating subsystem of Rails; these templates are rendered 102 when a controller constructs the response by giving control over to a view file 103 for rendering. Most view code files live in app/views/controller_name, and are 104 in a separate directory for each controller. For 105 example, BlogController's views will be in app/views/blog/ while UserController's views 106 will live in app/views/user/ (with the only exceptions to this rule being layouts, which are 107 covered below). 151 108 109 By default, these Action View templates are written using Ruby embedded in tags that are 110 mingled in with the HTML called Embedded Ruby(hence, the file extension erb). You can also 111 use Builder (.builder view files) to construct XML views, along with a number of other 112 alternative view template handlers. For more information on implementing these alternative 113 handlers, see the ActionView::Base documentation. 152 114 153 * Routing makes pretty urls incredibly easy 115 ==== Partials and Layouts 154 116 155 map.connect 'clients/:client_name/:project_name/:controller/:action' 117 Action View also allows you to create partial templates for various purposes. The first of these, 118 simply called partials, allow you to keep your code DRY by breaking commonly used pieces of a view 119 into a separate file that can be included into a view. This could be something like a comments form 120 or a form used to create and edit a model object. You can also create layouts, which allow 121 you to create a consistent look for every action on a controller (or multiple controllers) 122 and plug your views back into that look. For more information and some examples, check out the 123 documentation for ActionView::Base. 156 124 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 125 ==== Helpers 126 To avoid cluttering the templates with code unrelated to the rendering of the 127 template, a number of helper classes are available to provide common behavior 128 for JavaScript, pagination, dates, strings, and more. For example, you can autolink 129 URLs and e-mail addresses in strings using the autolink[ActionView::Helpers::TextHelper#autolink] 130 method. 164 131 165 redirect_to(:client_name => "nextangle", :project_name => "rails") => 166 /clients/nextangle/rails/project/dash 132 auto_link("Visit http://www.rubyonrails.org and e-mail david@loudthinking.com for more info.") 167 133 168 {Learn more}[link:classes/ActionController/Base.html] 134 Even further, in an attempt to keep as much of Rails application written in Ruby as possible, 135 there are also helpers for things like forms and image tags. For example, 136 to create a form for a model object that is an instance of the Post class, 137 you can do something like the following... 169 138 139 <%= form "post" %> 170 140 171 * Javascript and Ajax integration. 141 There are a large number of built-in extensions and methods like this, and it's easy 142 to add specific helpers to keep the separation as the application evolves. Check out 143 the ActionView::Base documentation for more information. 172 144 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] 145 ===== Note 178 146 147 Some of the features of ActionPack, such as scaffolding and form building, are tied to 148 ActiveRecord[http://ar.rubyonrails.org] (an object-relational 149 mapping package), but that doesn't mean that Action Pack depends on Active 150 Record. Action Pack is an independent package that can be used with any sort 151 of backend (Instiki[http://www.instiki.org], which is based on an older version 152 of Action Pack, used Madeleine for example). Read more about the role Action 153 Pack can play when used together with Active Record on 154 http://www.rubyonrails.org. 179 155 180 * Pagination for navigating lists of results.181 182 # controller183 def list184 @pages, @people =185 paginate :people, :order => 'last_name, first_name'186 end187 188 # view189 <%= link_to "Previous page", { :page => @pages.current.previous } if @pages.current.previous %>190 <%= link_to "Next page", { :page => @pages.current.next } if @pages.current.next %>191 192 {Learn more}[link:classes/ActionController/Pagination.html]193 194 195 * Easy testing of both controller and template result through TestRequest/Response196 197 class LoginControllerTest < Test::Unit::TestCase198 def setup199 @controller = LoginController.new200 @request = ActionController::TestRequest.new201 @response = ActionController::TestResponse.new202 end203 204 def test_failing_authenticate205 process :authenticate, :user_name => "nop", :password => ""206 assert flash.has_key?(:alert)207 assert_redirected_to :action => "index"208 end209 end210 211 {Learn more}[link:classes/ActionController/TestRequest.html]212 213 214 * Automated benchmarking and integrated logging215 216 Processing WeblogController#index (for 127.0.0.1 at Fri May 28 00:41:55)217 Parameters: {"action"=>"index", "controller"=>"weblog"}218 Rendering weblog/index (200 OK)219 Completed in 0.029281 (34 reqs/sec)220 221 If Active Record is used as the model, you'll have the database debugging222 as well:223 224 Processing WeblogController#create (for 127.0.0.1 at Sat Jun 19 14:04:23)225 Params: {"controller"=>"weblog", "action"=>"create",226 "post"=>{"title"=>"this is good"} }227 SQL (0.000627) INSERT INTO posts (title) VALUES('this is good')228 Redirected to http://test/weblog/display/5229 Completed in 0.221764 (4 reqs/sec) | DB: 0.059920 (27%)230 231 You specify a logger through a class method, such as:232 233 ActionController::Base.logger = Logger.new("Application Log")234 ActionController::Base.logger = Log4r::Logger.new("Application Log")235 236 237 * Caching at three levels of granularity (page, action, fragment)238 239 class WeblogController < ActionController::Base240 caches_page :show241 caches_action :account242 243 def show244 # the output of the method will be cached as245 # ActionController::Base.page_cache_directory + "/weblog/show/n.html"246 # and the web server will pick it up without even hitting Rails247 end248 249 def account250 # the output of the method will be cached in the fragment store251 # but Rails is hit to retrieve it, so filters are run252 end253 254 def update255 List.update(params[:list][:id], params[:list])256 expire_page :action => "show", :id => params[:list][:id]257 expire_action :action => "account"258 redirect_to :action => "show", :id => params[:list][:id]259 end260 end261 262 {Learn more}[link:classes/ActionController/Caching.html]263 264 265 * Component requests from one controller to another266 267 class WeblogController < ActionController::Base268 # Performs a method and then lets hello_world output its render269 def delegate_action270 do_other_stuff_before_hello_world271 render_component :controller => "greeter", :action => "hello_world"272 end273 end274 275 class GreeterController < ActionController::Base276 def hello_world277 render_text "Hello World!"278 end279 end280 281 The same can be done in a view to do a partial rendering:282 283 Let's see a greeting:284 <%= render_component :controller => "greeter", :action => "hello_world" %>285 286 {Learn more}[link:classes/ActionController/Components.html]287 288 289 * Powerful debugging mechanism for local requests290 291 All exceptions raised on actions performed on the request of a local user292 will be presented with a tailored debugging screen that includes exception293 message, stack trace, request parameters, session contents, and the294 half-finished response.295 296 {Learn more}[link:classes/ActionController/Rescue.html]297 298 299 * Scaffolding for Active Record model objects300 301 class AccountController < ActionController::Base302 scaffold :account303 end304 305 The AccountController now has the full CRUD range of actions and default306 templates: list, show, destroy, new, create, edit, update307 308 {Learn more}[link:classes/ActionController/Scaffolding/ClassMethods.html]309 310 311 * Form building for Active Record model objects312 313 The post object has a title (varchar), content (text), and314 written_on (date)315 316 <%= form "post" %>317 318 ...will generate something like (the selects will have more options, of319 course):320 321 <form action="create" method="POST">322 <p>323 <b>Title:</b><br/>324 <input type="text" name="post[title]" value="<%= @post.title %>" />325 </p>326 <p>327 <b>Content:</b><br/>328 <textarea name="post[content]"><%= @post.title %></textarea>329 </p>330 <p>331 <b>Written on:</b><br/>332 <select name='post[written_on(3i)]'><option>18</option></select>333 <select name='post[written_on(2i)]'><option value='7'>July</option></select>334 <select name='post[written_on(1i)]'><option>2004</option></select>335 </p>336 337 <input type="submit" value="Create">338 </form>339 340 This form generates a params[:post] array that can be used directly in a save action:341 342 class WeblogController < ActionController::Base343 def create344 post = Post.create(params[:post])345 redirect_to :action => "display", :id => post.id346 end347 end348 349 {Learn more}[link:classes/ActionView/Helpers/ActiveRecordHelper.html]350 351 352 * Runs on top of WEBrick, Mongrel, CGI, FCGI, and mod_ruby353 354 355 == Simple example (from outside of Rails)356 357 This example will implement a simple weblog system using inline templates and358 an Active Record model. So let's build that WeblogController with just a few359 methods:360 361 require 'action_controller'362 require 'post'363 364 class WeblogController < ActionController::Base365 layout "weblog/layout"366 367 def index368 @posts = Post.find(:all)369 end370 371 def display372 @post = Post.find(params[:id])373 end374 375 def new376 @post = Post.new377 end378 379 def create380 @post = Post.create(params[:post])381 redirect_to :action => "display", :id => @post.id382 end383 end384 385 WeblogController::Base.view_paths = [ File.dirname(__FILE__) ]386 WeblogController.process_cgi if $0 == __FILE__387 388 The last two lines are responsible for telling ActionController where the389 template files are located and actually running the controller on a new390 request from the web-server (like to be Apache).391 392 And the templates look like this:393 394 weblog/layout.erb:395 <html><body>396 <%= yield %>397 </body></html>398 399 weblog/index.erb:400 <% for post in @posts %>401 <p><%= link_to(post.title, :action => "display", :id => post.id %></p>402 <% end %>403 404 weblog/display.erb:405 <p>406 <b><%= post.title %></b><br/>407 <b><%= post.content %></b>408 </p>409 410 weblog/new.erb:411 <%= form "post" %>412 413 This simple setup will list all the posts in the system on the index page,414 which is called by accessing /weblog/. It uses the form builder for the Active415 Record model to make the new screen, which in turn hands everything over to416 the create action (that's the default target for the form builder when given a417 new model). After creating the post, it'll redirect to the display page using418 an URL such as /weblog/display/5 (where 5 is the id of the post).419 420 421 156 == Examples 422 157 423 158 Action Pack ships with three examples that all demonstrate an increasingly