~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/actionpack/lib/action_controller/base.rb

  • Committer: Michael Forrest
  • Date: 2010-10-15 16:28:50 UTC
  • Revision ID: michael.forrest@canonical.com-20101015162850-tj2vchanv0kr0dun
refrozeĀ gems

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
require 'set'
 
2
 
 
3
module ActionController #:nodoc:
 
4
  class ActionControllerError < StandardError #:nodoc:
 
5
  end
 
6
 
 
7
  class SessionRestoreError < ActionControllerError #:nodoc:
 
8
  end
 
9
 
 
10
  class RenderError < ActionControllerError #:nodoc:
 
11
  end
 
12
 
 
13
  class RoutingError < ActionControllerError #:nodoc:
 
14
    attr_reader :failures
 
15
    def initialize(message, failures=[])
 
16
      super(message)
 
17
      @failures = failures
 
18
    end
 
19
  end
 
20
 
 
21
  class MethodNotAllowed < ActionControllerError #:nodoc:
 
22
    attr_reader :allowed_methods
 
23
 
 
24
    def initialize(*allowed_methods)
 
25
      super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
 
26
      @allowed_methods = allowed_methods
 
27
    end
 
28
 
 
29
    def allowed_methods_header
 
30
      allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', '
 
31
    end
 
32
 
 
33
    def handle_response!(response)
 
34
      response.headers['Allow'] ||= allowed_methods_header
 
35
    end
 
36
  end
 
37
 
 
38
  class NotImplemented < MethodNotAllowed #:nodoc:
 
39
  end
 
40
 
 
41
  class UnknownController < ActionControllerError #:nodoc:
 
42
  end
 
43
 
 
44
  class UnknownAction < ActionControllerError #:nodoc:
 
45
  end
 
46
 
 
47
  class MissingFile < ActionControllerError #:nodoc:
 
48
  end
 
49
 
 
50
  class RenderError < ActionControllerError #:nodoc:
 
51
  end
 
52
 
 
53
  class SessionOverflowError < ActionControllerError #:nodoc:
 
54
    DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
 
55
 
 
56
    def initialize(message = nil)
 
57
      super(message || DEFAULT_MESSAGE)
 
58
    end
 
59
  end
 
60
 
 
61
  class DoubleRenderError < ActionControllerError #:nodoc:
 
62
    DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
 
63
 
 
64
    def initialize(message = nil)
 
65
      super(message || DEFAULT_MESSAGE)
 
66
    end
 
67
  end
 
68
 
 
69
  class RedirectBackError < ActionControllerError #:nodoc:
 
70
    DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
 
71
 
 
72
    def initialize(message = nil)
 
73
      super(message || DEFAULT_MESSAGE)
 
74
    end
 
75
  end
 
76
 
 
77
  class UnknownHttpMethod < ActionControllerError #:nodoc:
 
78
  end
 
79
 
 
80
  # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed
 
81
  # on request and then either render a template or redirect to another action. An action is defined as a public method
 
82
  # on the controller, which will automatically be made accessible to the web-server through Rails Routes.
 
83
  #
 
84
  # A sample controller could look like this:
 
85
  #
 
86
  #   class GuestBookController < ActionController::Base
 
87
  #     def index
 
88
  #       @entries = Entry.find(:all)
 
89
  #     end
 
90
  #
 
91
  #     def sign
 
92
  #       Entry.create(params[:entry])
 
93
  #       redirect_to :action => "index"
 
94
  #     end
 
95
  #   end
 
96
  #
 
97
  # Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
 
98
  # after executing code in the action. For example, the +index+ action of the GuestBookController would render the
 
99
  # template <tt>app/views/guestbook/index.erb</tt> by default after populating the <tt>@entries</tt> instance variable.
 
100
  #
 
101
  # Unlike index, the sign action will not render a template. After performing its main purpose (creating a
 
102
  # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external
 
103
  # "302 Moved" HTTP response that takes the user to the index action.
 
104
  #
 
105
  # The index and sign represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
 
106
  # Most actions are variations of these themes.
 
107
  #
 
108
  # == Requests
 
109
  #
 
110
  # Requests are processed by the Action Controller framework by extracting the value of the "action" key in the request parameters.
 
111
  # This value should hold the name of the action to be performed. Once the action has been identified, the remaining
 
112
  # request parameters, the session (if one is available), and the full request with all the HTTP headers are made available to
 
113
  # the action through instance variables. Then the action is performed.
 
114
  #
 
115
  # The full request object is available with the request accessor and is primarily used to query for HTTP headers. These queries
 
116
  # are made by accessing the environment hash, like this:
 
117
  #
 
118
  #   def server_ip
 
119
  #     location = request.env["SERVER_ADDR"]
 
120
  #     render :text => "This server hosted at #{location}"
 
121
  #   end
 
122
  #
 
123
  # == Parameters
 
124
  #
 
125
  # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
 
126
  # which returns a hash. For example, an action that was performed through <tt>/weblog/list?category=All&limit=5</tt> will include
 
127
  # <tt>{ "category" => "All", "limit" => 5 }</tt> in params.
 
128
  #
 
129
  # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
 
130
  #
 
131
  #   <input type="text" name="post[name]" value="david">
 
132
  #   <input type="text" name="post[address]" value="hyacintvej">
 
133
  #
 
134
  # A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
 
135
  # If the address input had been named "post[address][street]", the params would have included
 
136
  # <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
 
137
  #
 
138
  # == Sessions
 
139
  #
 
140
  # Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
 
141
  # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
 
142
  # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
 
143
  # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
 
144
  #
 
145
  # You can place objects in the session by using the <tt>session</tt> method, which accesses a hash:
 
146
  #
 
147
  #   session[:person] = Person.authenticate(user_name, password)
 
148
  #
 
149
  # And retrieved again through the same hash:
 
150
  #
 
151
  #   Hello #{session[:person]}
 
152
  #
 
153
  # For removing objects from the session, you can either assign a single key to +nil+:
 
154
  #
 
155
  #   # removes :person from session
 
156
  #   session[:person] = nil
 
157
  #
 
158
  # or you can remove the entire session with +reset_session+.
 
159
  #
 
160
  # Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
 
161
  # This prevents the user from tampering with the session but also allows him to see its contents.
 
162
  #
 
163
  # Do not put secret information in cookie-based sessions!
 
164
  #
 
165
  # Other options for session storage are:
 
166
  #
 
167
  # * ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and,
 
168
  #   unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set
 
169
  #
 
170
  #     config.action_controller.session_store = :active_record_store
 
171
  #
 
172
  #   in your <tt>config/environment.rb</tt> and run <tt>rake db:sessions:create</tt>.
 
173
  #
 
174
  # * MemCacheStore - Sessions are stored as entries in your memcached cache.
 
175
  #   Set the session store type in <tt>config/environment.rb</tt>:
 
176
  #
 
177
  #     config.action_controller.session_store = :mem_cache_store
 
178
  #
 
179
  #   This assumes that memcached has been installed and configured properly.
 
180
  #   See the MemCacheStore docs for more information.
 
181
  #
 
182
  # == Responses
 
183
  #
 
184
  # Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
 
185
  # object is generated automatically through the use of renders and redirects and requires no user intervention.
 
186
  #
 
187
  # == Renders
 
188
  #
 
189
  # Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering
 
190
  # of a template. Included in the Action Pack is the Action View, which enables rendering of ERb templates. It's automatically configured.
 
191
  # The controller passes objects to the view by assigning instance variables:
 
192
  #
 
193
  #   def show
 
194
  #     @post = Post.find(params[:id])
 
195
  #   end
 
196
  #
 
197
  # Which are then automatically available to the view:
 
