actionpack 1.8.1 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (101) hide show
  1. data/CHANGELOG +309 -16
  2. data/README +1 -1
  3. data/lib/action_controller.rb +5 -0
  4. data/lib/action_controller/assertions.rb +57 -12
  5. data/lib/action_controller/auto_complete.rb +47 -0
  6. data/lib/action_controller/base.rb +288 -258
  7. data/lib/action_controller/benchmarking.rb +8 -3
  8. data/lib/action_controller/caching.rb +88 -42
  9. data/lib/action_controller/cgi_ext/cgi_ext.rb +1 -1
  10. data/lib/action_controller/cgi_ext/cgi_methods.rb +41 -11
  11. data/lib/action_controller/cgi_ext/multipart_progress.rb +169 -0
  12. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +30 -12
  13. data/lib/action_controller/cgi_process.rb +39 -11
  14. data/lib/action_controller/code_generation.rb +235 -0
  15. data/lib/action_controller/cookies.rb +14 -8
  16. data/lib/action_controller/deprecated_renders_and_redirects.rb +76 -0
  17. data/lib/action_controller/filters.rb +8 -7
  18. data/lib/action_controller/helpers.rb +41 -6
  19. data/lib/action_controller/layout.rb +45 -16
  20. data/lib/action_controller/request.rb +86 -23
  21. data/lib/action_controller/rescue.rb +1 -0
  22. data/lib/action_controller/response.rb +1 -1
  23. data/lib/action_controller/routing.rb +536 -272
  24. data/lib/action_controller/scaffolding.rb +30 -25
  25. data/lib/action_controller/session/active_record_store.rb +251 -50
  26. data/lib/action_controller/streaming.rb +133 -0
  27. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -7
  28. data/lib/action_controller/templates/scaffolds/edit.rhtml +2 -2
  29. data/lib/action_controller/templates/scaffolds/layout.rhtml +22 -18
  30. data/lib/action_controller/templates/scaffolds/list.rhtml +3 -3
  31. data/lib/action_controller/templates/scaffolds/new.rhtml +2 -2
  32. data/lib/action_controller/templates/scaffolds/show.rhtml +1 -1
  33. data/lib/action_controller/test_process.rb +68 -47
  34. data/lib/action_controller/upload_progress.rb +421 -0
  35. data/lib/action_controller/url_rewriter.rb +8 -11
  36. data/lib/action_controller/vendor/html-scanner/html/document.rb +6 -5
  37. data/lib/action_controller/vendor/html-scanner/html/node.rb +70 -14
  38. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +17 -10
  39. data/lib/action_controller/vendor/html-scanner/html/version.rb +3 -3
  40. data/lib/action_controller/vendor/xml_simple.rb +1019 -0
  41. data/lib/action_controller/verification.rb +36 -30
  42. data/lib/action_view/base.rb +21 -14
  43. data/lib/action_view/helpers/active_record_helper.rb +15 -13
  44. data/lib/action_view/helpers/asset_tag_helper.rb +26 -9
  45. data/lib/action_view/helpers/benchmark_helper.rb +24 -0
  46. data/lib/action_view/helpers/capture_helper.rb +7 -5
  47. data/lib/action_view/helpers/date_helper.rb +63 -46
  48. data/lib/action_view/helpers/form_helper.rb +7 -1
  49. data/lib/action_view/helpers/form_options_helper.rb +19 -11
  50. data/lib/action_view/helpers/form_tag_helper.rb +5 -1
  51. data/lib/action_view/helpers/javascript_helper.rb +403 -35
  52. data/lib/action_view/helpers/javascripts/controls.js +261 -0
  53. data/lib/action_view/helpers/javascripts/dragdrop.js +476 -0
  54. data/lib/action_view/helpers/javascripts/effects.js +570 -0
  55. data/lib/action_view/helpers/javascripts/prototype.js +633 -371
  56. data/lib/action_view/helpers/number_helper.rb +11 -13
  57. data/lib/action_view/helpers/tag_helper.rb +1 -2
  58. data/lib/action_view/helpers/text_helper.rb +69 -6
  59. data/lib/action_view/helpers/upload_progress_helper.rb +433 -0
  60. data/lib/action_view/helpers/url_helper.rb +98 -3
  61. data/lib/action_view/partials.rb +14 -8
  62. data/lib/action_view/vendor/builder/xmlmarkup.rb +11 -0
  63. data/rakefile +13 -5
  64. data/test/abstract_unit.rb +1 -1
  65. data/test/controller/action_pack_assertions_test.rb +52 -9
  66. data/test/controller/active_record_assertions_test.rb +119 -120
  67. data/test/controller/active_record_store_test.rb +111 -0
  68. data/test/controller/addresses_render_test.rb +45 -0
  69. data/test/controller/caching_filestore.rb +92 -0
  70. data/test/controller/capture_test.rb +39 -0
  71. data/test/controller/cgi_test.rb +40 -3
  72. data/test/controller/helper_test.rb +65 -13
  73. data/test/controller/multipart_progress_testx.rb +365 -0
  74. data/test/controller/new_render_test.rb +263 -0
  75. data/test/controller/redirect_test.rb +64 -0
  76. data/test/controller/render_test.rb +20 -21
  77. data/test/controller/request_test.rb +83 -3
  78. data/test/controller/routing_test.rb +702 -0
  79. data/test/controller/send_file_test.rb +2 -0
  80. data/test/controller/test_test.rb +44 -8
  81. data/test/controller/upload_progress_testx.rb +89 -0
  82. data/test/controller/verification_test.rb +94 -29
  83. data/test/fixtures/addresses/list.rhtml +1 -0
  84. data/test/fixtures/test/capturing.rhtml +4 -0
  85. data/test/fixtures/test/list.rhtml +1 -1
  86. data/test/fixtures/test/update_element_with_capture.rhtml +9 -0
  87. data/test/template/active_record_helper_test.rb +30 -15
  88. data/test/template/asset_tag_helper_test.rb +12 -5
  89. data/test/template/benchmark_helper_test.rb +72 -0
  90. data/test/template/date_helper_test.rb +69 -0
  91. data/test/template/form_helper_test.rb +18 -10
  92. data/test/template/form_options_helper_test.rb +40 -5
  93. data/test/template/javascript_helper.rb +149 -2
  94. data/test/template/number_helper_test.rb +2 -0
  95. data/test/template/tag_helper_test.rb +4 -0
  96. data/test/template/text_helper_test.rb +36 -0
  97. data/test/template/upload_progress_helper_testx.rb +272 -0
  98. data/test/template/url_helper_test.rb +30 -0
  99. metadata +30 -6
  100. data/test/controller/layout_test.rb +0 -49
  101. data/test/controller/routing_tests.rb +0 -543
