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.
- data/CHANGELOG +309 -16
- data/README +1 -1
- data/lib/action_controller.rb +5 -0
- data/lib/action_controller/assertions.rb +57 -12
- data/lib/action_controller/auto_complete.rb +47 -0
- data/lib/action_controller/base.rb +288 -258
- data/lib/action_controller/benchmarking.rb +8 -3
- data/lib/action_controller/caching.rb +88 -42
- data/lib/action_controller/cgi_ext/cgi_ext.rb +1 -1
- data/lib/action_controller/cgi_ext/cgi_methods.rb +41 -11
- data/lib/action_controller/cgi_ext/multipart_progress.rb +169 -0
- data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +30 -12
- data/lib/action_controller/cgi_process.rb +39 -11
- data/lib/action_controller/code_generation.rb +235 -0
- data/lib/action_controller/cookies.rb +14 -8
- data/lib/action_controller/deprecated_renders_and_redirects.rb +76 -0
- data/lib/action_controller/filters.rb +8 -7
- data/lib/action_controller/helpers.rb +41 -6
- data/lib/action_controller/layout.rb +45 -16
- data/lib/action_controller/request.rb +86 -23
- data/lib/action_controller/rescue.rb +1 -0
- data/lib/action_controller/response.rb +1 -1
- data/lib/action_controller/routing.rb +536 -272
- data/lib/action_controller/scaffolding.rb +30 -25
- data/lib/action_controller/session/active_record_store.rb +251 -50
- data/lib/action_controller/streaming.rb +133 -0
- data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -7
- data/lib/action_controller/templates/scaffolds/edit.rhtml +2 -2
- data/lib/action_controller/templates/scaffolds/layout.rhtml +22 -18
- data/lib/action_controller/templates/scaffolds/list.rhtml +3 -3
- data/lib/action_controller/templates/scaffolds/new.rhtml +2 -2
- data/lib/action_controller/templates/scaffolds/show.rhtml +1 -1
- data/lib/action_controller/test_process.rb +68 -47
- data/lib/action_controller/upload_progress.rb +421 -0
- data/lib/action_controller/url_rewriter.rb +8 -11
- data/lib/action_controller/vendor/html-scanner/html/document.rb +6 -5
- data/lib/action_controller/vendor/html-scanner/html/node.rb +70 -14
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +17 -10
- data/lib/action_controller/vendor/html-scanner/html/version.rb +3 -3
- data/lib/action_controller/vendor/xml_simple.rb +1019 -0
- data/lib/action_controller/verification.rb +36 -30
- data/lib/action_view/base.rb +21 -14
- data/lib/action_view/helpers/active_record_helper.rb +15 -13
- data/lib/action_view/helpers/asset_tag_helper.rb +26 -9
- data/lib/action_view/helpers/benchmark_helper.rb +24 -0
- data/lib/action_view/helpers/capture_helper.rb +7 -5
- data/lib/action_view/helpers/date_helper.rb +63 -46
- data/lib/action_view/helpers/form_helper.rb +7 -1
- data/lib/action_view/helpers/form_options_helper.rb +19 -11
- data/lib/action_view/helpers/form_tag_helper.rb +5 -1
- data/lib/action_view/helpers/javascript_helper.rb +403 -35
- data/lib/action_view/helpers/javascripts/controls.js +261 -0
- data/lib/action_view/helpers/javascripts/dragdrop.js +476 -0
- data/lib/action_view/helpers/javascripts/effects.js +570 -0
- data/lib/action_view/helpers/javascripts/prototype.js +633 -371
- data/lib/action_view/helpers/number_helper.rb +11 -13
- data/lib/action_view/helpers/tag_helper.rb +1 -2
- data/lib/action_view/helpers/text_helper.rb +69 -6
- data/lib/action_view/helpers/upload_progress_helper.rb +433 -0
- data/lib/action_view/helpers/url_helper.rb +98 -3
- data/lib/action_view/partials.rb +14 -8
- data/lib/action_view/vendor/builder/xmlmarkup.rb +11 -0
- data/rakefile +13 -5
- data/test/abstract_unit.rb +1 -1
- data/test/controller/action_pack_assertions_test.rb +52 -9
- data/test/controller/active_record_assertions_test.rb +119 -120
- data/test/controller/active_record_store_test.rb +111 -0
- data/test/controller/addresses_render_test.rb +45 -0
- data/test/controller/caching_filestore.rb +92 -0
- data/test/controller/capture_test.rb +39 -0
- data/test/controller/cgi_test.rb +40 -3
- data/test/controller/helper_test.rb +65 -13
- data/test/controller/multipart_progress_testx.rb +365 -0
- data/test/controller/new_render_test.rb +263 -0
- data/test/controller/redirect_test.rb +64 -0
- data/test/controller/render_test.rb +20 -21
- data/test/controller/request_test.rb +83 -3
- data/test/controller/routing_test.rb +702 -0
- data/test/controller/send_file_test.rb +2 -0
- data/test/controller/test_test.rb +44 -8
- data/test/controller/upload_progress_testx.rb +89 -0
- data/test/controller/verification_test.rb +94 -29
- data/test/fixtures/addresses/list.rhtml +1 -0
- data/test/fixtures/test/capturing.rhtml +4 -0
- data/test/fixtures/test/list.rhtml +1 -1
- data/test/fixtures/test/update_element_with_capture.rhtml +9 -0
- data/test/template/active_record_helper_test.rb +30 -15
- data/test/template/asset_tag_helper_test.rb +12 -5
- data/test/template/benchmark_helper_test.rb +72 -0
- data/test/template/date_helper_test.rb +69 -0
- data/test/template/form_helper_test.rb +18 -10
- data/test/template/form_options_helper_test.rb +40 -5
- data/test/template/javascript_helper.rb +149 -2
- data/test/template/number_helper_test.rb +2 -0
- data/test/template/tag_helper_test.rb +4 -0
- data/test/template/text_helper_test.rb +36 -0
- data/test/template/upload_progress_helper_testx.rb +272 -0
- data/test/template/url_helper_test.rb +30 -0
- metadata +30 -6
- data/test/controller/layout_test.rb +0 -49
- 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
|
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 =
|
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
|
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
|
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
|
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
|
103
|
+
# You can place objects in the session by using the <tt>session</tt> hash accessor:
|
98
104
|
#
|
99
|
-
#
|
105
|
+
# session[:person] = Person.authenticate(user_name, password)
|
100
106
|
#
|
101
107
|
# And retrieved again through the same hash:
|
102
108
|
#
|
103
|
-
# Hello #{
|
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(
|
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(
|
141
|
+
# @results = Search.find(params[:query])
|
143
142
|
# case @results
|
144
|
-
# when 0 then render "
|
145
|
-
# when 1 then
|
146
|
-
# when 2..10 then
|
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
|
-
#
|
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
|
-
|
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
|
-
|
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
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
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/
|
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
|
435
|
-
#
|
436
|
-
#
|
437
|
-
#
|
438
|
-
#
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
#
|
444
|
-
#
|
445
|
-
# "
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
#
|
451
|
-
#
|
452
|
-
#
|
453
|
-
|
454
|
-
|
455
|
-
|
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
|
-
|
458
|
-
|
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
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
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
|
-
#
|
487
|
-
|
488
|
-
|
489
|
-
|
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
|
-
#
|
587
|
-
#
|
588
|
-
#
|
589
|
-
#
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
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
|
635
|
-
#
|
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
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
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
|