198
  #
 
199
  #   Title: <%= @post.title %>
 
200
  #
 
201
  # You don't have to rely on the automated rendering. Especially actions that could result in the rendering of different templates will use
 
202
  # the manual rendering methods:
 
203
  #
 
204
  #   def search
 
205
  #     @results = Search.find(params[:query])
 
206
  #     case @results
 
207
  #       when 0 then render :action => "no_results"
 
208
  #       when 1 then render :action => "show"
 
209
  #       when 2..10 then render :action => "show_many"
 
210
  #     end
 
211
  #   end
 
212
  #
 
213
  # Read more about writing ERb and Builder templates in link:classes/ActionView/Base.html.
 
214
  #
 
215
  # == Redirects
 
216
  #
 
217
  # Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to a database,
 
218
  # we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're going to reuse (and redirect to)
 
219
  # a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
 
220
  #
 
221
  #   def create
 
222
  #     @entry = Entry.new(params[:entry])
 
223
  #     if @entry.save
 
224
  #       # The entry was saved correctly, redirect to show
 
225
  #       redirect_to :action => 'show', :id => @entry.id
 
226
  #     else
 
227
  #       # things didn't go so well, do something else
 
228
  #     end
 
229
  #   end
 
230
  #
 
231
  # In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method which is then executed.
 
232
  #
 
233
  # == Calling multiple redirects or renders
 
234
  #
 
235
  # An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
 
236
  #
 
237
  #   def do_something
 
238
  #     redirect_to :action => "elsewhere"
 
239
  #     render :action => "overthere" # raises DoubleRenderError
 
240
  #   end
 
241
  #
 
242
  # If you need to redirect on the condition of something, then be sure to add "and return" to halt execution.
 
243
  #
 
244
  #   def do_something
 
245
  #     redirect_to(:action => "elsewhere") and return if monkeys.nil?
 
246
  #     render :action => "overthere" # won't be called if monkeys is nil
 
247
  #   end
 
248
  #
 
249
  class Base
 
250
    DEFAULT_RENDER_STATUS_CODE = "200 OK"
 
251
 
 
252
    include StatusCodes
 
253
 
 
254
    cattr_reader :protected_instance_variables
 
255
    # Controller specific instance variables which will not be accessible inside views.
 
256
    @@protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller
 
257
                                        @action_name @before_filter_chain_aborted @action_cache_path @_session @_headers @_params
 
258
                                        @_flash @_response)
 
259
 
 
260
    # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets,
 
261
    # and images to a dedicated asset server away from the main web server. Example:
 
262
    #   ActionController::Base.asset_host = "http://assets.example.com"
 
263
    @@asset_host = ""
 
264
    cattr_accessor :asset_host
 
265
 
 
266
    # All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors.
 
267
    # When the application is ready to go public, this should be set to false, and the protected method <tt>local_request?</tt>
 
268
    # should instead be implemented in the controller to determine when debugging screens should be shown.
 
269
    @@consider_all_requests_local = true
 
270
    cattr_accessor :consider_all_requests_local
 
271
 
 
272
    # Indicates whether to allow concurrent action processing. Your
 
273
    # controller actions and any other code they call must also behave well
 
274
    # when called from concurrent threads. Turned off by default.
 
275
    @@allow_concurrency = false
 
276
    cattr_accessor :allow_concurrency
 
277
 
 
278
    # Modern REST web services often need to submit complex data to the web application.
 
279
    # The <tt>@@param_parsers</tt> hash lets you register handlers which will process the HTTP body and add parameters to the
 
280
    # <tt>params</tt> hash. These handlers are invoked for POST and PUT requests.
 
281
    #
 
282
    # By default <tt>application/xml</tt> is enabled. A XmlSimple class with the same param name as the root will be instantiated
 
283
    # in the <tt>params</tt>. This allows XML requests to mask themselves as regular form submissions, so you can have one
 
284
    # action serve both regular forms and web service requests.
 
285
    #
 
286
    # Example of doing your own parser for a custom content type:
 
287
    #
 
288
    #   ActionController::Base.param_parsers[Mime::Type.lookup('application/atom+xml')] = Proc.new do |data|
 
289
    #      node = REXML::Document.new(post)
 
290
    #     { node.root.name => node.root }
 
291
    #   end
 
292
    #
 
293
    # Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the
 
294
    # root node for such requests. The new default is to keep the root, such that "<r><name>David</name></r>" results
 
295
    # in <tt>params[:r][:name]</tt> for "David" instead of <tt>params[:name]</tt>. To get the old behavior, you can
 
296
    # re-register XmlSimple as application/xml handler ike this:
 
297
    #
 
298
    #   ActionController::Base.param_parsers[Mime::XML] =
 
299
    #     Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' => false) }
 
300
    #
 
301
    # A YAML parser is also available and can be turned on with:
 
302
    #
 
303
    #   ActionController::Base.param_parsers[Mime::YAML] = :yaml
 
304
    @@param_parsers = {}
 
305
    cattr_accessor :param_parsers
 
306
 
 
307
    # Controls the default charset for all renders.
 
308
    @@default_charset = "utf-8"
 
309
    cattr_accessor :default_charset
 
310
 
 
311
    # The logger is used for generating information on the action run-time (including benchmarking) if available.
 
312
    # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
 
313
    cattr_accessor :logger
 
314
 
 
315
    # Controls the resource action separator
 
316
    @@resource_action_separator = "/"
 
317
    cattr_accessor :resource_action_separator
 
318
 
 
319
    # Allow to override path names for default resources' actions
 
320
    @@resources_path_names = { :new => 'new', :edit => 'edit' }
 
321
    cattr_accessor :resources_path_names
 
322
 
 
323
    # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
 
324
    # sets it to <tt>:authenticity_token</tt> by default.
 
325
    cattr_accessor :request_forgery_protection_token
 
326
 
 
327
    # Controls the IP Spoofing check when determining the remote IP.
 
328
    @@ip_spoofing_check = true
 
329
    cattr_accessor :ip_spoofing_check
 
330
 
 
331
    # Indicates whether or not optimise the generated named
 
332
    # route helper methods
 
333
    cattr_accessor :optimise_named_routes
 
334
    self.optimise_named_routes = true
 
335
 
 
336
    # Indicates whether the response format should be determined by examining the Accept HTTP header,
 
337
    # or by using the simpler params + ajax rules.
 
338
    #
 
339
    # If this is set to +true+ (the default) then +respond_to+ and +Request#format+ will take the Accept
 
340
    # header into account.  If it is set to false then the request format will be determined solely
 
341
    # by examining params[:format].  If params format is missing, the format will be either HTML or
 
342
    # Javascript depending on whether the request is an AJAX request.
 
343
    cattr_accessor :use_accept_header
 
344
    self.use_accept_header = true
 
345
 
 
346
    # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
 
347
    class_inheritable_accessor :allow_forgery_protection
 
348
    self.allow_forgery_protection = true
 
349
 
 
350
    # If you are deploying to a subdirectory, you will need to set
 
351
    # <tt>config.action_controller.relative_url_root</tt>
 
352
    # This defaults to ENV['RAILS_RELATIVE_URL_ROOT']
 
353
    cattr_accessor :relative_url_root
 
354
    self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
 
355
 
 
356
    # Holds the request object that's primarily used to get environment variables through access like
 
357
    # <tt>request.env["REQUEST_URI"]</tt>.
 
358
    attr_internal :request
 
359
 
 
360
    # Holds a hash of all the GET, POST, and Url parameters passed to the action. Accessed like <tt>params["post_id"]</tt>
 
361
    # to get the post_id. No type casts are made, so all values are returned as strings.
 