@@ -0,0 +1,47 @@
1
+ module ActionController
2
+ module AutoComplete #:nodoc:
3
+ def self.append_features(base) #:nodoc:
4
+ super
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ # Example:
9
+ #
10
+ # # Controller
11
+ # class BlogController < ApplicationController
12
+ # auto_complete_for :post, :title
13
+ # end
14
+ #
15
+ # # View
16
+ # <%= text_field_with_auto_complete :post, title %>
17
+ #
18
+ # By default, auto_complete_for limits the results to 10 entries,
19
+ # and sorts by the given field.
20
+ #
21
+ # auto_complete_for takes a third parameter, an options hash to
22
+ # the find method used to search for the records:
23
+ #
24
+ # auto_complete_for :post, :title, :limit => 15, :order => 'created_at DESC'
25
+ #
26
+ # For help on defining text input fields with autocompletion,
27
+ # see ActionView::Helpers::JavascriptHelper.
28
+ #
29
+ # For more examples, see script.aculo.us:
30
+ # * http://script.aculo.us/demos/ajax/autocompleter
31
+ # * http://script.aculo.us/demos/ajax/autocompleter_customized
32
+ module ClassMethods
33
+ def auto_complete_for(object, method, options = {})
34
+ define_method("auto_complete_for_#{object}_#{method}") do
35
+ find_options = {
36
+ :conditions => [ "LOWER(#{method}) LIKE ?", '%' + params[object][method] + '%' ],
37
+ :order => "#{method} ASC",
38
+ :limit => 10 }.merge!(options)
39
+
40
+ @items = object.to_s.camelize.constantize.find(:all, find_options)
41
+
42
+ render :inline => "<%= auto_complete_result @items, '#{method}' %>"
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,6 +1,7 @@
1
1
  require 'action_controller/request'
2
2
  require 'action_controller/response'
3
3
  require 'action_controller/routing'
4
+ require 'action_controller/code_generation'
4
5
  require 'action_controller/url_rewriter'
5
6
  require 'drb'
6
7
 
@@ -11,7 +12,7 @@ module ActionController #:nodoc:
11
12
  end
12
13
  class MissingTemplate < ActionControllerError #:nodoc:
13
14
  end
14
- class RoutingError < ActionControllerError#:nodoc:
15
+ class RoutingError < ActionControllerError #:nodoc:
15
16
  attr_reader :failures
16
17
  def initialize(message, failures=[])
17
18
  super(message)
@@ -24,6 +25,8 @@ module ActionController #:nodoc:
24
25
  end
25
26
  class MissingFile < ActionControllerError #:nodoc:
26
27
  end
28
+ class DoubleRenderError < ActionControllerError #:nodoc:
29
+ end
27
30
 
28
31
  # Action Controllers are made up of one or more actions that performs its purpose and then either renders a template or
29
32
  # redirects to another action. An action is defined as a public method on the controller, which will automatically be
@@ -57,6 +60,9 @@ module ActionController #:nodoc:
57
60
  # Also note that it's the final call to <tt>process_cgi</tt> that actually initiates the action performance. It will extract
58
61
  # request and response objects from the CGI
59
62
  #
63
+ # When Action Pack is used inside of Rails, the template_root is automatically configured and you don't need to call process_cgi
64
+ # yourself.
65
+ #
60
66
  # == Requests
61
67
  #
62
68
  # Requests are processed by the Action Controller framework by extracting the value of the "action" key in the request parameters.
@@ -64,19 +70,19 @@ module ActionController #:nodoc:
64
70
  # request parameters, the session (if one is available), and the full request with all the http headers are made available to
65
71
  # the action through instance variables. Then the action is performed.
66
72
  #
67
- # The full request object is available in @request and is primarily used to query for http headers. These queries are made by
68
- # accessing the environment hash, like this:
73
+ # The full request object is available with the request accessor and is primarily used to query for http headers. These queries
74
+ # are made by accessing the environment hash, like this:
69
75
  #
70
76
  # def hello_ip
71
- # location = @request.env["REMOTE_IP"]
77
+ # location = request.env["REMOTE_IP"]
72
78
  # render_text "Hello stranger from #{location}"
