actionpack 4.2.10 → 5.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +553 -401
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/abstract_controller/base.rb +28 -38
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +51 -11
- data/lib/abstract_controller/caching.rb +62 -0
- data/lib/abstract_controller/callbacks.rb +52 -19
- data/lib/abstract_controller/collector.rb +4 -9
- data/lib/abstract_controller/error.rb +4 -0
- data/lib/abstract_controller/helpers.rb +4 -3
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
- data/lib/abstract_controller/rendering.rb +28 -18
- data/lib/abstract_controller/translation.rb +8 -7
- data/lib/abstract_controller.rb +6 -2
- data/lib/action_controller/api/api_rendering.rb +14 -0
- data/lib/action_controller/api.rb +147 -0
- data/lib/action_controller/base.rb +10 -13
- data/lib/action_controller/caching.rb +13 -58
- data/lib/action_controller/form_builder.rb +48 -0
- data/lib/action_controller/log_subscriber.rb +3 -10
- data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
- data/lib/action_controller/metal/conditional_get.rb +106 -34
- data/lib/action_controller/metal/cookies.rb +1 -3
- data/lib/action_controller/metal/data_streaming.rb +11 -32
- data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +11 -6
- data/lib/action_controller/metal/force_ssl.rb +10 -10
- data/lib/action_controller/metal/head.rb +14 -8
- data/lib/action_controller/metal/helpers.rb +15 -6
- data/lib/action_controller/metal/http_authentication.rb +44 -35
- data/lib/action_controller/metal/implicit_render.rb +61 -6
- data/lib/action_controller/metal/instrumentation.rb +5 -5
- data/lib/action_controller/metal/live.rb +66 -88
- data/lib/action_controller/metal/mime_responds.rb +27 -42
- data/lib/action_controller/metal/params_wrapper.rb +8 -8
- data/lib/action_controller/metal/redirecting.rb +32 -9
- data/lib/action_controller/metal/renderers.rb +85 -40
- data/lib/action_controller/metal/rendering.rb +38 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +126 -48
- data/lib/action_controller/metal/rescue.rb +3 -12
- data/lib/action_controller/metal/streaming.rb +4 -4
- data/lib/action_controller/metal/strong_parameters.rb +293 -90
- data/lib/action_controller/metal/testing.rb +1 -12
- data/lib/action_controller/metal/url_for.rb +12 -5
- data/lib/action_controller/metal.rb +88 -63
- data/lib/action_controller/renderer.rb +111 -0
- data/lib/action_controller/template_assertions.rb +9 -0
- data/lib/action_controller/test_case.rb +288 -368
- data/lib/action_controller.rb +12 -9
- data/lib/action_dispatch/http/cache.rb +73 -34
- data/lib/action_dispatch/http/filter_parameters.rb +15 -11
- data/lib/action_dispatch/http/filter_redirect.rb +7 -8
- data/lib/action_dispatch/http/headers.rb +44 -13
- data/lib/action_dispatch/http/mime_negotiation.rb +41 -23
- data/lib/action_dispatch/http/mime_type.rb +126 -90
- data/lib/action_dispatch/http/mime_types.rb +3 -4
- data/lib/action_dispatch/http/parameter_filter.rb +18 -8
- data/lib/action_dispatch/http/parameters.rb +54 -41
- data/lib/action_dispatch/http/request.rb +149 -82
- data/lib/action_dispatch/http/response.rb +206 -102
- data/lib/action_dispatch/http/url.rb +117 -8
- data/lib/action_dispatch/journey/formatter.rb +39 -28
- data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
- data/lib/action_dispatch/journey/nodes/node.rb +14 -4
- data/lib/action_dispatch/journey/parser_extras.rb +4 -0
- data/lib/action_dispatch/journey/path/pattern.rb +38 -42
- data/lib/action_dispatch/journey/route.rb +74 -19
- data/lib/action_dispatch/journey/router/utils.rb +5 -5
- data/lib/action_dispatch/journey/router.rb +5 -9
- data/lib/action_dispatch/journey/routes.rb +14 -15
- data/lib/action_dispatch/journey/visitors.rb +86 -43
- data/lib/action_dispatch/middleware/callbacks.rb +10 -1
- data/lib/action_dispatch/middleware/cookies.rb +189 -135
- data/lib/action_dispatch/middleware/debug_exceptions.rb +124 -49
- data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -21
- data/lib/action_dispatch/middleware/executor.rb +19 -0
- data/lib/action_dispatch/middleware/flash.rb +66 -45
- data/lib/action_dispatch/middleware/params_parser.rb +32 -46
- data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
- data/lib/action_dispatch/middleware/reloader.rb +14 -58
- data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
- data/lib/action_dispatch/middleware/request_id.rb +11 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
- data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +30 -24
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
- data/lib/action_dispatch/middleware/ssl.rb +115 -36
- data/lib/action_dispatch/middleware/stack.rb +44 -40
- data/lib/action_dispatch/middleware/static.rb +51 -35
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
- data/lib/action_dispatch/railtie.rb +2 -2
- data/lib/action_dispatch/request/session.rb +69 -33
- data/lib/action_dispatch/request/utils.rb +51 -19
- data/lib/action_dispatch/routing/inspector.rb +32 -43
- data/lib/action_dispatch/routing/mapper.rb +491 -338
- data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
- data/lib/action_dispatch/routing/redirection.rb +3 -3
- data/lib/action_dispatch/routing/route_set.rb +145 -238
- data/lib/action_dispatch/routing/url_for.rb +27 -10
- data/lib/action_dispatch/routing.rb +17 -13
- data/lib/action_dispatch/testing/assertion_response.rb +45 -0
- data/lib/action_dispatch/testing/assertions/response.rb +38 -20
- data/lib/action_dispatch/testing/assertions/routing.rb +11 -10
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/integration.rb +368 -97
- data/lib/action_dispatch/testing/test_process.rb +5 -6
- data/lib/action_dispatch/testing/test_request.rb +22 -31
- data/lib/action_dispatch/testing/test_response.rb +7 -4
- data/lib/action_dispatch.rb +3 -1
- data/lib/action_pack/gem_version.rb +3 -3
- data/lib/action_pack.rb +1 -1
- metadata +30 -34
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
- /data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
@@ -1,224 +1,75 @@
|
|
1
1
|
require 'rack/session/abstract/id'
|
2
|
+
require 'active_support/core_ext/hash/conversions'
|
2
3
|
require 'active_support/core_ext/object/to_query'
|
3
4
|
require 'active_support/core_ext/module/anonymous'
|
4
5
|
require 'active_support/core_ext/hash/keys'
|
5
|
-
require '
|
6
|
-
|
6
|
+
require 'action_controller/template_assertions'
|
7
7
|
require 'rails-dom-testing'
|
8
8
|
|
9
9
|
module ActionController
|
10
|
-
|
11
|
-
|
10
|
+
# :stopdoc:
|
11
|
+
class Metal
|
12
|
+
include Testing::Functional
|
13
|
+
end
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
RENDER_TEMPLATE_INSTANCE_VARIABLES.each do |instance_variable|
|
22
|
-
instance_variable_set("@_#{instance_variable}", Hash.new(0))
|
23
|
-
end
|
24
|
-
|
25
|
-
@_subscribers = []
|
26
|
-
|
27
|
-
@_subscribers << ActiveSupport::Notifications.subscribe("render_template.action_view") do |_name, _start, _finish, _id, payload|
|
28
|
-
path = payload[:layout]
|
29
|
-
if path
|
30
|
-
@_layouts[path] += 1
|
31
|
-
if path =~ /^layouts\/(.*)/
|
32
|
-
@_layouts[$1] += 1
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
@_subscribers << ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload|
|
38
|
-
if virtual_path = payload[:virtual_path]
|
39
|
-
partial = virtual_path =~ /^.*\/_[^\/]*$/
|
40
|
-
|
41
|
-
if partial
|
42
|
-
@_partials[virtual_path] += 1
|
43
|
-
@_partials[virtual_path.split("/").last] += 1
|
44
|
-
end
|
45
|
-
|
46
|
-
@_templates[virtual_path] += 1
|
47
|
-
else
|
48
|
-
path = payload[:identifier]
|
49
|
-
if path
|
50
|
-
@_files[path] += 1
|
51
|
-
@_files[path.split("/").last] += 1
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
15
|
+
module Live
|
16
|
+
# Disable controller / rendering threads in tests. User tests can access
|
17
|
+
# the database on the main thread, so they could open a txn, then the
|
18
|
+
# controller thread will open a new connection and try to access data
|
19
|
+
# that's only visible to the main thread's txn. This is the problem in #23483
|
20
|
+
remove_method :new_controller_thread
|
21
|
+
def new_controller_thread # :nodoc:
|
22
|
+
yield
|
55
23
|
end
|
24
|
+
end
|
56
25
|
|
57
|
-
|
58
|
-
|
26
|
+
# ActionController::TestCase will be deprecated and moved to a gem in Rails 5.1.
|
27
|
+
# Please use ActionDispatch::IntegrationTest going forward.
|
28
|
+
class TestRequest < ActionDispatch::TestRequest #:nodoc:
|
29
|
+
DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
|
30
|
+
DEFAULT_ENV.delete 'PATH_INFO'
|
59
31
|
|
60
|
-
|
61
|
-
|
62
|
-
end
|
32
|
+
def self.new_session
|
33
|
+
TestSession.new
|
63
34
|
end
|
64
35
|
|
65
|
-
|
66
|
-
|
67
|
-
|
36
|
+
# Create a new test request with default `env` values
|
37
|
+
def self.create
|
38
|
+
env = {}
|
39
|
+
env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
|
40
|
+
env["rack.request.cookie_hash"] = {}.with_indifferent_access
|
41
|
+
new(default_env.merge(env), new_session)
|
68
42
|
end
|
69
43
|
|
70
|
-
def
|
71
|
-
|
72
|
-
ivar_name = "@_#{instance_variable}"
|
73
|
-
if instance_variable_defined?(ivar_name)
|
74
|
-
instance_variable_get(ivar_name).clear
|
75
|
-
end
|
76
|
-
end
|
44
|
+
def self.default_env
|
45
|
+
DEFAULT_ENV
|
77
46
|
end
|
47
|
+
private_class_method :default_env
|
78
48
|
|
79
|
-
|
80
|
-
|
81
|
-
# # assert that the "new" view template was rendered
|
82
|
-
# assert_template "new"
|
83
|
-
#
|
84
|
-
# # assert that the exact template "admin/posts/new" was rendered
|
85
|
-
# assert_template %r{\Aadmin/posts/new\Z}
|
86
|
-
#
|
87
|
-
# # assert that the layout 'admin' was rendered
|
88
|
-
# assert_template layout: 'admin'
|
89
|
-
# assert_template layout: 'layouts/admin'
|
90
|
-
# assert_template layout: :admin
|
91
|
-
#
|
92
|
-
# # assert that no layout was rendered
|
93
|
-
# assert_template layout: nil
|
94
|
-
# assert_template layout: false
|
95
|
-
#
|
96
|
-
# # assert that the "_customer" partial was rendered twice
|
97
|
-
# assert_template partial: '_customer', count: 2
|
98
|
-
#
|
99
|
-
# # assert that no partials were rendered
|
100
|
-
# assert_template partial: false
|
101
|
-
#
|
102
|
-
# # assert that a file was rendered
|
103
|
-
# assert_template file: "README.rdoc"
|
104
|
-
#
|
105
|
-
# # assert that no file was rendered
|
106
|
-
# assert_template file: nil
|
107
|
-
# assert_template file: false
|
108
|
-
#
|
109
|
-
# In a view test case, you can also assert that specific locals are passed
|
110
|
-
# to partials:
|
111
|
-
#
|
112
|
-
# # assert that the "_customer" partial was rendered with a specific object
|
113
|
-
# assert_template partial: '_customer', locals: { customer: @customer }
|
114
|
-
def assert_template(options = {}, message = nil)
|
115
|
-
# Force body to be read in case the template is being streamed.
|
116
|
-
response.body
|
117
|
-
|
118
|
-
case options
|
119
|
-
when NilClass, Regexp, String, Symbol
|
120
|
-
options = options.to_s if Symbol === options
|
121
|
-
rendered = @_templates
|
122
|
-
msg = message || sprintf("expecting <%s> but rendering with <%s>",
|
123
|
-
options.inspect, rendered.keys)
|
124
|
-
matches_template =
|
125
|
-
case options
|
126
|
-
when String
|
127
|
-
!options.empty? && rendered.any? do |t, num|
|
128
|
-
options_splited = options.split(File::SEPARATOR)
|
129
|
-
t_splited = t.split(File::SEPARATOR)
|
130
|
-
t_splited.last(options_splited.size) == options_splited
|
131
|
-
end
|
132
|
-
when Regexp
|
133
|
-
rendered.any? { |t,num| t.match(options) }
|
134
|
-
when NilClass
|
135
|
-
rendered.blank?
|
136
|
-
end
|
137
|
-
assert matches_template, msg
|
138
|
-
when Hash
|
139
|
-
options.assert_valid_keys(:layout, :partial, :locals, :count, :file)
|
140
|
-
|
141
|
-
if options.key?(:layout)
|
142
|
-
expected_layout = options[:layout]
|
143
|
-
msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
|
144
|
-
expected_layout, @_layouts.keys)
|
145
|
-
|
146
|
-
case expected_layout
|
147
|
-
when String, Symbol
|
148
|
-
assert_includes @_layouts.keys, expected_layout.to_s, msg
|
149
|
-
when Regexp
|
150
|
-
assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg)
|
151
|
-
when nil, false
|
152
|
-
assert(@_layouts.empty?, msg)
|
153
|
-
end
|
154
|
-
end
|
49
|
+
def initialize(env, session)
|
50
|
+
super(env)
|
155
51
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
if expected_partial = options[:partial]
|
163
|
-
if expected_locals = options[:locals]
|
164
|
-
if defined?(@_rendered_views)
|
165
|
-
view = expected_partial.to_s.sub(/^_/, '').sub(/\/_(?=[^\/]+\z)/, '/')
|
166
|
-
|
167
|
-
partial_was_not_rendered_msg = "expected %s to be rendered but it was not." % view
|
168
|
-
assert_includes @_rendered_views.rendered_views, view, partial_was_not_rendered_msg
|
169
|
-
|
170
|
-
msg = 'expecting %s to be rendered with %s but was with %s' % [expected_partial,
|
171
|
-
expected_locals,
|
172
|
-
@_rendered_views.locals_for(view)]
|
173
|
-
assert(@_rendered_views.view_rendered?(view, options[:locals]), msg)
|
174
|
-
else
|
175
|
-
warn "the :locals option to #assert_template is only supported in a ActionView::TestCase"
|
176
|
-
end
|
177
|
-
elsif expected_count = options[:count]
|
178
|
-
actual_count = @_partials[expected_partial]
|
179
|
-
msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
|
180
|
-
expected_partial, expected_count, actual_count)
|
181
|
-
assert(actual_count == expected_count.to_i, msg)
|
182
|
-
else
|
183
|
-
msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
|
184
|
-
options[:partial], @_partials.keys)
|
185
|
-
assert_includes @_partials, expected_partial, msg
|
186
|
-
end
|
187
|
-
elsif options.key?(:partial)
|
188
|
-
assert @_partials.empty?,
|
189
|
-
"Expected no partials to be rendered"
|
190
|
-
end
|
191
|
-
else
|
192
|
-
raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil"
|
193
|
-
end
|
52
|
+
self.session = session
|
53
|
+
self.session_options = TestSession::DEFAULT_OPTIONS
|
54
|
+
@custom_param_parsers = {
|
55
|
+
xml: lambda { |raw_post| Hash.from_xml(raw_post)['hash'] }
|
56
|
+
}
|
194
57
|
end
|
195
|
-
end
|
196
|
-
|
197
|
-
class TestRequest < ActionDispatch::TestRequest #:nodoc:
|
198
|
-
DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
|
199
|
-
DEFAULT_ENV.delete 'PATH_INFO'
|
200
58
|
|
201
|
-
def
|
202
|
-
|
59
|
+
def query_string=(string)
|
60
|
+
set_header Rack::QUERY_STRING, string
|
61
|
+
end
|
203
62
|
|
204
|
-
|
205
|
-
|
63
|
+
def content_type=(type)
|
64
|
+
set_header 'CONTENT_TYPE', type
|
206
65
|
end
|
207
66
|
|
208
|
-
def assign_parameters(routes, controller_path, action, parameters
|
209
|
-
|
210
|
-
|
211
|
-
non_path_parameters = get? ? query_parameters : request_parameters
|
212
|
-
parameters.each do |key, value|
|
213
|
-
if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?))
|
214
|
-
value = value.map{ |v| v.duplicable? ? v.dup : v }
|
215
|
-
elsif value.is_a?(Hash) && (value.frozen? || value.any?{ |k,v| v.frozen? })
|
216
|
-
value = Hash[value.map{ |k,v| [k, v.duplicable? ? v.dup : v] }]
|
217
|
-
elsif value.frozen? && value.duplicable?
|
218
|
-
value = value.dup
|
219
|
-
end
|
67
|
+
def assign_parameters(routes, controller_path, action, parameters, generated_path, query_string_keys)
|
68
|
+
non_path_parameters = {}
|
69
|
+
path_parameters = {}
|
220
70
|
|
221
|
-
|
71
|
+
parameters.each do |key, value|
|
72
|
+
if query_string_keys.include?(key)
|
222
73
|
non_path_parameters[key] = value
|
223
74
|
else
|
224
75
|
if value.is_a?(Array)
|
@@ -231,72 +82,88 @@ module ActionController
|
|
231
82
|
end
|
232
83
|
end
|
233
84
|
|
234
|
-
|
235
|
-
|
85
|
+
if get?
|
86
|
+
if self.query_string.blank?
|
87
|
+
self.query_string = non_path_parameters.to_query
|
88
|
+
end
|
89
|
+
else
|
90
|
+
if ENCODER.should_multipart?(non_path_parameters)
|
91
|
+
self.content_type = ENCODER.content_type
|
92
|
+
data = ENCODER.build_multipart non_path_parameters
|
93
|
+
else
|
94
|
+
fetch_header('CONTENT_TYPE') do |k|
|
95
|
+
set_header k, 'application/x-www-form-urlencoded'
|
96
|
+
end
|
236
97
|
|
237
|
-
|
238
|
-
|
98
|
+
case content_mime_type.to_sym
|
99
|
+
when nil
|
100
|
+
raise "Unknown Content-Type: #{content_type}"
|
101
|
+
when :json
|
102
|
+
data = ActiveSupport::JSON.encode(non_path_parameters)
|
103
|
+
when :xml
|
104
|
+
data = non_path_parameters.to_xml
|
105
|
+
when :url_encoded_form
|
106
|
+
data = non_path_parameters.to_query
|
107
|
+
else
|
108
|
+
@custom_param_parsers[content_mime_type.symbol] = ->(_) { non_path_parameters }
|
109
|
+
data = non_path_parameters.to_query
|
110
|
+
end
|
111
|
+
end
|
239
112
|
|
240
|
-
|
241
|
-
|
242
|
-
params.delete(k)
|
243
|
-
params.delete(k.to_sym)
|
113
|
+
set_header 'CONTENT_LENGTH', data.length.to_s
|
114
|
+
set_header 'rack.input', StringIO.new(data)
|
244
115
|
end
|
245
|
-
data = params.to_query
|
246
116
|
|
247
|
-
|
248
|
-
|
249
|
-
|
117
|
+
fetch_header("PATH_INFO") do |k|
|
118
|
+
set_header k, generated_path
|
119
|
+
end
|
120
|
+
path_parameters[:controller] = controller_path
|
121
|
+
path_parameters[:action] = action
|
250
122
|
|
251
|
-
|
252
|
-
@formats = nil
|
253
|
-
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
|
254
|
-
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
|
255
|
-
@method = @request_method = nil
|
256
|
-
@fullpath = @ip = @remote_ip = @protocol = nil
|
257
|
-
@env['action_dispatch.request.query_parameters'] = {}
|
258
|
-
@set_cookies ||= {}
|
259
|
-
@set_cookies.update(Hash[cookie_jar.instance_variable_get("@set_cookies").map{ |k,o| [k,o[:value]] }])
|
260
|
-
deleted_cookies = cookie_jar.instance_variable_get("@delete_cookies")
|
261
|
-
@set_cookies.reject!{ |k,v| deleted_cookies.include?(k) }
|
262
|
-
cookie_jar.update(rack_cookies)
|
263
|
-
cookie_jar.update(cookies)
|
264
|
-
cookie_jar.update(@set_cookies)
|
265
|
-
cookie_jar.recycle!
|
123
|
+
self.path_parameters = path_parameters
|
266
124
|
end
|
267
125
|
|
268
|
-
|
126
|
+
ENCODER = Class.new do
|
127
|
+
include Rack::Test::Utils
|
128
|
+
|
129
|
+
def should_multipart?(params)
|
130
|
+
# FIXME: lifted from Rack-Test. We should push this separation upstream
|
131
|
+
multipart = false
|
132
|
+
query = lambda { |value|
|
133
|
+
case value
|
134
|
+
when Array
|
135
|
+
value.each(&query)
|
136
|
+
when Hash
|
137
|
+
value.values.each(&query)
|
138
|
+
when Rack::Test::UploadedFile
|
139
|
+
multipart = true
|
140
|
+
end
|
141
|
+
}
|
142
|
+
params.values.each(&query)
|
143
|
+
multipart
|
144
|
+
end
|
269
145
|
|
270
|
-
|
271
|
-
DEFAULT_ENV
|
272
|
-
end
|
273
|
-
end
|
146
|
+
public :build_multipart
|
274
147
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
end
|
279
|
-
end
|
148
|
+
def content_type
|
149
|
+
"multipart/form-data; boundary=#{Rack::Test::MULTIPART_BOUNDARY}"
|
150
|
+
end
|
151
|
+
end.new
|
280
152
|
|
281
|
-
|
282
|
-
def recycle!
|
283
|
-
@body = nil
|
284
|
-
initialize
|
285
|
-
end
|
153
|
+
private
|
286
154
|
|
287
|
-
def
|
288
|
-
@
|
155
|
+
def params_parsers
|
156
|
+
super.merge @custom_param_parsers
|
289
157
|
end
|
158
|
+
end
|
290
159
|
|
160
|
+
class LiveTestResponse < Live::Response
|
291
161
|
# Was the response successful?
|
292
162
|
alias_method :success?, :successful?
|
293
163
|
|
294
164
|
# Was the URL not found?
|
295
165
|
alias_method :missing?, :not_found?
|
296
166
|
|
297
|
-
# Were we redirected?
|
298
|
-
alias_method :redirect?, :redirection?
|
299
|
-
|
300
167
|
# Was there a server-side error?
|
301
168
|
alias_method :error?, :server_error?
|
302
169
|
end
|
@@ -304,7 +171,7 @@ module ActionController
|
|
304
171
|
# Methods #destroy and #load! are overridden to avoid calling methods on the
|
305
172
|
# @store object, which does not exist for the TestSession class.
|
306
173
|
class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
|
307
|
-
DEFAULT_OPTIONS = Rack::Session::Abstract::
|
174
|
+
DEFAULT_OPTIONS = Rack::Session::Abstract::Persisted::DEFAULT_OPTIONS
|
308
175
|
|
309
176
|
def initialize(session = {})
|
310
177
|
super(nil, nil)
|
@@ -359,13 +226,13 @@ module ActionController
|
|
359
226
|
# class BooksControllerTest < ActionController::TestCase
|
360
227
|
# def test_create
|
361
228
|
# # Simulate a POST response with the given HTTP parameters.
|
362
|
-
# post(:create, book: { title: "Love Hina" })
|
229
|
+
# post(:create, params: { book: { title: "Love Hina" }})
|
363
230
|
#
|
364
|
-
# #
|
231
|
+
# # Asserts that the controller tried to redirect us to
|
365
232
|
# # the created book's URI.
|
366
233
|
# assert_response :found
|
367
234
|
#
|
368
|
-
# #
|
235
|
+
# # Asserts that the controller really put the book in the database.
|
369
236
|
# assert_not_nil Book.find_by(title: "Love Hina")
|
370
237
|
# end
|
371
238
|
# end
|
@@ -389,7 +256,7 @@ module ActionController
|
|
389
256
|
# request. You can modify this object before sending the HTTP request. For example,
|
390
257
|
# you might want to set some session properties before sending a GET request.
|
391
258
|
# <b>@response</b>::
|
392
|
-
# An
|
259
|
+
# An ActionDispatch::TestResponse object, representing the response
|
393
260
|
# of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
|
394
261
|
# after calling +post+. If the various assert methods are not sufficient, then you
|
395
262
|
# may use this object to inspect the HTTP response in detail.
|
@@ -412,21 +279,15 @@ module ActionController
|
|
412
279
|
# In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
|
413
280
|
# can be used against. These collections are:
|
414
281
|
#
|
415
|
-
# * assigns: Instance variables assigned in the action that are available for the view.
|
416
282
|
# * session: Objects being saved in the session.
|
417
283
|
# * flash: The flash objects currently in the session.
|
418
284
|
# * cookies: \Cookies being sent to the user on this request.
|
419
285
|
#
|
420
286
|
# These collections can be used just like any other hash:
|
421
287
|
#
|
422
|
-
# assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
|
423
288
|
# assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
|
424
289
|
# assert flash.empty? # makes sure that there's nothing in the flash
|
425
290
|
#
|
426
|
-
# For historic reasons, the assigns hash uses string-based keys. So <tt>assigns[:person]</tt> won't work, but <tt>assigns["person"]</tt> will. To
|
427
|
-
# appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing.
|
428
|
-
# So <tt>assigns(:person)</tt> will work just like <tt>assigns["person"]</tt>, but again, <tt>assigns[:person]</tt> will not work.
|
429
|
-
#
|
430
291
|
# On top of the collections, you have the complete url that a given action redirected to available in <tt>redirect_to_url</tt>.
|
431
292
|
#
|
432
293
|
# For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
|
@@ -499,169 +360,228 @@ module ActionController
|
|
499
360
|
# Simulate a GET request with the given parameters.
|
500
361
|
#
|
501
362
|
# - +action+: The controller action to call.
|
502
|
-
# - +
|
503
|
-
#
|
363
|
+
# - +params+: The hash with HTTP parameters that you want to pass. This may be +nil+.
|
364
|
+
# - +body+: The request body with a string that is appropriately encoded
|
504
365
|
# (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
|
505
366
|
# - +session+: A hash of parameters to store in the session. This may be +nil+.
|
506
367
|
# - +flash+: A hash of parameters to store in the flash. This may be +nil+.
|
507
368
|
#
|
508
369
|
# You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
|
509
370
|
# +post+, +patch+, +put+, +delete+, and +head+.
|
371
|
+
# Example sending parameters, session and setting a flash message:
|
372
|
+
#
|
373
|
+
# get :show,
|
374
|
+
# params: { id: 7 },
|
375
|
+
# session: { user_id: 1 },
|
376
|
+
# flash: { notice: 'This is flash message' }
|
510
377
|
#
|
511
378
|
# Note that the request method is not verified. The different methods are
|
512
379
|
# available to make the tests more expressive.
|
513
380
|
def get(action, *args)
|
514
|
-
|
381
|
+
res = process_with_kwargs("GET", action, *args)
|
382
|
+
cookies.update res.cookies
|
383
|
+
res
|
515
384
|
end
|
516
385
|
|
517
386
|
# Simulate a POST request with the given parameters and set/volley the response.
|
518
387
|
# See +get+ for more details.
|
519
388
|
def post(action, *args)
|
520
|
-
|
389
|
+
process_with_kwargs("POST", action, *args)
|
521
390
|
end
|
522
391
|
|
523
392
|
# Simulate a PATCH request with the given parameters and set/volley the response.
|
524
393
|
# See +get+ for more details.
|
525
394
|
def patch(action, *args)
|
526
|
-
|
395
|
+
process_with_kwargs("PATCH", action, *args)
|
527
396
|
end
|
528
397
|
|
529
398
|
# Simulate a PUT request with the given parameters and set/volley the response.
|
530
399
|
# See +get+ for more details.
|
531
400
|
def put(action, *args)
|
532
|
-
|
401
|
+
process_with_kwargs("PUT", action, *args)
|
533
402
|
end
|
534
403
|
|
535
404
|
# Simulate a DELETE request with the given parameters and set/volley the response.
|
536
405
|
# See +get+ for more details.
|
537
406
|
def delete(action, *args)
|
538
|
-
|
407
|
+
process_with_kwargs("DELETE", action, *args)
|
539
408
|
end
|
540
409
|
|
541
410
|
# Simulate a HEAD request with the given parameters and set/volley the response.
|
542
411
|
# See +get+ for more details.
|
543
412
|
def head(action, *args)
|
544
|
-
|
413
|
+
process_with_kwargs("HEAD", action, *args)
|
545
414
|
end
|
546
415
|
|
547
|
-
def xml_http_request(
|
416
|
+
def xml_http_request(*args)
|
417
|
+
ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
|
418
|
+
xhr and xml_http_request methods are deprecated in favor of
|
419
|
+
`get :index, xhr: true` and `post :create, xhr: true`
|
420
|
+
MSG
|
421
|
+
|
548
422
|
@request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
|
549
|
-
@request.env['HTTP_ACCEPT'] ||=
|
550
|
-
__send__(
|
423
|
+
@request.env['HTTP_ACCEPT'] ||= [Mime[:js], Mime[:html], Mime[:xml], 'text/xml', '*/*'].join(', ')
|
424
|
+
__send__(*args).tap do
|
551
425
|
@request.env.delete 'HTTP_X_REQUESTED_WITH'
|
552
426
|
@request.env.delete 'HTTP_ACCEPT'
|
553
427
|
end
|
554
428
|
end
|
555
429
|
alias xhr :xml_http_request
|
556
430
|
|
557
|
-
|
558
|
-
case hash_or_array_or_value
|
559
|
-
when Hash
|
560
|
-
Hash[hash_or_array_or_value.map{|key, value| [key, paramify_values(value)] }]
|
561
|
-
when Array
|
562
|
-
hash_or_array_or_value.map {|i| paramify_values(i)}
|
563
|
-
when Rack::Test::UploadedFile, ActionDispatch::Http::UploadedFile
|
564
|
-
hash_or_array_or_value
|
565
|
-
else
|
566
|
-
hash_or_array_or_value.to_param
|
567
|
-
end
|
568
|
-
end
|
569
|
-
|
570
|
-
# Simulate a HTTP request to +action+ by specifying request method,
|
431
|
+
# Simulate an HTTP request to +action+ by specifying request method,
|
571
432
|
# parameters and set/volley the response.
|
572
433
|
#
|
573
434
|
# - +action+: The controller action to call.
|
574
|
-
# - +
|
575
|
-
# are +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, +HEAD+. Defaults to +GET+.
|
576
|
-
# - +
|
577
|
-
#
|
578
|
-
# or
|
435
|
+
# - +method+: Request method used to send the HTTP request. Possible values
|
436
|
+
# are +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, +HEAD+. Defaults to +GET+. Can be a symbol.
|
437
|
+
# - +params+: The hash with HTTP parameters that you want to pass. This may be +nil+.
|
438
|
+
# - +body+: The request body with a string that is appropriately encoded
|
439
|
+
# (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
|
579
440
|
# - +session+: A hash of parameters to store in the session. This may be +nil+.
|
580
441
|
# - +flash+: A hash of parameters to store in the flash. This may be +nil+.
|
442
|
+
# - +format+: Request format. Defaults to +nil+. Can be string or symbol.
|
581
443
|
#
|
582
444
|
# Example calling +create+ action and sending two params:
|
583
445
|
#
|
584
|
-
# process :create,
|
585
|
-
#
|
586
|
-
#
|
587
|
-
#
|
588
|
-
#
|
446
|
+
# process :create,
|
447
|
+
# method: 'POST',
|
448
|
+
# params: {
|
449
|
+
# user: { name: 'Gaurish Sharma', email: 'user@example.com' }
|
450
|
+
# },
|
451
|
+
# session: { user_id: 1 },
|
452
|
+
# flash: { notice: 'This is flash message' }
|
589
453
|
#
|
590
454
|
# To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+ and +HEAD+ requests
|
591
455
|
# prefer using #get, #post, #patch, #put, #delete and #head methods
|
592
456
|
# respectively which will make tests more expressive.
|
593
457
|
#
|
594
458
|
# Note that the request method is not verified.
|
595
|
-
def process(action,
|
459
|
+
def process(action, *args)
|
596
460
|
check_required_ivars
|
597
461
|
|
598
|
-
if
|
599
|
-
|
462
|
+
if kwarg_request?(args)
|
463
|
+
parameters, session, body, flash, http_method, format, xhr = args[0].values_at(:params, :session, :body, :flash, :method, :format, :xhr)
|
464
|
+
else
|
465
|
+
http_method, parameters, session, flash = args
|
466
|
+
format = nil
|
467
|
+
|
468
|
+
if parameters.is_a?(String) && http_method != 'HEAD'
|
469
|
+
body = parameters
|
470
|
+
parameters = nil
|
471
|
+
end
|
472
|
+
|
473
|
+
if parameters || session || flash
|
474
|
+
non_kwarg_request_warning
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
if body
|
479
|
+
@request.set_header 'RAW_POST_DATA', body
|
480
|
+
end
|
481
|
+
|
482
|
+
if http_method
|
483
|
+
http_method = http_method.to_s.upcase
|
484
|
+
else
|
485
|
+
http_method = "GET"
|
600
486
|
end
|
601
487
|
|
602
|
-
parameters, session, flash = args
|
603
488
|
parameters ||= {}
|
604
489
|
|
605
|
-
|
606
|
-
|
607
|
-
|
490
|
+
if format
|
491
|
+
parameters[:format] = format
|
492
|
+
end
|
608
493
|
|
609
494
|
@html_document = nil
|
610
|
-
@html_scanner_document = nil
|
611
495
|
|
612
|
-
|
613
|
-
|
614
|
-
|
496
|
+
self.cookies.update @request.cookies
|
497
|
+
self.cookies.update_cookies_from_jar
|
498
|
+
@request.set_header 'HTTP_COOKIE', cookies.to_header
|
499
|
+
@request.delete_header 'action_dispatch.cookies'
|
615
500
|
|
616
|
-
@request.
|
617
|
-
@response
|
501
|
+
@request = TestRequest.new scrub_env!(@request.env), @request.session
|
502
|
+
@response = build_response @response_klass
|
503
|
+
@response.request = @request
|
618
504
|
@controller.recycle!
|
619
505
|
|
620
|
-
@request.
|
506
|
+
@request.set_header 'REQUEST_METHOD', http_method
|
621
507
|
|
622
|
-
|
623
|
-
"anonymous" :
|
624
|
-
@controller.class.controller_path
|
508
|
+
parameters = parameters.symbolize_keys
|
625
509
|
|
626
|
-
@
|
510
|
+
generated_extras = @routes.generate_extras(parameters.merge(controller: controller_class_name, action: action.to_s))
|
511
|
+
generated_path = generated_path(generated_extras)
|
512
|
+
query_string_keys = query_parameter_names(generated_extras)
|
513
|
+
|
514
|
+
@request.assign_parameters(@routes, controller_class_name, action.to_s, parameters, generated_path, query_string_keys)
|
627
515
|
|
628
516
|
@request.session.update(session) if session
|
629
517
|
@request.flash.update(flash || {})
|
630
518
|
|
631
|
-
|
632
|
-
|
519
|
+
if xhr
|
520
|
+
@request.set_header 'HTTP_X_REQUESTED_WITH', 'XMLHttpRequest'
|
521
|
+
@request.fetch_header('HTTP_ACCEPT') do |k|
|
522
|
+
@request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], 'text/xml', '*/*'].join(', ')
|
523
|
+
end
|
524
|
+
end
|
633
525
|
|
634
|
-
|
526
|
+
@request.fetch_header("SCRIPT_NAME") do |k|
|
527
|
+
@request.set_header k, @controller.config.relative_url_root
|
528
|
+
end
|
635
529
|
|
636
|
-
|
530
|
+
begin
|
531
|
+
@controller.recycle!
|
532
|
+
@controller.dispatch(action, @request, @response)
|
533
|
+
ensure
|
534
|
+
@request = @controller.request
|
535
|
+
@response = @controller.response
|
637
536
|
|
638
|
-
|
639
|
-
@controller.process(name)
|
537
|
+
@request.delete_header 'HTTP_COOKIE'
|
640
538
|
|
641
|
-
|
642
|
-
|
643
|
-
|
539
|
+
if @request.have_cookie_jar?
|
540
|
+
unless @request.cookie_jar.committed?
|
541
|
+
@request.cookie_jar.write(@response)
|
542
|
+
self.cookies.update(@request.cookie_jar.instance_variable_get(:@cookies))
|
543
|
+
end
|
644
544
|
end
|
645
|
-
|
646
|
-
@response.prepare!
|
545
|
+
@response.prepare!
|
647
546
|
|
648
|
-
|
547
|
+
if flash_value = @request.flash.to_session_value
|
548
|
+
@request.session['flash'] = flash_value
|
549
|
+
else
|
550
|
+
@request.session.delete('flash')
|
551
|
+
end
|
649
552
|
|
650
|
-
|
651
|
-
|
553
|
+
if xhr
|
554
|
+
@request.delete_header 'HTTP_X_REQUESTED_WITH'
|
555
|
+
@request.delete_header 'HTTP_ACCEPT'
|
556
|
+
end
|
557
|
+
@request.query_string = ''
|
558
|
+
|
559
|
+
@response.sent!
|
652
560
|
end
|
653
561
|
|
654
562
|
@response
|
655
563
|
end
|
656
564
|
|
565
|
+
def controller_class_name
|
566
|
+
@controller.class.anonymous? ? "anonymous" : @controller.class.controller_path
|
567
|
+
end
|
568
|
+
|
569
|
+
def generated_path(generated_extras)
|
570
|
+
generated_extras[0]
|
571
|
+
end
|
572
|
+
|
573
|
+
def query_parameter_names(generated_extras)
|
574
|
+
generated_extras[1] + [:controller, :action]
|
575
|
+
end
|
576
|
+
|
657
577
|
def setup_controller_request_and_response
|
658
578
|
@controller = nil unless defined? @controller
|
659
579
|
|
660
|
-
response_klass = TestResponse
|
580
|
+
@response_klass = ActionDispatch::TestResponse
|
661
581
|
|
662
582
|
if klass = self.class.controller_class
|
663
583
|
if klass < ActionController::Live
|
664
|
-
response_klass = LiveTestResponse
|
584
|
+
@response_klass = LiveTestResponse
|
665
585
|
end
|
666
586
|
unless @controller
|
667
587
|
begin
|
@@ -672,8 +592,8 @@ module ActionController
|
|
672
592
|
end
|
673
593
|
end
|
674
594
|
|
675
|
-
@request =
|
676
|
-
@response = build_response response_klass
|
595
|
+
@request = TestRequest.create
|
596
|
+
@response = build_response @response_klass
|
677
597
|
@response.request = @request
|
678
598
|
|
679
599
|
if @controller
|
@@ -682,12 +602,8 @@ module ActionController
|
|
682
602
|
end
|
683
603
|
end
|
684
604
|
|
685
|
-
def build_request
|
686
|
-
TestRequest.new
|
687
|
-
end
|
688
|
-
|
689
605
|
def build_response(klass)
|
690
|
-
klass.
|
606
|
+
klass.create
|
691
607
|
end
|
692
608
|
|
693
609
|
included do
|
@@ -699,6 +615,46 @@ module ActionController
|
|
699
615
|
|
700
616
|
private
|
701
617
|
|
618
|
+
def scrub_env!(env)
|
619
|
+
env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
|
620
|
+
env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
|
621
|
+
env.delete 'action_dispatch.request.query_parameters'
|
622
|
+
env.delete 'action_dispatch.request.request_parameters'
|
623
|
+
env
|
624
|
+
end
|
625
|
+
|
626
|
+
def process_with_kwargs(http_method, action, *args)
|
627
|
+
if kwarg_request?(args)
|
628
|
+
args.first.merge!(method: http_method)
|
629
|
+
process(action, *args)
|
630
|
+
else
|
631
|
+
non_kwarg_request_warning if args.any?
|
632
|
+
|
633
|
+
args = args.unshift(http_method)
|
634
|
+
process(action, *args)
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
638
|
+
REQUEST_KWARGS = %i(params session flash method body xhr)
|
639
|
+
def kwarg_request?(args)
|
640
|
+
args[0].respond_to?(:keys) && (
|
641
|
+
(args[0].key?(:format) && args[0].keys.size == 1) ||
|
642
|
+
args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) }
|
643
|
+
)
|
644
|
+
end
|
645
|
+
|
646
|
+
def non_kwarg_request_warning
|
647
|
+
ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
|
648
|
+
ActionController::TestCase HTTP request methods will accept only
|
649
|
+
keyword arguments in future Rails versions.
|
650
|
+
|
651
|
+
Examples:
|
652
|
+
|
653
|
+
get :show, params: { id: 1 }, session: { user_id: 1 }
|
654
|
+
process :update, method: :post, params: { id: 1 }
|
655
|
+
MSG
|
656
|
+
end
|
657
|
+
|
702
658
|
def document_root_element
|
703
659
|
html_document.root
|
704
660
|
end
|
@@ -713,43 +669,6 @@ module ActionController
|
|
713
669
|
end
|
714
670
|
end
|
715
671
|
|
716
|
-
def build_request_uri(action, parameters)
|
717
|
-
unless @request.env["PATH_INFO"]
|
718
|
-
options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters
|
719
|
-
options.update(
|
720
|
-
:action => action,
|
721
|
-
:relative_url_root => nil,
|
722
|
-
:_recall => @request.path_parameters)
|
723
|
-
|
724
|
-
if route_name = options.delete(:use_route)
|
725
|
-
ActiveSupport::Deprecation.warn <<-MSG.squish
|
726
|
-
Passing the `use_route` option in functional tests are deprecated.
|
727
|
-
Support for this option in the `process` method (and the related
|
728
|
-
`get`, `head`, `post`, `patch`, `put` and `delete` helpers) will
|
729
|
-
be removed in the next version without replacement.
|
730
|
-
|
731
|
-
Functional tests are essentially unit tests for controllers and
|
732
|
-
they should not require knowledge to how the application's routes
|
733
|
-
are configured. Instead, you should explicitly pass the appropiate
|
734
|
-
params to the `process` method.
|
735
|
-
|
736
|
-
Previously the engines guide also contained an incorrect example
|
737
|
-
that recommended using this option to test an engine's controllers
|
738
|
-
within the dummy application. That recommendation was incorrect
|
739
|
-
and has since been corrected. Instead, you should override the
|
740
|
-
`@routes` variable in the test case with `Foo::Engine.routes`. See
|
741
|
-
the updated engines guide for details.
|
742
|
-
MSG
|
743
|
-
end
|
744
|
-
|
745
|
-
url, query_string = @routes.path_for(options, route_name).split("?", 2)
|
746
|
-
|
747
|
-
@request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
|
748
|
-
@request.env["PATH_INFO"] = url
|
749
|
-
@request.env["QUERY_STRING"] = query_string || ""
|
750
|
-
end
|
751
|
-
end
|
752
|
-
|
753
672
|
def html_format?(parameters)
|
754
673
|
return true unless parameters.key?(:format)
|
755
674
|
Mime.fetch(parameters[:format]) { Mime['html'] }.html?
|
@@ -758,4 +677,5 @@ module ActionController
|
|
758
677
|
|
759
678
|
include Behavior
|
760
679
|
end
|
680
|
+
# :startdoc:
|
761
681
|
end
|