362
    attr_internal :params
 
363
 
 
364
    # Holds the response object that's primarily used to set additional HTTP headers through access like
 
365
    # <tt>response.headers["Cache-Control"] = "no-cache"</tt>. Can also be used to access the final body HTML after a template
 
366
    # has been rendered through response.body -- useful for <tt>after_filter</tt>s that wants to manipulate the output,
 
367
    # such as a OutputCompressionFilter.
 
368
    attr_internal :response
 
369
 
 
370
    # Holds a hash of objects in the session. Accessed like <tt>session[:person]</tt> to get the object tied to the "person"
 
371
    # key. The session will hold any type of object as values, but the key should be a string or symbol.
 
372
    attr_internal :session
 
373
 
 
374
    # Holds a hash of header names and values. Accessed like <tt>headers["Cache-Control"]</tt> to get the value of the Cache-Control
 
375
    # directive. Values should always be specified as strings.
 
376
    attr_internal :headers
 
377
 
 
378
    # Returns the name of the action this controller is processing.
 
379
    attr_accessor :action_name
 
380
 
 
381
    class << self
 
382
      def call(env)
 
383
        # HACK: For global rescue to have access to the original request and response
 
384
        request = env["action_controller.rescue.request"] ||= Request.new(env)
 
385
        response = env["action_controller.rescue.response"] ||= Response.new
 
386
        process(request, response)
 
387
      end
 
388
 
 
389
      # Factory for the standard create, process loop where the controller is discarded after processing.
 
390
      def process(request, response) #:nodoc:
 
391
        new.process(request, response)
 
392
      end
 
393
 
 
394
      # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
 
395
      def controller_class_name
 
396
        @controller_class_name ||= name.demodulize
 
397
      end
 
398
 
 
399
      # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat".
 
400
      def controller_name
 
401
        @controller_name ||= controller_class_name.sub(/Controller$/, '').underscore
 
402
      end
 
403
 
 
404
      # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
 
405
      def controller_path
 
406
        @controller_path ||= name.gsub(/Controller$/, '').underscore
 
407
      end
 
408
 
 
409
      # Return an array containing the names of public methods that have been marked hidden from the action processor.
 
410
      # By default, all methods defined in ActionController::Base and included modules are hidden.
 
411
      # More methods can be hidden using <tt>hide_actions</tt>.
 
412
      def hidden_actions
 
413
        read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, [])
 
414
      end
 
415
 
 
416
      # Hide each of the given methods from being callable as actions.
 
417
      def hide_action(*names)
 
418
        write_inheritable_attribute(:hidden_actions, hidden_actions | names.map { |name| name.to_s })
 
419
      end
 
420
 
 
421
      # View load paths determine the bases from which template references can be made. So a call to
 
422
      # render("test/template") will be looked up in the view load paths array and the closest match will be
 
423
      # returned.
 
424
      def view_paths
 
425
        if defined? @view_paths
 
426
          @view_paths
 
427
        else
 
428
          superclass.view_paths
 
429
        end
 
430
      end
 
431
 
 
432
      def view_paths=(value)
 
433
        @view_paths = ActionView::Base.process_view_paths(value) if value
 
434
      end
 
435
 
 
436
      # Adds a view_path to the front of the view_paths array.
 
437
      # If the current class has no view paths, copy them from
 
438
      # the superclass.  This change will be visible for all future requests.
 
439
      #
 
440
      #   ArticleController.prepend_view_path("views/default")
 
441
      #   ArticleController.prepend_view_path(["views/default", "views/custom"])
 
442
      #
 
443
      def prepend_view_path(path)
 
444
        @view_paths = superclass.view_paths.dup if !defined?(@view_paths) || @view_paths.nil?
 
445
        @view_paths.unshift(*path)
 
446
      end
 
447
 
 
448
      # Adds a view_path to the end of the view_paths array.
 
449
      # If the current class has no view paths, copy them from
 
450
      # the superclass. This change will be visible for all future requests.
 
451
      #
 
452
      #   ArticleController.append_view_path("views/default")
 
453
      #   ArticleController.append_view_path(["views/default", "views/custom"])
 
454
      #
 
455
      def append_view_path(path)
 
456
        @view_paths = superclass.view_paths.dup if @view_paths.nil?
 
457
        @view_paths.push(*path)
 
458
      end
 
459
 
 
460
      # Replace sensitive parameter data from the request log.
 
461
      # Filters parameters that have any of the arguments as a substring.
 
462
      # Looks in all subhashes of the param hash for keys to filter.
 
463
      # If a block is given, each key and value of the parameter hash and all
 
464
      # subhashes is passed to it, the value or key
 
465
      # can be replaced using String#replace or similar method.
 
466
      #
 
467
      # Examples:
 
468
      #   filter_parameter_logging
 
469
      #   => Does nothing, just slows the logging process down
 
470
      #
 
471
      #   filter_parameter_logging :password
 
472
      #   => replaces the value to all keys matching /password/i with "[FILTERED]"
 
473
      #
 
474
      #   filter_parameter_logging :foo, "bar"
 
475
      #   => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
 
476
      #
 
477
      #   filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i }
 
478
      #   => reverses the value to all keys matching /secret/i
 
479
      #
 
480
      #   filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i }
 
481
      #   => reverses the value to all keys matching /secret/i, and
 
482
      #      replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
 
483
      def filter_parameter_logging(*filter_words, &block)
 
484
        parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0
 
485
 
 
486
        define_method(:filter_parameters) do |unfiltered_parameters|
 
487
          filtered_parameters = {}
 
488
 
 
489
          unfiltered_parameters.each do |key, value|
 
490
            if key =~ parameter_filter
 
491
              filtered_parameters[key] = '[FILTERED]'
 
492
            elsif value.is_a?(Hash)
 
493
              filtered_parameters[key] = filter_parameters(value)
 
494
            elsif value.is_a?(Array)
 
495
              filtered_parameters[key] = value.collect do |item|
 
496
                case item
 
497
                when Hash, Array
 
498
                  filter_parameters(item)
 
499
                else
 
500
                  item
 
501
                end
 
502
              end
 
503
            elsif block_given?
 
504
              key = key.dup
 
505
              value = value.dup if value.duplicable?
 
506
              yield key, value
 
507
              filtered_parameters[key] = value
 
508
            else
 
509
              filtered_parameters[key] = value
 
510
            end
 
511
          end
 
512
 
 
513
          filtered_parameters
 
514
        end
 
515
        protected :filter_parameters
 
516
      end
 
517
 
 
518
      delegate :exempt_from_layout, :to => 'ActionView::Template'
 
519
    end
 
520
 
 
521
    public
 
522
      # Extracts the action_name from the request parameters and performs that action.
 
523
      def process(request, response, method = :perform_action, *arguments) #:nodoc:
 
524
        response.request = request
 
525
 
 
526
        initialize_template_class(response)
 
527
        assign_shortcuts(request, response)
 
528
        initialize_current_url
 
529
        assign_names
 
530
 
 
531
        log_processing
 
532
        send(method, *arguments)
 
533
 
 
534
        send_response
 
535
      ensure
 
536
        process_cleanup
 
537
      end
 
538
 
 
539
      def send_response
 
540
        response.prepare!
 
541
        response
 
542
      end
 
543
 
 
544
      # Returns a URL that has been rewritten according to the options hash and the defined routes.
 
545
      # (For doing a complete redirect, use +redirect_to+).
 
546
      #
 
547
      # <tt>url_for</tt> is used to:
 
548
      #
 
549
      # All keys given to +url_for+ are forwarded to the Route module, save for the following:
 