73
79
  # end
74
80
  #
75
81
  # == Parameters
76
82
  #
77
- # All request parameters whether they come from a GET or POST request, or from the URL, are available through the @params hash.
83
+ # All request parameters whether they come from a GET or POST request, or from the URL, are available through the params hash.
78
84
  # So an action that was performed through /weblog/list?category=All&limit=5 will include { "category" => "All", "limit" => 5 }
79
- # in @params.
85
+ # in params.
80
86
  #
81
87
  # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
82
88
  #
@@ -84,7 +90,7 @@ module ActionController #:nodoc:
84
90
  # <input type="text" name="post[address]" value="hyacintvej">
85
91
  #
86
92
  # A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
87
- # If the address input had been named "post[address][street]", the @params would have included
93
+ # If the address input had been named "post[address][street]", the params would have included
88
94
  # <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
89
95
  #
90
96
  # == Sessions
@@ -94,24 +100,17 @@ module ActionController #:nodoc:
94
100
  # 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
95
101
  # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
96
102
  #
97
- # You can place objects in the session by using the <tt>@session</tt> hash:
103
+ # You can place objects in the session by using the <tt>session</tt> hash accessor:
98
104
  #
99
- # @session[:person] = Person.authenticate(user_name, password)
105
+ # session[:person] = Person.authenticate(user_name, password)
100
106
  #
101
107
  # And retrieved again through the same hash:
102
108
  #
103
- # Hello #{@session[:person]}
109
+ # Hello #{session[:person]}
104
110
  #
105
111
  # Any object can be placed in the session (as long as it can be Marshalled). But remember that 1000 active sessions each storing a
106
112
  # 50kb object could lead to a 50MB memory overhead. In other words, think carefully about size and caching before resorting to the use
107
113
  # of the session.
108
- #
109
- # If you store a model in the session, you must also include a line like:
110
- #
111
- # model :person
112
- #
113
- # For that particular controller. In Rails, you can also just add it in your app/controller/application.rb file (so the model is available
114
- # for all controllers). This lets Action Pack know to have the model definition loaded before retrieving the object from the session.
115
114
  #
116
115
  # For removing objects from the session, you can either assign a single key to nil, like <tt>@session[:person] = nil</tt>, or you can
117
116
  # remove the entire session with reset_session.
@@ -128,7 +127,7 @@ module ActionController #:nodoc:
128
127
  # The controller passes objects to the view by assigning instance variables:
129
128
  #
130
129
  # def show
131
- # @post = Post.find(@params["id"])
130
+ # @post = Post.find(params[:id])
132
131
  # end
133
132
  #
134
133
  # Which are then automatically available to the view:
@@ -139,11 +138,11 @@ module ActionController #:nodoc:
139
138
  # the manual rendering methods:
140
139
  #
141
140
  # def search
142
- # @results = Search.find(@params["query"])
141
+ # @results = Search.find(params[:query])
143
142
  # case @results
144
- # when 0 then render "weblog/no_results"
145
- # when 1 then render_action "show"
146
- # when 2..10 then render_action "show_many"
143
+ # when 0 then render :action=> "no_results"
144
+ # when 1 then render :action=> "show"
145
+ # when 2..10 then render :action=> "show_many"
147
146
  # end
148
147
  # end
149
148
  #
@@ -184,7 +183,7 @@ module ActionController #:nodoc:
184
183
  #
185
184
  # def do_something
186
185
  # redirect_to :action => "elsewhere"
187
- # render_action "overthere"
186
+ # render :action => "overthere"
188
187
  # end
189
188
  #
190
189
  # Only the redirect happens. The rendering call is simply ignored.
@@ -203,18 +202,17 @@ module ActionController #:nodoc:
203
202
 
204
203
  DEFAULT_RENDER_STATUS_CODE = "200 OK"
205
204
 
206
- DEFAULT_SEND_FILE_OPTIONS = {
207
- :type => 'application/octet-stream'.freeze,
208
- :disposition => 'attachment'.freeze,
209
- :stream => true,
210
- :buffer_size => 4096
211
- }.freeze
212
-
213
205
  # Determines whether the view has access to controller internals @request, @response, @session, and @template.
214
206
  # By default, it does.
215
207
  @@view_controller_internals = true
216
208
  cattr_accessor :view_controller_internals
217
209
 
210
+ # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets,
211
+ # and images to a dedicated asset server away from the main web server. Example:
212
+ # ActionController::Base.asset_host = "http://assets.example.com"
213
+ @@asset_host = ""
214
+ cattr_accessor :asset_host
215
+
218
216
  # All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors.
219
217
  # When the application is ready to go public, this should be set to false, and the protected method <tt>local_request?</tt>
220
218
  # should instead be implemented in the controller to determine when debugging screens should be shown.
@@ -227,6 +225,12 @@ module ActionController #:nodoc:
227
225
  @@debug_routes = true
228
226
  cattr_accessor :debug_routes
229
227
 
228
+ # Controls whether the application is thread-safe, so multi-threaded servers like WEBrick knows whether to apply a mutex
229
+ # around the performance of each action. Action Pack and Active Record are by default thread-safe, but many applications
230
+ # may not be. Turned off by default.
231
+ @@allow_concurrency = false
232
+ cattr_accessor :allow_concurrency
233
+
230
234
  # Template root determines the base from which template references will be made. So a call to render("test/template")
231
235
  # will be converted to "#{template_root}/test/template.rhtml".
