Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

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 response 
     1= Action Pack -- On Rails from request to response 
    22 
    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. 
     3Action Pack splits the response mechanism for web requests into two parts: 
     4a controller portion (i.e., classes to handle controlling logic such as routing and execution of 
     5requests) and a view portion (i.e., rendering a template). This two-step 
     6process is known as an action.  In an action, the controller will normally create, read, update, or 
     7delete (CRUD for short) some sort of model (often backed by a database and ActiveRecord) 
     8before choosing either to render a template or redirect to another action. 
     9These actions are implemented as public methods on Action Controllers 
     10and use Action Views to implement the template rendering. 
    811 
    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 
    1613 
    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. 
     14Action Controllers are responsible for handling all the controlling logic for 
     15actions, which is accomplished through two main mechanisms: routing and controllers.  Routing 
     16is a subsystem that allows you to create custom routes for your controller's actions.  Controllers 
     17are classes that inherit from ActionController::Base, which, like "normal" classes in an application,  
     18should group methods and objects that are somehow related.  For example, <tt>ContactController</tt> would be responsible  
     19for listing contacts, creating, deleting, and updating contacts. A <tt>WeblogController</tt> could be  
     20responsible for both posts and comments.  All actions on a single controller need to be related some form. 
    2121 
    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 
    3023 
    31 A short rundown of the major features: 
     24Routing is the mechanism that allows Rails applications to have URLs like http://www.railsapp.com/myaction/name/13 
     25rather than http://www.railsapp.com/myaction?param=name&id=13.  When you make a request to a Rails application, the  
     26request is broken down and then fed to the routing mechanism; it then decides which controller class to send the request to 
     27for handling.  Routes are defined in config/routes.rb, and look something like the following. 
    3228 
    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" 
    3531 
    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" 
    5234 
    53   {Learn more}[link:classes/ActionController/Base.html] 
     35For more documentation, check out ActionController::Base and ActionController::Routing. 
    5436 
     37=== Controllers 
    5538 
    56 * Embedded Ruby for templates (no new "easy" template language) 
     39Once a route has been recognized (i.e., the routing subsystem knows which controller to hand the  
     40request off to), the controller takes the request and looks for an action (i.e., a public method) on 
     41itself that matches the name of the action requested.  If the action is not found, then <tt>method_missing</tt> 
     42is called and (usually) an exception is raised (which results in a "404 Not Found" error in production mode). 
     43If it is found, then the controller proceeds to either render the action's template (or some other output) or redirect 
     44to another action. 
    5745 
    58     <% for post in @posts %> 
    59       Title: <%= post.title %> 
    60     <% end %> 
     46==== Rendering and Redirecting 
    6147 
    62     All post titles: <%= @post.collect{ |p| p.title }.join ", " %> 
     48Typically when a controller has finished its logic and is ready to hand the control off to the view, it will 
     49call <tt>render</tt>.  This call may be explicit (e.g., you may want to render a different template than the one specified 
     50for that action) or implicit, which means that if your action is on the controller <tt>AccountController</tt> and 
     51is named login, then the template app/views/account/login.erb will be rendered.   
    6352 
    64     <% unless @person.is_client? %> 
    65       Not for clients to see... 
    66     <% end %> 
    67    
    68   {Learn more}[link:classes/ActionView.html] 
     53You can render other output also, such as XML, text, a partial template, or nothing.  For more information, see 
     54the ActionController::Base or the ActionController::Base#render documentation. 
    6955 
     56Other times, rather than render, you may want to redirect to another action.  Rails handles this for you (using HTTP headers) 
     57so you don't have to worry about sending the headers yourself or whether or not your users have JavaScript enabled.  Simply 
     58put something like the following in your controller... 
    7059 
    71 * Builder-based templates (great for XML content, like RSS) 
     60  redirect_to :action => 'thanks' 
    7261 
    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 
     63documentation for more information. 
    8064 
    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 
    9266 
    93   {Learn more}[link:classes/ActionView/Base.html] 
     67Action Controller also supports filters, which can be used to execute logic before and/or after an action is executed.   
     68So, 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  
     69a <tt>before_filter</tt> to check their login credentials and an <tt>after_filter</tt> to move their uploaded file.   
    9470 
     71See the ActionController::Base or ActionController::Filters::ClassMethods documentation for more information and examples. 
    9572 
    96 * Filters for pre and post processing of the response (as methods, procs, and classes) 
     73==== Environment 
    9774 
    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    
     75Controllers also have access to parts of the request environment.  First, can manipulate and access the CGI session.  This allows 
     76you to store values on a per-session basis; this means that data such as logins and authentication information can be properly disposed 
     77of 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,  
     78but you can also store them in the database and (though it's not preferred) on the filesystem.  Check out the ActionController::Sessions  
     79documentation for a closer look.  Action Controller classes can also access a special hash called <tt>flash</tt>; think of <tt>flash</tt>  
     80as 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 
     81messages, which will be disposed of after that request.  For more information on <tt>flash</tt>, check out the ActionController::Base 
     82documentation or the ActionController::Flash::FlashHash documentation. 
    12083 
    121 * Helpers for forms, dates, action links, and text 
     84An Action Controller class also has access to much of the CGI environment, including objects related to the request, the application's 
     85response, cookies, and more.  See the ActionController::Base documentation for more. 
    12286 
    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 
    12988 
     89Another feature of Action Controller is scaffolding, a simple CRUD interface to your ActiveRecord 
     90models.  Simply drop one line of code into your controller... 
    13091 
    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' 
    13393 
    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 
     95dynamically (i.e., you can't edit it because Rails generates it on the fly), but you can also generate 
     96static code files for a scaffolding.  See the ActionController::Scaffolding documentation for more information 
     97on that. 
    14098 
    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 
    149100 
    150   {Learn more}[link:classes/ActionController/Layout/ClassMethods.html] 
     101Action View is the templating subsystem of Rails; these templates are rendered 
     102when a controller constructs the response by giving control over to a view file 
     103for rendering.  Most view code files live in app/views/controller_name, and are  
     104in a separate directory for each controller.  For 
     105example, BlogController's views will be in app/views/blog/ while UserController's views 
     106will live in app/views/user/ (with the only exceptions to this rule being layouts, which are  
     107covered below). 
    151108 
     109By default, these Action View templates are written using Ruby embedded in tags that are  
     110mingled in with the HTML called Embedded Ruby(hence, the file extension erb).  You can also 
     111use Builder (.builder view files) to construct XML views, along with a number of other 
     112alternative view template handlers.  For more information on implementing these alternative 
     113handlers, see the ActionView::Base documentation. 
    152114 
    153 * Routing makes pretty urls incredibly easy 
     115==== Partials and Layouts 
    154116 
    155     map.connect 'clients/:client_name/:project_name/:controller/:action' 
     117Action View also allows you to create partial templates for various purposes.  The first of these, 
     118simply called partials, allow you to keep your code DRY by breaking commonly used pieces of a view  
     119into a separate file that can be included into a view.  This could be something like a comments form  
     120or a form used to create and edit a model object.  You can also create layouts, which allow 
     121you to create a consistent look for every action on a controller (or multiple controllers) 
     122and plug your views back into that look.  For more information and some examples, check out the 
     123documentation for ActionView::Base. 
    156124 
    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 
     126To avoid cluttering the templates with code unrelated to the rendering of the 
     127template, a number of helper classes are available to provide common behavior  
     128for JavaScript, pagination, dates, strings, and more. For example, you can autolink 
     129URLs and e-mail addresses in strings using the autolink[ActionView::Helpers::TextHelper#autolink] 
     130method. 
    164131 
    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.") 
    167133 
    168   {Learn more}[link:classes/ActionController/Base.html] 
     134Even further, in an attempt to keep as much of Rails application written in Ruby as possible, 
     135there are also helpers for things like forms and image tags.  For example,  
     136to create a form for a model object that is an instance of the Post class,  
     137you can do something like the following... 
    169138 
     139  <%= form "post" %> 
    170140 
    171 * Javascript and Ajax integration. 
     141There are a large number of built-in extensions and methods like this, and it's easy 
     142to add specific helpers to keep the separation as the application evolves.  Check out 
     143the ActionView::Base documentation for more information. 
    172144 
    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 
    178146 
     147Some of the features of ActionPack, such as scaffolding and form building, are tied to 
     148ActiveRecord[http://ar.rubyonrails.org] (an object-relational 
     149mapping package), but that doesn't mean that Action Pack depends on Active 
     150Record. Action Pack is an independent package that can be used with any sort 
     151of backend (Instiki[http://www.instiki.org], which is based on an older version 
     152of Action Pack, used Madeleine for example). Read more about the role Action 
     153Pack can play when used together with Active Record on 
     154http://www.rubyonrails.org. 
    179155 
    180 * Pagination for navigating lists of results. 
    181  
    182     # controller 
    183     def list 
    184       @pages, @people = 
    185         paginate :people, :order => 'last_name, first_name' 
    186     end 
    187  
    188     # view 
    189     <%= 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/Response 
    196  
    197     class LoginControllerTest < Test::Unit::TestCase 
    198       def setup 
    199         @controller = LoginController.new 
    200         @request    = ActionController::TestRequest.new 
    201         @response   = ActionController::TestResponse.new 
    202       end 
    203  
    204       def test_failing_authenticate 
    205         process :authenticate, :user_name => "nop", :password => "" 
    206         assert flash.has_key?(:alert) 
    207         assert_redirected_to :action => "index" 
    208       end 
    209     end 
    210  
    211   {Learn more}[link:classes/ActionController/TestRequest.html] 
    212  
    213  
    214 * Automated benchmarking and integrated logging 
    215  
    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 debugging 
    222     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/5 
    229     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::Base 
    240       caches_page :show 
    241       caches_action :account 
    242        
    243       def show 
    244         # the output of the method will be cached as  
    245         # ActionController::Base.page_cache_directory + "/weblog/show/n.html" 
    246         # and the web server will pick it up without even hitting Rails 
    247       end 
    248        
    249       def account 
    250         # the output of the method will be cached in the fragment store 
    251         # but Rails is hit to retrieve it, so filters are run 
    252       end 
    253        
    254       def update 
    255         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       end 
    260     end 
    261  
    262   {Learn more}[link:classes/ActionController/Caching.html] 
    263  
    264  
    265 * Component requests from one controller to another 
    266  
    267     class WeblogController < ActionController::Base 
    268       # Performs a method and then lets hello_world output its render 
    269       def delegate_action 
    270         do_other_stuff_before_hello_world 
    271         render_component :controller => "greeter",  :action => "hello_world" 
    272       end 
    273     end 
    274    
    275     class GreeterController < ActionController::Base 
    276       def hello_world 
    277         render_text "Hello World!" 
    278       end 
    279     end 
    280    
    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 requests 
    290  
    291     All exceptions raised on actions performed on the request of a local user 
    292     will be presented with a tailored debugging screen that includes exception 
    293     message, stack trace, request parameters, session contents, and the 
    294     half-finished response. 
    295  
    296   {Learn more}[link:classes/ActionController/Rescue.html] 
    297  
    298  
    299 * Scaffolding for Active Record model objects 
    300  
    301     class AccountController < ActionController::Base 
    302       scaffold :account 
    303     end 
    304      
    305     The AccountController now has the full CRUD range of actions and default 
    306     templates: list, show, destroy, new, create, edit, update 
    307      
    308   {Learn more}[link:classes/ActionController/Scaffolding/ClassMethods.html] 
    309  
    310  
    311 * Form building for Active Record model objects 
    312  
    313     The post object has a title (varchar), content (text), and  
    314     written_on (date) 
    315  
    316     <%= form "post" %> 
    317      
    318     ...will generate something like (the selects will have more options, of 
    319     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::Base 
    343       def create 
    344         post = Post.create(params[:post]) 
    345         redirect_to :action => "display", :id => post.id 
    346       end 
    347     end 
    348  
    349   {Learn more}[link:classes/ActionView/Helpers/ActiveRecordHelper.html] 
    350  
    351  
    352 * Runs on top of WEBrick, Mongrel, CGI, FCGI, and mod_ruby 
    353  
    354  
    355 == Simple example (from outside of Rails) 
    356  
    357 This example will implement a simple weblog system using inline templates and 
    358 an Active Record model. So let's build that WeblogController with just a few 
    359 methods: 
    360  
    361   require 'action_controller' 
    362   require 'post' 
    363  
    364   class WeblogController < ActionController::Base 
    365     layout "weblog/layout" 
    366    
    367     def index 
    368       @posts = Post.find(:all) 
    369     end 
    370      
    371     def display 
    372       @post = Post.find(params[:id]) 
    373     end 
    374      
    375     def new 
    376       @post = Post.new 
    377     end 
    378      
    379     def create 
    380       @post = Post.create(params[:post]) 
    381       redirect_to :action => "display", :id => @post.id 
    382     end 
    383   end 
    384  
    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 the 
    389 template files are located and actually running the controller on a new 
    390 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 Active 
    415 Record model to make the new screen, which in turn hands everything over to 
    416 the create action (that's the default target for the form builder when given a 
    417 new model). After creating the post, it'll redirect to the display page using 
    418 an URL such as /weblog/display/5 (where 5 is the id of the post). 
    419  
    420  
    421156== Examples 
    422157 
    423158Action Pack ships with three examples that all demonstrate an increasingly