550
      # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path. For example,
 
551
      #   <tt>url_for :controller => 'posts', :action => 'show', :id => 10, :anchor => 'comments'</tt>
 
552
      #   will produce "/posts/show/10#comments".
 
553
      # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>false</tt> by default).
 
554
      # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
 
555
      #   is currently not recommended since it breaks caching.
 
556
      # * <tt>:host</tt> - Overrides the default (current) host if provided.
 
557
      # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
 
558
      # * <tt>:port</tt> - Optionally specify the port to connect to.
 
559
      # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
 
560
      # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
 
561
      # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the +relative_url_root+
 
562
      #   of the request so the path will include the web server relative installation directory.
 
563
      #
 
564
      # The URL is generated from the remaining keys in the hash. A URL contains two key parts: the <base> and a query string.
 
565
      # Routes composes a query string as the key/value pairs not included in the <base>.
 
566
      #
 
567
      # The default Routes setup supports a typical Rails path of "controller/action/id" where action and id are optional, with
 
568
      # action defaulting to 'index' when not given. Here are some typical url_for statements and their corresponding URLs:
 
569
      #
 
570
      #   url_for :controller => 'posts', :action => 'recent'                # => 'proto://host.com/posts/recent'
 
571
      #   url_for :controller => 'posts', :action => 'index'                 # => 'proto://host.com/posts'
 
572
      #   url_for :controller => 'posts', :action => 'index', :port=>'8033'  # => 'proto://host.com:8033/posts'
 
573
      #   url_for :controller => 'posts', :action => 'show', :id => 10       # => 'proto://host.com/posts/show/10'
 
574
      #   url_for :controller => 'posts', :user => 'd', :password => '123'   # => 'proto://d:123@host.com/posts'
 
575
      #
 
576
      # When generating a new URL, missing values may be filled in from the current request's parameters. For example,
 
577
      # <tt>url_for :action => 'some_action'</tt> will retain the current controller, as expected. This behavior extends to
 
578
      # other parameters, including <tt>:controller</tt>, <tt>:id</tt>, and any other parameters that are placed into a Route's
 
579
      # path.
 
580
      # Ā 
 
581
      # The URL helpers such as <tt>url_for</tt> have a limited form of memory: when generating a new URL, they can look for
 
582
      # missing values in the current request's parameters. Routes attempts to guess when a value should and should not be
 
583
      # taken from the defaults. There are a few simple rules on how this is performed:
 
584
      #
 
585
      # * If the controller name begins with a slash no defaults are used:
 
586
      #
 
587
      #     url_for :controller => '/home'
 
588
      #
 
589
      #   In particular, a leading slash ensures no namespace is assumed. Thus,
 
590
      #   while <tt>url_for :controller => 'users'</tt> may resolve to
 
591
      #   <tt>Admin::UsersController</tt> if the current controller lives under
 
592
      #   that module, <tt>url_for :controller => '/users'</tt> ensures you link
 
593
      #   to <tt>::UsersController</tt> no matter what.
 
594
      # * If the controller changes, the action will default to index unless provided
 
595
      #
 
596
      # The final rule is applied while the URL is being generated and is best illustrated by an example. Let us consider the
 
597
      # route given by <tt>map.connect 'people/:last/:first/:action', :action => 'bio', :controller => 'people'</tt>.
 
598
      #
 
599
      # Suppose that the current URL is "people/hh/david/contacts". Let's consider a few different cases of URLs which are generated
 
600
      # from this page.
 
601
      #
 
602
      # * <tt>url_for :action => 'bio'</tt> -- During the generation of this URL, default values will be used for the first and
 
603
      # last components, and the action shall change. The generated URL will be, "people/hh/david/bio".
 
604
      # * <tt>url_for :first => 'davids-little-brother'</tt> This generates the URL 'people/hh/davids-little-brother' -- note
 
605
      #   that this URL leaves out the assumed action of 'bio'.
 
606
      #
 
607
      # However, you might ask why the action from the current request, 'contacts', isn't carried over into the new URL. The
 
608
      # answer has to do with the order in which the parameters appear in the generated path. In a nutshell, since the
 
609
      # value that appears in the slot for <tt>:first</tt> is not equal to default value for <tt>:first</tt> we stop using
 
610
      # defaults. On its own, this rule can account for much of the typical Rails URL behavior.
 
611
      # Ā 
 
612
      # Although a convenience, defaults can occasionally get in your way. In some cases a default persists longer than desired.
 
613
      # The default may be cleared by adding <tt>:name => nil</tt> to <tt>url_for</tt>'s options.
 
614
      # This is often required when writing form helpers, since the defaults in play may vary greatly depending upon where the
 
615
      # helper is used from. The following line will redirect to PostController's default action, regardless of the page it is
 
616
      # displayed on:
 
617
      #
 
618
      #   url_for :controller => 'posts', :action => nil
 
619
      #
 
620
      # If you explicitly want to create a URL that's almost the same as the current URL, you can do so using the
 
621
      # <tt>:overwrite_params</tt> options. Say for your posts you have different views for showing and printing them.
 
622
      # Then, in the show view, you get the URL for the print view like this
 
623
      #
 
624
      #   url_for :overwrite_params => { :action => 'print' }
 
625
      #
 
626
      # This takes the current URL as is and only exchanges the action. In contrast, <tt>url_for :action => 'print'</tt>
 
627
      # would have slashed-off the path components after the changed action.
 
628
      def url_for(options = {})
 
629
        options ||= {}
 
630
        case options
 
631
          when String
 
632
            options
 
633
          when Hash
 
634
            @url.rewrite(rewrite_options(options))
 
635
          else
 
636
            polymorphic_url(options)
 
637
        end
 
638
      end
 
639
 
 
640
      # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
 
641
      def controller_class_name
 
642
        self.class.controller_class_name
 
643
      end
 
644
 
 
645
      # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat".
 
646
      def controller_name
 
647
        self.class.controller_name
 
648
      end
 
649
 
 
650
      # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
 
651
      def controller_path
 
652
        self.class.controller_path
 
653
      end
 
654
 
 
655
      def session_enabled?
 
656
        ActiveSupport::Deprecation.warn("Sessions are now lazy loaded. So if you don't access them, consider them disabled.", caller)
 
657
      end
 
658
 
 
659
      self.view_paths = []
 
660
 
 
661
      # View load paths for controller.
 
662
      def view_paths
 
663
        @template.view_paths
 
664
      end
 
665
 
 
666
      def view_paths=(value)
 
667
        @template.view_paths = ActionView::Base.process_view_paths(value)
 
668
      end
 
669
 
 
670
      # Adds a view_path to the front of the view_paths array.
 
671
      # This change affects the current request only.
 
672
      #
 
673
      #   self.prepend_view_path("views/default")
 
674
      #   self.prepend_view_path(["views/default", "views/custom"])
 
675
      #
 
676
      def prepend_view_path(path)
 
677
        @template.view_paths.unshift(*path)
 
678
      end
 
679
 
 
680
      # Adds a view_path to the end of the view_paths array.
 
681
      # This change affects the current request only.
 
682
      #
 
683
      #   self.append_view_path("views/default")
 
684
      #   self.append_view_path(["views/default", "views/custom"])
 
685
      #
 
686
      def append_view_path(path)
 
687
        @template.view_paths.push(*path)
 
688
      end
 
689
 
 
690
    protected
 
691
      # Renders the content that will be returned to the browser as the response body.
 
692
      #
 
693
      # === Rendering an action
 
694
      #
 
695
      # Action rendering is the most common form and the type used automatically by Action Controller when nothing else is
 
696
      # specified. By default, actions are rendered within the current layout (if one exists).
 