232
236
  class_inheritable_accessor :template_root
@@ -267,6 +271,9 @@ module ActionController #:nodoc:
267
271
  # is generated by taking a snapshot of all the instance variables in the current scope just before a template is rendered.
268
272
  attr_accessor :assigns
269
273
 
274
+ # Returns the name of the action this controller is processing.
275
+ attr_accessor :action_name
276
+
270
277
  class << self
271
278
  # Factory for the standard create, process loop where the controller is discarded after processing.
272
279
  def process(request, response) #:nodoc:
@@ -275,20 +282,24 @@ module ActionController #:nodoc:
275
282
 
276
283
  # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
277
284
  def controller_class_name
278
- Inflector.demodulize(name)
285
+ @controller_class_name ||= name.demodulize
279
286
  end
280
287
 
281
288
  # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat".
282
289
  def controller_name
283
- Inflector.underscore(controller_class_name.sub(/Controller/, ""))
290
+ @controller_name ||= controller_class_name.sub(/Controller$/, '').underscore
284
291
  end
285
292
 
286
293
  # Convert the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
287
294
  def controller_path
288
- components = self.name.to_s.split('::').collect { |name| name.underscore }
289
- components[-1] = $1 if /^(.*)_controller$/ =~ components[-1]
290
- components.shift if components.first == 'controllers' # Transitional conditional to accomodate root Controllers module
291
- components.join('/')
295
+ unless @controller_path
296
+ components = self.name.to_s.split('::')
297
+ components[-1] = $1 if /^(.*)Controller$/ =~ components.last
298
+ # Accomodate the root Controllers module.
299
+ components.shift if components.first == 'Controllers'
300
+ @controller_path = components.map { |name| name.underscore }.join('/')
301
+ end
302
+ @controller_path
292
303
  end
293
304
 
294
305
  # Return an array containing the names of public methods that have been marked hidden from the action processor.
@@ -314,9 +325,15 @@ module ActionController #:nodoc:
314
325
  # and find templates in /code/weblog/components/admin/parties/users/
315
326
  def uses_component_template_root
316
327
  path_of_calling_controller = File.dirname(caller[0].split(/:\d+:/).first)
317
- path_of_controller_root = path_of_calling_controller.sub(/#{controller_path.split("/")[0..-2]}$/, "")
328
+ path_of_controller_root = path_of_calling_controller.sub(/#{controller_path.split("/")[0..-2]}$/, "") # " (for ruby-mode)
318
329
  self.template_root = path_of_controller_root
319
330
  end
331
+
332
+ # Temporary method for enabling upload progress until it's ready to leave experimental mode
333
+ def enable_upload_progress # :nodoc:
334
+ require 'action_controller/upload_progress'
335
+ include ActionController::UploadProgress
336
+ end
320
337
  end
321
338
 
322
339
  public
@@ -325,6 +342,7 @@ module ActionController #:nodoc:
325
342
  initialize_template_class(response)
326
343
  assign_shortcuts(request, response)
327
344
  initialize_current_url
345
+ @action_name = params[:action] || 'index'
328
346
 
329
347
  log_processing unless logger.nil?
330
348
  send(method, *arguments)
@@ -377,7 +395,7 @@ module ActionController #:nodoc:
377
395
  # from this page.
378
396
  #
379
397
  # * <tt>url_for :action => 'bio'</tt> -- During the generation of this URL, default values will be used for the first and
380
- # last components, and the action shall change. The generated URL will be, "people/david/hh/bio".
398
+ # last components, and the action shall change. The generated URL will be, "people/hh/david/bio".
381
399
  # * <tt>url_for :first => 'davids-little-brother'</tt> This generates the URL 'people/hh/davids-little-brother' -- note
382
400
  # that this URL leaves out the assumed action of 'bio'.
383
401
  #
@@ -425,191 +443,211 @@ module ActionController #:nodoc:
425
443
  self.class.controller_name
426
444
  end
427
445
 
428
- # Returns the name of the action this controller is processing.
429
- def action_name
430
- @params["action"] || "index"
431
- end
432
-
433
446
  protected
434
- # Renders the template specified by <tt>template_name</tt>, which defaults to the name of the current controller and action.
435
- # So calling +render+ in WeblogController#show will attempt to render "#{template_root}/weblog/show.rhtml" or
436
- # "#{template_root}/weblog/show.rxml" (in that order). The template_root is set on the ActionController::Base class and is
437
- # shared by all controllers. It's also possible to pass a status code using the second parameter. This defaults to "200 OK",
438
- # but can be changed, such as by calling <tt>render("weblog/error", "500 Error")</tt>.
439
- def render(template_name = nil, status = nil) #:doc:
440
- render_file(template_name || default_template_name, status, true)
441
- end
442
-
443
- # Works like render, but instead of requiring a full template name, you can get by with specifying the action name. So calling
444
- # <tt>render_action "show_many"</tt> in WeblogController#display will render "#{template_root}/weblog/show_many.rhtml" or
445
- # "#{template_root}/weblog/show_many.rxml".
446
- def render_action(action_name, status = nil) #:doc:
447
- render(default_template_name(action_name), status)
448
- end
449
-
450
- # Works like render, but disregards the template_root and requires a full path to the template that needs to be rendered. Can be
451
- # used like <tt>render_file "/Users/david/Code/Ruby/template"</tt> to render "/Users/david/Code/Ruby/template.rhtml" or
452
- # "/Users/david/Code/Ruby/template.rxml".
453
- def render_file(template_path, status = nil, use_full_path = false) #:doc:
454
- assert_existance_of_template_file(template_path) if use_full_path
455
- logger.info("Rendering #{template_path} (#{status || DEFAULT_RENDER_STATUS_CODE})") unless logger.nil?
447
+ # Renders the content that'll be returned to the browser as the response body. This can just be as regular text, but is
448
+ # more often the compilation of a template.
449
+ #
450
+ # === Rendering an action
451
+ #
452
+ # Action rendering is the most common form and the type used automatically by Action Controller when nothing else is
453
+ # specified. By default, actions are rendered within the current layout (if one exists).
454
+ #
455
+ # # Renders the template for the action "goal" within the current controller
456
+ # render :action => "goal"
457
+ #
458
+ # # Renders the template for the action "explosion" from the ErrorsController
459
+ # render :action => "errors/explosion", :status => 500
460
+ #
461
+ # # Renders the template for the action "short_goal" within the current controller,
462
+ # # but without the current active layout
463
+ # render :action => "short_goal", :layout => false
464
+ #
465
+ # # Renders the template for the action "long_goal" within the current controller,
466
+ # # but with a custom layout
467
+ # render :action => "short_goal", :layout => "spectacular"
468
+ #
469
+ # _Deprecation_ _notice_: This used to have the signatures <tt>render_action("action", status = 200)</tt>,
470
+ # <tt>render_without_layout("controller/action", status = 200)</tt>, and
471
+ # <tt>render_with_layout("controller/action", status = 200, layout)</tt>.
472
+ #
473
+ # === Rendering partials
474
+ #
475
+ # Partial rendering is most commonly used together with Ajax calls that only updates one or a few elements on a page
476
+ # without reloading. Rendering of partials from the controller makes it possible to use the same partial template in
477
+ # both the full-page rendering (by calling it from within the template) and when sub-page updates happen (from the
478
+ # controller action responding to Ajax calls). By default, the current layout is not used.
479
+ #
480
+ # # Renders the partial located at app/views/controller/_win.r(html|xml)
481
+ # render :partial => "win"
482
+ #
483
+ # # Renders the partial with a status code of 500 (internal error)
484
+ # render :partial => "broken", :status => 500
485
+ #
486
+ # # Renders the same partial but also makes a local variable available to it
487
+ # render :partial => "win", :locals => { :name => "david" }
488
+ #
489
+ # # Renders a collection of the same partial by making each element of @wins available through
490
+ # # the local variable "win" as it builds the complete response
491
+ # render :partial => "win", :collection => @wins
492
+ #
493
+ # # Renders the same collection of partials, but also renders the win_divider partial in between
494
+ # # each win partial.
495
+ # render :partial => "win", :collection => @wins, :spacer_template => "win_divider"
496
+ #
497
+ # _Deprecation_ _notice_: This used to have the signatures
498
+ # <tt>render_partial(partial_path = default_template_name, object = nil, local_assigns = {})</tt> and
499
+ # <tt>render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = {})</tt>.
500
+ #
501
+ # === Rendering a file
502
+ #
503
+ # File rendering works just like action rendering except that it takes a complete path to the template intended
504
+ # for rendering and that the current layout is not applied automatically.
505
+ #
506
+ # # Renders the template located in /path/to/some/template.r(html|xml)
507
+ # render :file => "/path/to/some/template"
508
+ #
509
+ # # Renders the same template within the current layout, but with a 404 status code
510
+ # render :file => "/path/to/some/template", :layout => true, :status => 404
511
+ #
512
+ # _Deprecation_ _notice_: This used to have the signature <tt>render_file(path, status = 200)</tt>
513
+ #
514
+ # === Rendering text
515
+ #
516
+ # Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text
517
+ # rendering is not done within the active layout.
518
+ #
519
+ # # Renders the clear text "hello world" with status code 200
520
+ # render :text => "hello world!"
521
+ #
522
+ # # Renders the clear text "Explosion!" with status code 500
523
+ # render :text => "Explosion!", :status => 500
524
+ #
525
+ # # Renders the clear text "Hi there!" within the current active layout (if one exists)
526
+ # render :text => "Explosion!", :layout => true
527
+ #
528
+ # # Renders the clear text "Hi there!" within the the layout
529
+ # # placed in "app/views/layouts/special.r(html|xml)"
530
+ # render :text => "Explosion!", :layout => "special"
531
+ #
532
+ # _Deprecation_ _notice_: This used to have the signature <tt>render_text("text", status = 200)</tt>
533
+ #
534
+ # === Rendering an inline template
535
+ #
536
+ # Rendering of an inline template works as a cross between text and action rendering where the source for the template
537
+ # is supplied inline, like text, but its interpreted with ERb or Builder, like action. By default, ERb is used for rendering
538
+ # and the current layout is not used.
539
+ #
540
+ # # Renders "hello, hello, hello, again"
541
+ # render :inline => "<%= 'hello, ' * 3 + 'again' %>"
542
+ #
543
+ # # Renders "<p>Good seeing you!</p>" using Builder
544
+ # render :inline => "xml.p { 'Good seeing you!' }", :type => :rxml
545
+ #
546
+ # # Renders "hello david"
547
+ # render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" }
548
+ #
549
+ # _Deprecation_ _notice_: This used to have the signature <tt>render_template(template, status = 200, type = :rhtml)</tt>
550
+ #
551
+ # === Rendering nothing
552
+ #
553
+ # Rendering nothing is often convenient in combination with Ajax calls that perform their effect client-side or
554
+ # when you just want to communicate a status code.
555
+ #
556
+ # # Renders an empty response with status code 200
557
+ # render :nothing => true
558
+ #
559
+ # # Renders an empty response with status code 401 (access denied)
560
+ # render :nothing => true, :status => 401
561
+ def render(options = {}, deprecated_status = nil) #:doc:
562
+ # puts "Rendering: #{options.inspect}"
563
+ raise DoubleRenderError, "Can only render or redirect once per action" if performed?
456
564
 
457
- add_variables_to_assigns
458
- render_text(@template.render_file(template_path, use_full_path), status)
459
- end
460
-
461
- # Renders the +template+ string, which is useful for rendering short templates you don't want to bother having a file for. So
462
- # you'd call <tt>render_template "Hello, <%= @user.name %>"</tt> to greet the current user. Or if you want to render as Builder
463
- # template, you could do <tt>render_template "xml.h1 @user.name", nil, "rxml"</tt>.
464
- def render_template(template, status = nil, type = "rhtml") #:doc:
465
- add_variables_to_assigns
466
- render_text(@template.render_template(type, template), status)
467
- end
565
+ # Backwards compatibility
566
+ return render({ :template => options || default_template_name, :status => deprecated_status }) if !options.is_a?(Hash)
468
567
 
469
- # Renders the +text+ string without parsing it through any template engine. Useful for rendering static information as it's
470
- # considerably faster than rendering through the template engine.
471
- # Use block for response body if provided (useful for deferred rendering or streaming output).
472
- def render_text(text = nil, status = nil, &block) #:doc:
473
- return if performed?
474
568
  add_variables_to_assigns
475
- @response.headers["Status"] = status || DEFAULT_RENDER_STATUS_CODE
476
- @response.body = block_given? ? block : text
477
- @performed_render = true
478
- end
479
-
480
- # Renders an empty response that can be used when the request is only interested in triggering an effect. Do note that good
481
- # HTTP manners mandate that you don't use GET requests to trigger data changes.
482
- def render_nothing(status = nil) #:doc:
483
- render_text "", status
569
+ options[:status] = (options[:status] || DEFAULT_RENDER_STATUS_CODE).to_s
570
+
571
+ if options[:text]
572
+ @response.headers["Status"] = options[:status]
573
+ @response.body = options[:text]
574
+ @performed_render = true
575
+ return options[:text]
576
+
577
+ elsif options[:file]
578
+ assert_existance_of_template_file(options[:file]) if options[:use_full_path]
579
+ logger.info("Rendering #{options[:file]} (#{options[:status]})") unless logger.nil?
580
+ render(options.merge({ :text => @template.render_file(options[:file], options[:use_full_path])}))
581
+
582
+ elsif options[:template]
583
+ render(options.merge({ :file => options[:template], :use_full_path => true }))
584
+
585
+ elsif options[:inline]
586
+ render(options.merge({
587
+ :text =>
588
+ @template.render_template(
589
+ options[:type] || :rhtml,
590
+ options[:inline],
591
+ options[:locals] || {}
592
+ )
593
+ }))
594
+
595
+ elsif options[:action]
596
+ render(options.merge({ :template => default_template_name(options[:action]) }))
597
+
598
+ elsif options[:partial] && options[:collection]
599
+ render(options.merge({
600
+ :text => (
601
+ @template.render_partial_collection(
602
+ options[:partial] == true ? default_template_name : options[:partial],
603
+ options[:collection], options[:spacer_template],
604
+ options[:locals] || {}
605
+ ) || ''
606
+ )
607
+ }))
608
+
609
+ elsif options[:partial]
610
+ render(options.merge({ :text => @template.render_partial(
611
+ options[:partial] == true ? default_template_name : options[:partial],
612
+ options[:object], options[:locals] || {}
613
+ ) }))
614
+
615
+ elsif options[:nothing]
616
+ render(options.merge({ :text => "" }))
617
+
618
+ else
619
+ render(options.merge({ :action => action_name }))
620
+ end
484
621
  end
485
622
 
486
- # Returns the result of the render as a string.
487
- def render_to_string(template_name = default_template_name) #:doc:
488
- add_variables_to_assigns
489
- @template.render_file(template_name)
623
+ # Renders according to the same rules as <tt>render</tt>, but returns the result in a string instead
624
+ # of sending it as the response body to the browser.
625
+ def render_to_string(options = {}) #:doc:
626
+ result = render(options)
627
+ erase_render_results
628
+ return result
490
629
  end
491
630
 
492
- # Renders the partial specified by <tt>partial_path</tt>, which by default is the name of the action itself. Example:
493
- #
494
- # class WeblogController < ActionController::Base
495
- # def show
496
- # render_partial # renders "weblog/_show.r(xml|html)"
497
- # end
498
- # end
499
- def render_partial(partial_path = default_template_name, object = nil, local_assigns = {}) #:doc:
500
- add_variables_to_assigns
501
- render_text(@template.render_partial(partial_path, object, local_assigns))
502
- end
503
-
504
- # Renders a collection of partials using <tt>partial_name</tt> to iterate over the +collection+.
505
- def render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = {})#:doc:
506
- add_variables_to_assigns
507
- render_text(@template.render_collection_of_partials(partial_name, collection, partial_spacer_template, local_assigns))
508
- end
509
-
510
- # Sends the file by streaming it 4096 bytes at a time. This way the
511
- # whole file doesn't need to be read into memory at once. This makes
512
- # it feasible to send even large files.
513
- #
514
- # Be careful to sanitize the path parameter if it coming from a web
515
- # page. send_file(@params['path']) allows a malicious user to
516
- # download any file on your server.
517
- #
518
- # Options:
519
- # * <tt>:filename</tt> - suggests a filename for the browser to use.
520
- # Defaults to File.basename(path).
521
- # * <tt>:type</tt> - specifies an HTTP content type.
522
- # Defaults to 'application/octet-stream'.
523
- # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
524
- # Valid values are 'inline' and 'attachment' (default).
525
- # * <tt>:streaming</tt> - whether to send the file to the user agent as it is read (true)
526
- # or to read the entire file before sending (false). Defaults to true.
527
- # * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream the file.
528
- # Defaults to 4096.
529
- #
530
- # The default Content-Type and Content-Disposition headers are
531
- # set to download arbitrary binary files in as many browsers as
532
- # possible. IE versions 4, 5, 5.5, and 6 are all known to have
533
- # a variety of quirks (especially when downloading over SSL).
534
- #
535
- # Simple download:
536
- # send_file '/path/to.zip'
537
- #
538
- # Show a JPEG in browser:
539
- # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
540
- #
541
- # Read about the other Content-* HTTP headers if you'd like to
542
- # provide the user with more information (such as Content-Description).
543
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11
544
- #
545
- # Also be aware that the document may be cached by proxies and browsers.
546
- # The Pragma and Cache-Control headers declare how the file may be cached
547
- # by intermediaries. They default to require clients to validate with
548
- # the server before releasing cached responses. See
549
- # http://www.mnot.net/cache_docs/ for an overview of web caching and
550
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
551
- # for the Cache-Control header spec.
552
- def send_file(path, options = {}) #:doc:
553
- raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
554
-
555
- options[:length] ||= File.size(path)
556
- options[:filename] ||= File.basename(path)
557
- send_file_headers! options
558
631
 
632
+ # Clears the rendered results, allowing for another render to be performed.
633
+ def erase_render_results #:nodoc:
634
+ @response.body = nil
559
635
  @performed_render = false
560
-
561
- if options[:stream]
562
- render_text do
563
- logger.info "Streaming file #{path}" unless logger.nil?
564
- len = options[:buffer_size] || 4096
565
- File.open(path, 'rb') do |file|
566
- if $stdout.respond_to?(:syswrite)
567
- begin
568
- while true
569
- $stdout.syswrite file.sysread(len)
570
- end
571
- rescue EOFError
572
- end
573
- else
574
- while buf = file.read(len)
575
- $stdout.write buf
576
- end
577
- end
578
- end
579
- end
580
- else
581
- logger.info "Sending file #{path}" unless logger.nil?
582
- File.open(path, 'rb') { |file| render_text file.read }
583
- end
584
636
  end
585
637
 
586
- # Send binary data to the user as a file download. May set content type, apparent file name,
587
- # and specify whether to show data inline or download as an attachment.
588
- #
589
- # Options:
590
- # * <tt>:filename</tt> - Suggests a filename for the browser to use.
591
- # * <tt>:type</tt> - specifies an HTTP content type.
592
- # Defaults to 'application/octet-stream'.
593
- # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
594
- # Valid values are 'inline' and 'attachment' (default).
595
- #
596
- # Generic data download:
597
- # send_data buffer
598
- #
599
- # Download a dynamically-generated tarball:
600
- # send_data generate_tgz('dir'), :filename => 'dir.tgz'
601
- #
602
- # Display an image Active Record in the browser:
603
- # send_data image.data, :type => image.content_type, :disposition => 'inline'
604
- #
605
- # See +send_file+ for more information on HTTP Content-* headers and caching.
606
- def send_data(data, options = {}) #:doc:
607
- logger.info "Sending data #{options[:filename]}" unless logger.nil?
608
- send_file_headers! options.merge(:length => data.size)
609
- @performed_render = false
610
- render_text data
638
+ # Clears the redirected results from the headers, resetting the status to 200 and returns
639
+ # the URL that was used to redirect or nil if there was no redirected URL
640
+ # Note that +redirect_to+ will change the body of the response to indicate a redirection.
641
+ # The response body is not reset here, see +erase_render_results+
642
+ def erase_redirect_results #:nodoc:
643
+ @performed_redirect = false
644
+ response.redirected_to = nil
645
+ response.redirected_to_method_params = nil
646
+ response.headers['Status'] = DEFAULT_RENDER_STATUS_CODE
647
+ response.headers.delete('location')
611
648
  end
612
649
 
650
+
613
651
  def rewrite_options(options)
614
652
  if defaults = default_url_options(options)
615
653
  defaults.merge(options)
@@ -631,33 +669,40 @@ module ActionController #:nodoc:
631
669
  def default_url_options(options) #:doc:
632
670
  end
633
671
 
634
- # Redirects the browser to an URL that has been rewritten according to the hash of +options+ using a "302 Moved" HTTP header.
635
- # See url_for for a description of the valid options.
672
+ # Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
673
+ #
674
+ # * <tt>Hash</tt>: The URL will be generated by calling url_for with the +options+.
675
+ # * <tt>String starting with protocol:// (like http://)</tt>: Is passed straight through as the target for redirection.
676
+ # * <tt>String not containing a protocol</tt>: The current current protocol and host is prepended to the string.
677
+ #
678
+ # Examples:
679
+ # redirect_to :action => "show", :id => 5
680
+ # redirect_to "http://www.rubyonrails.org"
681
+ # redirect_to "/images/screenshot.jpg"
682
+ #
683
+ # The redirection happens as a "302 Moved" header.
636
684
  def redirect_to(options = {}, *parameters_for_method_reference) #:doc:
637
- if parameters_for_method_reference.empty?
638
- @response.redirected_to = options
639
- redirect_to_url(url_for(options))
640
- else
641
- @response.redirected_to, @response.redirected_to_method_params = options, parameters_for_method_reference
642
- redirect_to_url(url_for(options, *parameters_for_method_reference))
685
+ case options
686
+ when %r{^\w+://.*}
687
+ raise DoubleRenderError, "Can only render or redirect once per action" if performed?
688
+ logger.info("Redirected to #{options}") unless logger.nil?
689
+ response.redirect(options)
690
+ response.redirected_to = options
691
+ @performed_redirect = true
692
+
693
+ when String
694
+ redirect_to(request.protocol + request.host_with_port + options)
695
+
696
+ else
697
+ if parameters_for_method_reference.empty?
698
+ redirect_to(url_for(options))
699
+ response.redirected_to = options
700
+ else
701
+ redirect_to(url_for(options, *parameters_for_method_reference))
702
+ response.redirected_to, response.redirected_to_method_params = options, parameters_for_method_reference
703
+ end
643
704
  end
644
705
  end
645
-
646
- # Redirects the browser to the specified <tt>path</tt> within the current host (specified with a leading /). Used to sidestep
647
- # the URL rewriting and go directly to a known path. Example: <tt>redirect_to_path "/images/screenshot.jpg"</tt>.
648
- def redirect_to_path(path) #:doc:
649
- redirect_to_url(@request.protocol + @request.host_with_port + path)
650
- end
651
-
652
- # Redirects the browser to the specified <tt>url</tt>. Used to redirect outside of the current application. Example:
653
- # <tt>redirect_to_url "http://www.rubyonrails.org"</tt>. If the resource has moved permanently, it's possible to pass true as the
654
- # second parameter and the browser will get "301 Moved Permanently" instead of "302 Found".
655
- def redirect_to_url(url, permanently = false) #:doc:
656
- return if performed?
657
- logger.info("Redirected to #{url}") unless logger.nil?
658
- @response.redirect(url, permanently)
659
- @performed_redirect = true
660
- end
661
706
 
662
707
  # Resets the session by clearing out all the objects stored within and initializing a new session object.
663
708
  def reset_session #:doc:
@@ -666,11 +711,6 @@ module ActionController #:nodoc:
666
711
  @response.session = @session
667
712
  end
668
713
 
669
- # Deprecated cookie writer method
670
- def cookie(*options)
671
- @response.headers["cookie"] << CGI::Cookie.new(*options)
672
- end
673
-
674
714
  private
675
715
  def initialize_template_class(response)
676
716
  begin
@@ -721,16 +761,17 @@ module ActionController #:nodoc:
721
761
  def action_methods
722
762
  @action_methods ||= (self.class.public_instance_methods - self.class.hidden_actions)
723
763
  end
724
-
764
+
765
+
725
766
  def add_variables_to_assigns
726
767
  add_instance_variables_to_assigns
727
768
  add_class_variables_to_assigns if view_controller_internals
728
769
  end
729
770
 
730
771
  def add_instance_variables_to_assigns
731
- protected_variables_cache = protected_instance_variables
772
+ @@protected_variables_cache = protected_instance_variables.inject({}) { |h, k| h[k] = true; h }
732
773
  instance_variables.each do |var|
733
- next if protected_variables_cache.include?(var)
774
+ next if @@protected_variables_cache.include?(var)
734
775
  @assigns[var[1..-1]] = instance_variable_get(var)
735
776
  end
736
777
  end
@@ -749,13 +790,19 @@ module ActionController #:nodoc:
749
790
  end
750
791
  end
751
792
 
793
+
752
794
  def request_origin
753
795
  "#{@request.remote_ip} at #{Time.now.to_s}"
754
796
  end
755
797
 
798
+ def complete_request_uri
799
+ request.protocol + request.host + request.request_uri
800
+ end
801
+
756
802
  def close_session
757
803
  @session.close unless @session.nil? || Hash === @session
758
804
  end
805
+
759
806
 
760
807
  def template_exists?(template_name = default_template_name)
761
808
  @template.file_exists?(template_name)
@@ -773,23 +820,6 @@ module ActionController #:nodoc:
773
820
  end
774
821
  end
775
822
 
776
- def send_file_headers!(options)
777
- options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
778
- [:length, :type, :disposition].each do |arg|
779
- raise ArgumentError, ":#{arg} option required" if options[arg].nil?
780
- end
781
-
782
- disposition = options[:disposition].dup || 'attachment'
783
- disposition <<= %(; filename="#{options[:filename]}") if options[:filename]
784
-
785
- @headers.update(
786
- 'Content-Length' => options[:length],
787
- 'Content-Type' => options[:type].strip, # fixes a problem with extra '\r' with some browsers
788
- 'Content-Disposition' => disposition,
789
- 'Content-Transfer-Encoding' => 'binary'
790
- );
791
- end
792
-
793
823
  def default_template_name(default_action_name = action_name)
794
824
  "#{self.class.controller_path}/#{default_action_name}"
795
825
  end