697
      #
 
698
      #   # Renders the template for the action "goal" within the current controller
 
699
      #   render :action => "goal"
 
700
      #
 
701
      #   # Renders the template for the action "short_goal" within the current controller,
 
702
      #   # but without the current active layout
 
703
      #   render :action => "short_goal", :layout => false
 
704
      #
 
705
      #   # Renders the template for the action "long_goal" within the current controller,
 
706
      #   # but with a custom layout
 
707
      #   render :action => "long_goal", :layout => "spectacular"
 
708
      #
 
709
      # === Rendering partials
 
710
      #
 
711
      # Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page
 
712
      # without reloading. Rendering of partials from the controller makes it possible to use the same partial template in
 
713
      # both the full-page rendering (by calling it from within the template) and when sub-page updates happen (from the
 
714
      # controller action responding to Ajax calls). By default, the current layout is not used.
 
715
      #
 
716
      #   # Renders the same partial with a local variable.
 
717
      #   render :partial => "person", :locals => { :name => "david" }
 
718
      #
 
719
      #   # Renders the partial, making @new_person available through
 
720
      #   # the local variable 'person'
 
721
      #   render :partial => "person", :object => @new_person
 
722
      #
 
723
      #   # Renders a collection of the same partial by making each element
 
724
      #   # of @winners available through the local variable "person" as it
 
725
      #   # builds the complete response.
 
726
      #   render :partial => "person", :collection => @winners
 
727
      #
 
728
      #   # Renders a collection of partials but with a custom local variable name
 
729
      #   render :partial => "admin_person", :collection => @winners, :as => :person
 
730
      #
 
731
      #   # Renders the same collection of partials, but also renders the
 
732
      #   # person_divider partial between each person partial.
 
733
      #   render :partial => "person", :collection => @winners, :spacer_template => "person_divider"
 
734
      #
 
735
      #   # Renders a collection of partials located in a view subfolder
 
736
      #   # outside of our current controller.  In this example we will be
 
737
      #   # rendering app/views/shared/_note.r(html|xml)  Inside the partial
 
738
      #   # each element of @new_notes is available as the local var "note".
 
739
      #   render :partial => "shared/note", :collection => @new_notes
 
740
      #
 
741
      #   # Renders the partial with a status code of 500 (internal error).
 
742
      #   render :partial => "broken", :status => 500
 
743
      #
 
744
      # Note that the partial filename must also be a valid Ruby variable name,
 
745
      # so e.g. 2005 and register-user are invalid.
 
746
      #
 
747
      #
 
748
      # == Automatic etagging
 
749
      #
 
750
      # Rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the
 
751
      # response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified
 
752
      # and the response body will be set to an empty string. No etag header will be inserted if it's already set.
 
753
      #
 
754
      # === Rendering a template
 
755
      #
 
756
      # Template rendering works just like action rendering except that it takes a path relative to the template root.
 
757
      # The current layout is automatically applied.
 
758
      #
 
759
      #   # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb)
 
760
      #   render :template => "weblog/show"
 
761
      #
 
762
      #   # Renders the template with a local variable
 
763
      #   render :template => "weblog/show", :locals => {:customer => Customer.new}
 
764
      #
 
765
      # === Rendering a file
 
766
      #
 
767
      # File rendering works just like action rendering except that it takes a filesystem path. By default, the path
 
768
      # is assumed to be absolute, and the current layout is not applied.
 
769
      #
 
770
      #   # Renders the template located at the absolute filesystem path
 
771
      #   render :file => "/path/to/some/template.erb"
 
772
      #   render :file => "c:/path/to/some/template.erb"
 
773
      #
 
774
      #   # Renders a template within the current layout, and with a 404 status code
 
775
      #   render :file => "/path/to/some/template.erb", :layout => true, :status => 404
 
776
      #   render :file => "c:/path/to/some/template.erb", :layout => true, :status => 404
 
777
      #
 
778
      # === Rendering text
 
779
      #
 
780
      # Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text
 
781
      # rendering is not done within the active layout.
 
782
      #
 
783
      #   # Renders the clear text "hello world" with status code 200
 
784
      #   render :text => "hello world!"
 
785
      #
 
786
      #   # Renders the clear text "Explosion!"  with status code 500
 
787
      #   render :text => "Explosion!", :status => 500
 
788
      #
 
789
      #   # Renders the clear text "Hi there!" within the current active layout (if one exists)
 
790
      #   render :text => "Hi there!", :layout => true
 
791
      #
 
792
      #   # Renders the clear text "Hi there!" within the layout
 
793
      #   # placed in "app/views/layouts/special.r(html|xml)"
 
794
      #   render :text => "Hi there!", :layout => "special"
 
795
      #
 
796
      # === Streaming data and/or controlling the page generation
 
797
      #
 
798
      # The <tt>:text</tt> option can also accept a Proc object, which can be used to:
 
799
      #
 
800
      # 1. stream on-the-fly generated data to the browser. Note that you should
 
801
      #    use the methods provided by ActionController::Steaming instead if you
 
802
      #    want to stream a buffer or a file.
 
803
      # 2. manually control the page generation. This should generally be avoided,
 
804
      #    as it violates the separation between code and content, and because almost
 
805
      #    everything that can be done with this method can also be done more cleanly
 
806
      #    using one of the other rendering methods, most notably templates.
 
807
      #
 
808
      # Two arguments are passed to the proc, a <tt>response</tt> object and an
 
809
      # <tt>output</tt> object. The response object is equivalent to the return
 
810
      # value of the ActionController::Base#response method, and can be used to
 
811
      # control various things in the HTTP response, such as setting the
 
812
      # Content-Type header. The output object is an writable <tt>IO</tt>-like
 
813
      # object, so one can call <tt>write</tt> and <tt>flush</tt> on it.
 
814
      #
 
815
      # The following example demonstrates how one can stream a large amount of
 
816
      # on-the-fly generated data to the browser:
 
817
      #
 
818
      #   # Streams about 180 MB of generated data to the browser.
 
819
      #   render :text => proc { |response, output|
 
820
      #     10_000_000.times do |i|
 
821
      #       output.write("This is line #{i}\n")
 
822
      #     end
 
823
      #   }
 
824
      #
 
825
      # Another example:
 
826
      #
 
827
      #   # Renders "Hello from code!"
 
828
      #   render :text => proc { |response, output| output.write("Hello from code!") }
 
829
      #
 
830
      # === Rendering XML
 
831
      #
 
832
      # Rendering XML sets the content type to application/xml.
 
833
      #
 
834
      #   # Renders '<name>David</name>'
 
835
      #   render :xml => {:name => "David"}.to_xml
 
836
      #
 
837
      # It's not necessary to call <tt>to_xml</tt> on the object you want to render, since <tt>render</tt> will
 
838
      # automatically do that for you:
 
839
      #
 
840
      #   # Also renders '<name>David</name>'
 
841
      #   render :xml => {:name => "David"}
 
842
      #
 
843
      # === Rendering JSON
 
844
      #
 
845
      # Rendering JSON sets the content type to application/json and optionally wraps the JSON in a callback. It is expected
 
846
      # that the response will be parsed (or eval'd) for use as a data structure.
 
847
      #
 
848
      #   # Renders '{"name": "David"}'
 
849
      #   render :json => {:name => "David"}.to_json
 
850
      #
 
851
      # It's not necessary to call <tt>to_json</tt> on the object you want to render, since <tt>render</tt> will
 
852
      # automatically do that for you:
 
853
      #
 
854
      #   # Also renders '{"name": "David"}'
 
855
      #   render :json => {:name => "David"}
 
856
      #
 
857
      # Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag),
 
858
      # so the <tt>:callback</tt> option is provided for these cases.
 
859
      #
 
860
      #   # Renders 'show({"name": "David"})'
 
861
      #   render :json => {:name => "David"}.to_json, :callback => 'show'
 
862
      #
 
863
      # === Rendering an inline template
 
864
      #
 
865
      # Rendering of an inline template works as a cross between text and action rendering where the source for the template
 
866
      # is supplied inline, like text, but its interpreted with ERb or Builder, like action. By default, ERb is used for rendering
 
867
      # and the current layout is not used.
 
868
      #
 
869
      #   # Renders "hello, hello, hello, again"
 
870
      #   render :inline => "<%= 'hello, ' * 3 + 'again' %>"
 
871
      #
 
872
      #   # Renders "<p>Good seeing you!</p>" using Builder
 
873
      #   render :inline => "xml.p { 'Good seeing you!' }", :type => :builder
 
874
      #
 
875
      #   # Renders "hello david"
 
876
      #   render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" }
 
877
      #
 
878
      # === Rendering inline JavaScriptGenerator page updates
 
879
      #
 
880
      # In addition to rendering JavaScriptGenerator page updates with Ajax in RJS templates (see ActionView::Base for details),
 
881
      # you can also pass the <tt>:update</tt> parameter to +render+, along with a block, to render page updates inline.
 
882
      #
 
883
      #   render :update do |page|
 
884
      #     page.replace_html  'user_list', :partial => 'user', :collection => @users
 
885
      #     page.visual_effect :highlight, 'user_list'
 
886
      #   end
 
887
      #
 
888
      # === Rendering vanilla JavaScript
 
889
      #
 
890
      # In addition to using RJS with render :update, you can also just render vanilla JavaScript with :js.
 
891
      #
 
892
      #   # Renders "alert('hello')" and sets the mime type to text/javascript
 
893
      #   render :js => "alert('hello')"
 
894
      #
 
895
      # === Rendering with status and location headers
 
896
      # All renders take the <tt>:status</tt> and <tt>:location</tt> options and turn them into headers. They can even be used together:
 
897
      #
 
898
      #   render :xml => post.to_xml, :status => :created, :location => post_url(post)
 
899
      def render(options = nil, extra_options = {}, &block) #:doc:
 
900
        raise DoubleRenderError, "Can only render or redirect once per action" if performed?
 
901
 
 
902
        validate_render_arguments(options, extra_options, block_given?)
 
903
 
 
904
        if options.nil?
 
905
          options = { :template => default_template, :layout => true }
 
906
        elsif options == :update
 
907
          options = extra_options.merge({ :update => true })
 
908
        elsif options.is_a?(String) || options.is_a?(Symbol)
 
909
          case options.to_s.index('/')
 
910
          when 0
 
911
            extra_options[:file] = options
 
912
          when nil
 
913
            extra_options[:action] = options
 
914
          else
 
915
            extra_options[:template] = options
 
916
          end
 
917
 
 
918
          options = extra_options
 
919
        elsif !options.is_a?(Hash)
 
920
          extra_options[:partial] = options
 
921
          options = extra_options
 
922
        end
 
923
 
 
924
        layout = pick_layout(options)
 
925
        response.layout = layout.path_without_format_and_extension if layout
 
926
        logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger && layout
 
927
 
 
928
        if content_type = options[:content_type]
 
929
          response.content_type = content_type.to_s
 
930
        end
 
931
 
 
932
        if location = options[:location]
 
933
          response.headers["Location"] = url_for(location)
 
934
        end
 
935
 
 
936
        if options.has_key?(:text)
 
937
          text = layout ? @template.render(options.merge(:text => options[:text], :layout => layout)) : options[:text]
 
938
          render_for_text(text, options[:status])
 
939
 
 
940
        else
 
941
          if file = options[:file]
 
942
            render_for_file(file, options[:status], layout, options[:locals] || {})
 
943
 
 
944
          elsif template = options[:template]
 
945
            render_for_file(template, options[:status], layout, options[:locals] || {})
 
946
 
 
947
          elsif inline = options[:inline]
 
948
            render_for_text(@template.render(options.merge(:layout => layout)), options[:status])
 
949
 
 
950
          elsif action_name = options[:action]
 
951
            render_for_file(default_template(action_name.to_s), options[:status], layout)
 
952
 
 
953
          elsif xml = options[:xml]
 
954
            response.content_type ||= Mime::XML
 
955
            render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml, options[:status])
 
956
 
 
957
          elsif js = options[:js]
 
958
            response.content_type ||= Mime::JS
 
959
            render_for_text(js, options[:status])
 
960
 
 
961
          elsif options.include?(:json)
 
962
            json = options[:json]
 
963
            json = ActiveSupport::JSON.encode(json) unless json.is_a?(String)
 
964
            json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
 
965
            response.content_type ||= Mime::JSON
 
966
            render_for_text(json, options[:status])
 
967
 
 
968
          elsif options[:partial]
 
969
            options[:partial] = default_template_name if options[:partial] == true
 
970
            if layout
 
971
              render_for_text(@template.render(:text => @template.render(options), :layout => layout), options[:status])
 
972
            else
 
973
              render_for_text(@template.render(options), options[:status])
 
974
            end
 
975
 
 
976
          elsif options[:update]
 
977
            @template.send(:_evaluate_assigns_and_ivars)
 
978
 
 
979
            generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
 
980
            response.content_type = Mime::JS
 
981
            render_for_text(generator.to_s, options[:status])
 
982
 
 
983
          elsif options[:nothing]
 
984
            render_for_text(nil, options[:status])
 
985
 
 
986
          else
 
987
            render_for_file(default_template, options[:status], layout)
 
988
          end
 
989
        end
 
990
      end
 
991
 
 
992
      # Renders according to the same rules as <tt>render</tt>, but returns the result in a string instead
 
993
      # of sending it as the response body to the browser.
 
994
      def render_to_string(options = nil, &block) #:doc:
 
995
        render(options, &block)
 
996
      ensure
 
997
        response.content_type = nil
 
998
        erase_render_results
 
999
        reset_variables_added_to_assigns
 
1000
      end
 
1001
 
 
1002
      # Return a response that has no content (merely headers). The options
 
1003
      # argument is interpreted to be a hash of header names and values.
 
1004
      # This allows you to easily return a response that consists only of
 
1005
      # significant headers:
 
1006
      #
 
1007
      #   head :created, :location => person_path(@person)
 
1008
      #
 
1009
      # It can also be used to return exceptional conditions:
 
1010
      #
 
1011
      #   return head(:method_not_allowed) unless request.post?
 
1012
      #   return head(:bad_request) unless valid_request?
 
1013
      #   render
 
1014
      def head(*args)
 
1015
        if args.length > 2
 
1016
          raise ArgumentError, "too many arguments to head"
 
1017
        elsif args.empty?
 
1018
          raise ArgumentError, "too few arguments to head"
 
1019
        end
 
1020
        options = args.extract_options!
 
1021
        status = interpret_status(args.shift || options.delete(:status) || :ok)
 
1022
 
 
1023
        options.each do |key, value|
 
1024
          headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
 
1025
        end
 
1026
 
 
1027
        render :nothing => true, :status => status
 
1028
      end
 
1029
 
 
1030
      # Clears the rendered results, allowing for another render to be performed.
 
1031
      def erase_render_results #:nodoc:
 
1032
        response.body = nil
 
1033
        @performed_render = false
 
1034
      end
 
1035
 
 
1036
      # Clears the redirected results from the headers, resets the status to 200 and returns
 
1037
      # the URL that was used to redirect or nil if there was no redirected URL
 
1038
      # Note that +redirect_to+ will change the body of the response to indicate a redirection.
 
1039
      # The response body is not reset here, see +erase_render_results+
 
1040
      def erase_redirect_results #:nodoc:
 
1041
        @performed_redirect = false
 
1042
        response.redirected_to = nil
 
1043
        response.redirected_to_method_params = nil
 
1044
        response.status = DEFAULT_RENDER_STATUS_CODE
 
1045
        response.headers.delete('Location')
 
1046
      end
 
1047
 
 
1048
      # Erase both render and redirect results
 
1049
      def erase_results #:nodoc:
 
1050
        erase_render_results
 
1051
        erase_redirect_results
 
1052
      end
 
1053
 
 
1054
      def rewrite_options(options) #:nodoc:
 
1055
        if defaults = default_url_options(options)
 
1056
          defaults.merge(options)
 
1057
        else
 
1058
          options
 
1059
        end
 
1060
      end
 
1061
 
 
1062
      # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
 
1063
      # the form of a hash, just like the one you would use for url_for directly. Example:
 
1064
      #
 
1065
      #   def default_url_options(options)
 
1066
      #     { :project => @project.active? ? @project.url_name : "unknown" }
 
1067
      #   end
 
1068
      #
 
1069
      # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the
 
1070
      # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set
 
1071
      # by this method.
 
1072
      def default_url_options(options = nil)
 
1073
      end
 
1074
 
 
1075
      # Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
 
1076
      #
 
1077
      # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
 
1078
      # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
 
1079
      # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
 
1080
      # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
 
1081
      # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
 
1082
      #   Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
 
1083
      #
 
1084
      # Examples:
 
1085
      #   redirect_to :action => "show", :id => 5
 
1086
      #   redirect_to post
 
1087
      #   redirect_to "http://www.rubyonrails.org"
 
1088
      #   redirect_to "/images/screenshot.jpg"
 
1089
      #   redirect_to articles_url
 
1090
      #   redirect_to :back
 
1091
      #
 
1092
      # The redirection happens as a "302 Moved" header unless otherwise specified.
 
1093
      #
 
1094
      # Examples:
 
1095
      #   redirect_to post_url(@post), :status=>:found
 
1096
      #   redirect_to :action=>'atom', :status=>:moved_permanently
 
1097
      #   redirect_to post_url(@post), :status=>301
 
1098
      #   redirect_to :action=>'atom', :status=>302
 
1099
      #
 
1100
      # When using <tt>redirect_to :back</tt>, if there is no referrer,
 
1101
      # RedirectBackError will be raised. You may specify some fallback
 
1102
      # behavior for this case by rescuing RedirectBackError.
 
1103
      def redirect_to(options = {}, response_status = {}) #:doc:
 
1104
        raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
 
1105
 
 
1106
        if options.is_a?(Hash) && options[:status]
 
1107
          status = options.delete(:status)
 
1108
        elsif response_status[:status]
 
1109
          status = response_status[:status]
 
1110
        else
 
1111
          status = 302
 
1112
        end
 
1113
 
 
1114
        response.redirected_to = options
 
1115
 
 
1116
        case options
 
1117
          # The scheme name consist of a letter followed by any combination of
 
1118
          # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
 
1119
          # characters; and is terminated by a colon (":").
 
1120
          when %r{^\w[\w\d+.-]*:.*}
 
1121
            redirect_to_full_url(options, status)
 
1122
          when String
 
1123
            redirect_to_full_url(request.protocol + request.host_with_port + options, status)
 
1124
          when :back
 
1125
            if referer = request.headers["Referer"]
 
1126
              redirect_to(referer, :status=>status)
 
1127
            else
 
1128
              raise RedirectBackError
 
1129
            end
 
1130
          else
 
1131
            redirect_to_full_url(url_for(options), status)
 
1132
        end
 
1133
      end
 
1134
 
 
1135
      def redirect_to_full_url(url, status)
 
1136
        raise DoubleRenderError if performed?
 
1137
        logger.info("Redirected to #{url}") if logger && logger.info?
 
1138
        response.redirect(url, interpret_status(status))
 
1139
        @performed_redirect = true
 
1140
      end
 
1141
 
 
1142
      # Sets the etag and/or last_modified on the response and checks it against
 
1143
      # the client request. If the request doesn't match the options provided, the
 
1144
      # request is considered stale and should be generated from scratch. Otherwise,
 
1145
      # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent.
 
1146
      #
 
1147
      # Parameters:
 
1148
      # * <tt>:etag</tt>
 
1149
      # * <tt>:last_modified</tt> 
 
1150
      # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
 
1151
      #
 
1152
      # Example:
 
1153
      #
 
1154
      #   def show
 
1155
      #     @article = Article.find(params[:id])
 
1156
      #
 
1157
      #     if stale?(:etag => @article, :last_modified => @article.created_at.utc)
 
1158
      #       @statistics = @article.really_expensive_call
 
1159
      #       respond_to do |format|
 
1160
      #         # all the supported formats
 
1161
      #       end
 
1162
      #     end
 
1163
      #   end
 
1164
      def stale?(options)
 
1165
        fresh_when(options)
 
1166
        !request.fresh?(response)
 
1167
      end
 
1168
 
 
1169
      # Sets the etag, last_modified, or both on the response and renders a
 
1170
      # "304 Not Modified" response if the request is already fresh.
 
1171
      #
 
1172
      # Parameters:
 
1173
      # * <tt>:etag</tt>
 
1174
      # * <tt>:last_modified</tt> 
 
1175
      # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
 
1176
      #
 
1177
      # Example:
 
1178
      #
 
1179
      #   def show
 
1180
      #     @article = Article.find(params[:id])
 
1181
      #     fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true)
 
1182
      #   end
 
1183
      #
 
1184
      # This will render the show template if the request isn't sending a matching etag or
 
1185
      # If-Modified-Since header and just a "304 Not Modified" response if there's a match.
 
1186
      #
 
1187
      def fresh_when(options)
 
1188
        options.assert_valid_keys(:etag, :last_modified, :public)
 
1189
 
 
1190
        response.etag          = options[:etag]          if options[:etag]
 
1191
        response.last_modified = options[:last_modified] if options[:last_modified]
 
1192
        
 
1193
        if options[:public] 
 
1194
          cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip }
 
1195
          cache_control.delete("private")
 
1196
          cache_control.delete("no-cache")
 
1197
          cache_control << "public"
 
1198
          response.headers["Cache-Control"] = cache_control.join(', ')
 
1199
        end
 
1200
 
 
1201
        if request.fresh?(response)
 
1202
          head :not_modified
 
1203
        end
 
1204
      end
 
1205
 
 
1206
      # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that
 
1207
      # intermediate caches shouldn't cache the response.
 
1208
      #
 
1209
      # Examples:
 
1210
      #   expires_in 20.minutes
 
1211
      #   expires_in 3.hours, :public => true
 
1212
      #   expires in 3.hours, 'max-stale' => 5.hours, :public => true
 
1213
      #
 
1214
      # This method will overwrite an existing Cache-Control header.
 
1215
      # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
 
1216
      def expires_in(seconds, options = {}) #:doc:
 
1217
        cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip }
 
1218
 
 
1219
        cache_control << "max-age=#{seconds}"
 
1220
        cache_control.delete("no-cache")
 
1221
        if options[:public]
 
1222
          cache_control.delete("private")
 
1223
          cache_control << "public"
 
1224
        else
 
1225
          cache_control << "private"
 
1226
        end
 
1227
        
 
1228
        # This allows for additional headers to be passed through like 'max-stale' => 5.hours
 
1229
        cache_control += options.symbolize_keys.reject{|k,v| k == :public || k == :private }.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"}
 
1230
        
 
1231
        response.headers["Cache-Control"] = cache_control.join(', ')
 
1232
      end
 
1233
 
 
1234
      # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or
 
1235
      # intermediate caches (like caching proxy servers).
 
1236
      def expires_now #:doc:
 
1237
        response.headers["Cache-Control"] = "no-cache"
 
1238
      end
 
1239
 
 
1240
      # Resets the session by clearing out all the objects stored within and initializing a new session object.
 
1241
      def reset_session #:doc:
 
1242
        request.reset_session
 
1243
        @_session = request.session
 
1244
      end
 
1245
 
 
1246
    private
 
1247
      def render_for_file(template_path, status = nil, layout = nil, locals = {}) #:nodoc:
 
1248
        path = template_path.respond_to?(:path_without_format_and_extension) ? template_path.path_without_format_and_extension : template_path
 
1249
        logger.info("Rendering #{path}" + (status ? " (#{status})" : '')) if logger
 
1250
        render_for_text @template.render(:file => template_path, :locals => locals, :layout => layout), status
 
1251
      end
 
1252
 
 
1253
      def render_for_text(text = nil, status = nil, append_response = false) #:nodoc:
 
1254
        @performed_render = true
 
1255
 
 
1256
        response.status = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
 
1257
 
 
1258
        if append_response
 
1259
          response.body ||= ''
 
1260
          response.body << text.to_s
 
1261
        else
 
1262
          response.body = case text
 
1263
            when Proc then text
 
1264
            when nil  then " " # Safari doesn't pass the headers of the return if the response is zero length
 
1265
            else           text.to_s
 
1266
          end
 
1267
        end
 
1268
      end
 
1269
 
 
1270
      def validate_render_arguments(options, extra_options, has_block)
 
1271
        if options && (has_block && options != :update) && !options.is_a?(String) && !options.is_a?(Hash) && !options.is_a?(Symbol)
 
1272
          raise RenderError, "You called render with invalid options : #{options.inspect}"
 
1273
        end
 
1274
 
 
1275
        if !extra_options.is_a?(Hash)
 
1276
          raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}"
 
1277
        end
 
1278
      end
 
1279
 
 
1280
      def initialize_template_class(response)
 
1281
        response.template = ActionView::Base.new(self.class.view_paths, {}, self)
 
1282
        response.template.helpers.send :include, self.class.master_helper_module
 
1283
        response.redirected_to = nil
 
1284
        @performed_render = @performed_redirect = false
 
1285
      end
 
1286
 
 
1287
      def assign_shortcuts(request, response)
 
1288
        @_request, @_params = request, request.parameters
 
1289
 
 
1290
        @_response         = response
 
1291
        @_response.session = request.session
 
1292
 
 
1293
        @_session = @_response.session
 
1294
        @template = @_response.template
 
1295
 
 
1296
        @_headers = @_response.headers
 
1297
      end
 
1298
 
 
1299
      def initialize_current_url
 
1300
        @url = UrlRewriter.new(request, params.clone)
 
1301
      end
 
1302
 
 
1303
      def log_processing
 
1304
        if logger && logger.info?
 
1305
          log_processing_for_request_id
 
1306
          log_processing_for_parameters
 
1307
        end
 
1308
      end
 
1309
 
 
1310
      def log_processing_for_request_id
 
1311
        request_id = "\n\nProcessing #{self.class.name}\##{action_name} "
 
1312
        request_id << "to #{params[:format]} " if params[:format]
 
1313
        request_id << "(for #{request_origin}) [#{request.method.to_s.upcase}]"
 
1314
 
 
1315
        logger.info(request_id)
 
1316
      end
 
1317
 
 
1318
      def log_processing_for_parameters
 
1319
        parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
 
1320
        parameters = parameters.except!(:controller, :action, :format, :_method)
 
1321
 
 
1322
        logger.info "  Parameters: #{parameters.inspect}" unless parameters.empty?
 
1323
      end
 
1324
 
 
1325
      def default_render #:nodoc:
 
1326
        render
 
1327
      end
 
1328
 
 
1329
      def perform_action
 
1330
        if action_methods.include?(action_name)
 
1331
          send(action_name)
 
1332
          default_render unless performed?
 
1333
        elsif respond_to? :method_missing
 
1334
          method_missing action_name
 
1335
          default_render unless performed?
 
1336
        else
 
1337
          begin
 
1338
            default_render
 
1339
          rescue ActionView::MissingTemplate => e
 
1340
            # Was the implicit template missing, or was it another template?
 
1341
            if e.path == default_template_name
 
1342
              raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence(:locale => :en)}", caller
 
1343
            else
 
1344
              raise e
 
1345
            end
 
1346
          end
 
1347
        end
 
1348
      end
 
1349
 
 
1350
      def performed?
 
1351
        @performed_render || @performed_redirect
 
1352
      end
 
1353
 
 
1354
      def assign_names
 
1355
        @action_name = (params['action'] || 'index')
 
1356
      end
 
1357
 
 
1358
      def action_methods
 
1359
        self.class.action_methods
 
1360
      end
 
1361
 
 
1362
      def self.action_methods
 
1363
        @action_methods ||=
 
1364
          # All public instance methods of this class, including ancestors
 
1365
          public_instance_methods(true).map { |m| m.to_s }.to_set -
 
1366
          # Except for public instance methods of Base and its ancestors
 
1367
          Base.public_instance_methods(true).map { |m| m.to_s } +
 
1368
          # Be sure to include shadowed public instance methods of this class
 
1369
          public_instance_methods(false).map { |m| m.to_s } -
 
1370
          # And always exclude explicitly hidden actions
 
1371
          hidden_actions
 
1372
      end
 
1373
 
 
1374
      def reset_variables_added_to_assigns
 
1375
        @template.instance_variable_set("@assigns_added", nil)
 
1376
      end
 
1377
 
 
1378
      def request_origin
 
1379
        # this *needs* to be cached!
 
1380
        # otherwise you'd get different results if calling it more than once
 
1381
        @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}"
 
1382
      end
 
1383
 
 
1384
      def complete_request_uri
 
1385
        "#{request.protocol}#{request.host}#{request.request_uri}"
 
1386
      end
 
1387
 
 
1388
      def default_template(action_name = self.action_name)
 
1389
        self.view_paths.find_template(default_template_name(action_name), default_template_format)
 
1390
      end
 
1391
 
 
1392
      def default_template_name(action_name = self.action_name)
 
1393
        if action_name
 
1394
          action_name = action_name.to_s
 
1395
          if action_name.include?('/') && template_path_includes_controller?(action_name)
 
1396
            action_name = strip_out_controller(action_name)
 
1397
          end
 
1398
        end
 
1399
        "#{self.controller_path}/#{action_name}"
 
1400
      end
 
1401
 
 
1402
      def strip_out_controller(path)
 
1403
        path.split('/', 2).last
 
1404
      end
 
1405
 
 
1406
      def template_path_includes_controller?(path)
 
1407
        self.controller_path.split('/')[-1] == path.split('/')[0]
 
1408
      end
 
1409
 
 
1410
      def process_cleanup
 
1411
      end
 
1412
  end
 
1413
 
 
1414
  Base.class_eval do
 
1415
    [ Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers,
 
1416
      Cookies, Caching, Verification, Streaming, SessionManagement,
 
1417
      HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods,
 
1418
      RecordIdentifier, RequestForgeryProtection, Translation
 
1419
    ].each do |mod|
 
1420
      include mod
 
1421
    end
 
1422
  end
 
1423
end