actionpack 2.1.2 → 2.2.2
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 +223 -7
- data/README +6 -12
- data/Rakefile +11 -11
- data/lib/action_controller.rb +9 -9
- data/lib/action_controller/assertions/response_assertions.rb +29 -78
- data/lib/action_controller/assertions/routing_assertions.rb +33 -33
- data/lib/action_controller/assertions/selector_assertions.rb +9 -5
- data/lib/action_controller/base.rb +227 -161
- data/lib/action_controller/benchmarking.rb +37 -24
- data/lib/action_controller/caching/actions.rb +53 -21
- data/lib/action_controller/caching/fragments.rb +10 -36
- data/lib/action_controller/caching/sweeping.rb +3 -3
- data/lib/action_controller/cgi_ext/session.rb +2 -22
- data/lib/action_controller/cgi_process.rb +8 -46
- data/lib/action_controller/components.rb +4 -1
- data/lib/action_controller/cookies.rb +10 -0
- data/lib/action_controller/dispatcher.rb +49 -15
- data/lib/action_controller/filters.rb +48 -10
- data/lib/action_controller/headers.rb +16 -14
- data/lib/action_controller/helpers.rb +2 -2
- data/lib/action_controller/http_authentication.rb +1 -1
- data/lib/action_controller/integration.rb +57 -60
- data/lib/action_controller/layout.rb +27 -53
- data/lib/action_controller/mime_responds.rb +5 -1
- data/lib/action_controller/mime_type.rb +64 -42
- data/lib/action_controller/mime_types.rb +2 -1
- data/lib/action_controller/performance_test.rb +16 -0
- data/lib/action_controller/polymorphic_routes.rb +16 -9
- data/lib/action_controller/rack_process.rb +303 -0
- data/lib/action_controller/request.rb +205 -97
- data/lib/action_controller/request_forgery_protection.rb +2 -2
- data/lib/action_controller/request_profiler.rb +0 -0
- data/lib/action_controller/rescue.rb +20 -115
- data/lib/action_controller/resources.rb +186 -83
- data/lib/action_controller/response.rb +140 -26
- data/lib/action_controller/routing.rb +28 -30
- data/lib/action_controller/routing/builder.rb +45 -54
- data/lib/action_controller/routing/optimisations.rb +31 -21
- data/lib/action_controller/routing/recognition_optimisation.rb +33 -27
- data/lib/action_controller/routing/route.rb +162 -147
- data/lib/action_controller/routing/route_set.rb +8 -7
- data/lib/action_controller/routing/routing_ext.rb +4 -1
- data/lib/action_controller/routing/segments.rb +50 -21
- data/lib/action_controller/session/cookie_store.rb +3 -2
- data/lib/action_controller/session/drb_server.rb +7 -7
- data/lib/action_controller/session_management.rb +6 -2
- data/lib/action_controller/streaming.rb +15 -8
- data/lib/action_controller/templates/rescues/diagnostics.erb +2 -2
- data/lib/action_controller/templates/rescues/template_error.erb +2 -2
- data/lib/action_controller/test_case.rb +66 -2
- data/lib/action_controller/test_process.rb +71 -66
- data/lib/action_controller/translation.rb +13 -0
- data/lib/action_controller/url_rewriter.rb +90 -13
- data/lib/action_controller/vendor/html-scanner/html/node.rb +9 -2
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +1 -1
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -2
- data/lib/action_controller/verification.rb +2 -2
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_view.rb +19 -11
- data/lib/action_view/base.rb +184 -150
- data/lib/action_view/helpers.rb +38 -0
- data/lib/action_view/helpers/active_record_helper.rb +56 -27
- data/lib/action_view/helpers/asset_tag_helper.rb +356 -153
- data/lib/action_view/helpers/atom_feed_helper.rb +74 -19
- data/lib/action_view/helpers/benchmark_helper.rb +3 -3
- data/lib/action_view/helpers/cache_helper.rb +1 -2
- data/lib/action_view/helpers/capture_helper.rb +19 -44
- data/lib/action_view/helpers/date_helper.rb +486 -296
- data/lib/action_view/helpers/debug_helper.rb +20 -13
- data/lib/action_view/helpers/form_helper.rb +71 -30
- data/lib/action_view/helpers/form_options_helper.rb +15 -85
- data/lib/action_view/helpers/form_tag_helper.rb +61 -38
- data/lib/action_view/helpers/javascript_helper.rb +80 -89
- data/lib/action_view/helpers/number_helper.rb +179 -74
- data/lib/action_view/helpers/prototype_helper.rb +216 -201
- data/lib/action_view/helpers/record_tag_helper.rb +4 -5
- data/lib/action_view/helpers/sanitize_helper.rb +65 -33
- data/lib/action_view/helpers/scriptaculous_helper.rb +2 -2
- data/lib/action_view/helpers/tag_helper.rb +39 -22
- data/lib/action_view/helpers/text_helper.rb +212 -118
- data/lib/action_view/helpers/translation_helper.rb +21 -0
- data/lib/action_view/helpers/url_helper.rb +100 -58
- data/lib/action_view/inline_template.rb +13 -14
- data/lib/action_view/locale/en.yml +91 -0
- data/lib/action_view/partials.rb +100 -55
- data/lib/action_view/paths.rb +125 -0
- data/lib/action_view/renderable.rb +102 -0
- data/lib/action_view/renderable_partial.rb +48 -0
- data/lib/action_view/template.rb +90 -101
- data/lib/action_view/template_error.rb +11 -21
- data/lib/action_view/template_handler.rb +8 -28
- data/lib/action_view/template_handlers.rb +45 -0
- data/lib/action_view/template_handlers/builder.rb +5 -15
- data/lib/action_view/template_handlers/erb.rb +9 -6
- data/lib/action_view/template_handlers/rjs.rb +2 -17
- data/lib/action_view/test_case.rb +7 -4
- data/test/abstract_unit.rb +4 -1
- data/test/active_record_unit.rb +28 -30
- data/test/activerecord/render_partial_with_record_identification_test.rb +25 -12
- data/test/controller/action_pack_assertions_test.rb +8 -37
- data/test/controller/addresses_render_test.rb +0 -3
- data/test/controller/assert_select_test.rb +51 -24
- data/test/controller/base_test.rb +4 -4
- data/test/controller/caching_test.rb +136 -66
- data/test/controller/capture_test.rb +1 -21
- data/test/controller/cgi_test.rb +157 -10
- data/test/controller/components_test.rb +41 -25
- data/test/controller/content_type_test.rb +49 -17
- data/test/controller/cookie_test.rb +1 -1
- data/test/controller/deprecation/deprecated_base_methods_test.rb +0 -3
- data/test/controller/dispatcher_test.rb +9 -1
- data/test/controller/filter_params_test.rb +2 -2
- data/test/controller/filters_test.rb +13 -13
- data/test/controller/html-scanner/cdata_node_test.rb +15 -0
- data/test/controller/html-scanner/node_test.rb +21 -0
- data/test/controller/html-scanner/sanitizer_test.rb +14 -0
- data/test/controller/integration_test.rb +167 -6
- data/test/controller/layout_test.rb +11 -68
- data/test/controller/logging_test.rb +46 -0
- data/test/controller/mime_responds_test.rb +61 -59
- data/test/controller/mime_type_test.rb +6 -6
- data/test/controller/polymorphic_routes_test.rb +37 -2
- data/test/controller/rack_test.rb +323 -0
- data/test/controller/redirect_test.rb +72 -71
- data/test/controller/render_test.rb +1120 -108
- data/test/controller/request_forgery_protection_test.rb +66 -52
- data/test/controller/request_test.rb +103 -146
- data/test/controller/rescue_test.rb +20 -24
- data/test/controller/resources_test.rb +408 -25
- data/test/controller/routing_test.rb +1774 -1774
- data/test/controller/send_file_test.rb +0 -4
- data/test/controller/session/cookie_store_test.rb +53 -1
- data/test/controller/test_test.rb +15 -37
- data/test/controller/translation_test.rb +26 -0
- data/test/controller/url_rewriter_test.rb +27 -28
- data/test/controller/view_paths_test.rb +48 -47
- data/test/fixtures/_top_level_partial.html.erb +1 -0
- data/test/fixtures/_top_level_partial_only.erb +1 -0
- data/test/fixtures/developers/_developer.erb +1 -0
- data/test/fixtures/fun/games/_game.erb +1 -0
- data/test/fixtures/fun/serious/games/_game.erb +1 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.html.erb +3 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs +6 -0
- data/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder +5 -0
- data/test/fixtures/functional_caching/inline_fragment_cached.html.erb +2 -0
- data/test/fixtures/layouts/_column.html.erb +2 -0
- data/test/fixtures/projects/_project.erb +1 -0
- data/test/fixtures/public/javascripts/subdir/subdir.js +1 -0
- data/test/fixtures/public/stylesheets/subdir/subdir.css +1 -0
- data/test/fixtures/replies/_reply.erb +1 -0
- data/test/fixtures/test/_counter.html.erb +1 -0
- data/test/fixtures/test/_customer.erb +1 -1
- data/test/fixtures/test/_customer_with_var.erb +1 -0
- data/test/fixtures/test/_layout_for_block_with_args.html.erb +3 -0
- data/test/fixtures/test/_local_inspector.html.erb +1 -0
- data/test/fixtures/test/_partial_with_only_html_version.html.erb +1 -0
- data/test/fixtures/test/hello.builder +1 -1
- data/test/fixtures/test/hyphen-ated.erb +1 -0
- data/test/fixtures/test/implicit_content_type.atom.builder +2 -0
- data/test/fixtures/test/nested_layout.erb +3 -0
- data/test/fixtures/test/non_erb_block_content_for.builder +1 -1
- data/test/fixtures/test/sub_template_raise.html.erb +1 -0
- data/test/fixtures/test/template.erb +1 -0
- data/test/fixtures/test/using_layout_around_block_with_args.html.erb +1 -0
- data/test/template/active_record_helper_i18n_test.rb +46 -0
- data/test/template/active_record_helper_test.rb +24 -24
- data/test/template/asset_tag_helper_test.rb +161 -29
- data/test/template/atom_feed_helper_test.rb +114 -5
- data/test/template/compiled_templates_test.rb +59 -0
- data/test/template/date_helper_i18n_test.rb +113 -0
- data/test/template/date_helper_test.rb +403 -109
- data/test/template/form_helper_test.rb +213 -154
- data/test/template/form_options_helper_test.rb +249 -897
- data/test/template/form_tag_helper_test.rb +80 -32
- data/test/template/javascript_helper_test.rb +17 -18
- data/test/template/number_helper_i18n_test.rb +54 -0
- data/test/template/number_helper_test.rb +43 -13
- data/test/template/prototype_helper_test.rb +101 -84
- data/test/template/record_tag_helper_test.rb +24 -20
- data/test/template/render_test.rb +193 -0
- data/test/template/sanitize_helper_test.rb +3 -3
- data/test/template/tag_helper_test.rb +34 -14
- data/test/template/text_helper_test.rb +83 -9
- data/test/template/translation_helper_test.rb +28 -0
- data/test/template/url_helper_test.rb +55 -18
- metadata +57 -18
- data/lib/action_view/helpers/javascripts/controls.js +0 -963
- data/lib/action_view/helpers/javascripts/dragdrop.js +0 -972
- data/lib/action_view/helpers/javascripts/effects.js +0 -1120
- data/lib/action_view/helpers/javascripts/prototype.js +0 -4225
- data/lib/action_view/partial_template.rb +0 -70
- data/lib/action_view/template_finder.rb +0 -177
- data/lib/action_view/template_handlers/compilable.rb +0 -128
- data/test/controller/custom_handler_test.rb +0 -45
- data/test/controller/new_render_test.rb +0 -945
- data/test/fixtures/test/block_content_for.erb +0 -2
- data/test/fixtures/test/erb_content_for.erb +0 -2
- data/test/template/deprecated_erb_variable_test.rb +0 -9
- data/test/template/template_finder_test.rb +0 -73
- data/test/template/template_object_test.rb +0 -95
@@ -3,11 +3,6 @@ module ActionController #:nodoc:
|
|
3
3
|
def self.included(base)
|
4
4
|
base.extend(ClassMethods)
|
5
5
|
base.class_eval do
|
6
|
-
# NOTE: Can't use alias_method_chain here because +render_without_layout+ is already
|
7
|
-
# defined as a publicly exposed method
|
8
|
-
alias_method :render_with_no_layout, :render
|
9
|
-
alias_method :render, :render_with_a_layout
|
10
|
-
|
11
6
|
class << self
|
12
7
|
alias_method_chain :inherited, :layout
|
13
8
|
end
|
@@ -169,17 +164,17 @@ module ActionController #:nodoc:
|
|
169
164
|
# performance and have access to them as any normal template would.
|
170
165
|
def layout(template_name, conditions = {}, auto = false)
|
171
166
|
add_layout_conditions(conditions)
|
172
|
-
write_inheritable_attribute
|
173
|
-
write_inheritable_attribute
|
167
|
+
write_inheritable_attribute(:layout, template_name)
|
168
|
+
write_inheritable_attribute(:auto_layout, auto)
|
174
169
|
end
|
175
170
|
|
176
171
|
def layout_conditions #:nodoc:
|
177
|
-
@layout_conditions ||= read_inheritable_attribute(
|
172
|
+
@layout_conditions ||= read_inheritable_attribute(:layout_conditions)
|
178
173
|
end
|
179
174
|
|
180
175
|
def default_layout(format) #:nodoc:
|
181
|
-
layout = read_inheritable_attribute(
|
182
|
-
return layout unless read_inheritable_attribute(
|
176
|
+
layout = read_inheritable_attribute(:layout)
|
177
|
+
return layout unless read_inheritable_attribute(:auto_layout)
|
183
178
|
@default_layout ||= {}
|
184
179
|
@default_layout[format] ||= default_layout_with_format(format, layout)
|
185
180
|
@default_layout[format]
|
@@ -199,7 +194,7 @@ module ActionController #:nodoc:
|
|
199
194
|
end
|
200
195
|
|
201
196
|
def add_layout_conditions(conditions)
|
202
|
-
write_inheritable_hash
|
197
|
+
write_inheritable_hash(:layout_conditions, normalize_conditions(conditions))
|
203
198
|
end
|
204
199
|
|
205
200
|
def normalize_conditions(conditions)
|
@@ -221,10 +216,10 @@ module ActionController #:nodoc:
|
|
221
216
|
# object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return
|
222
217
|
# weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
|
223
218
|
def active_layout(passed_layout = nil)
|
224
|
-
layout = passed_layout || self.class.default_layout(
|
219
|
+
layout = passed_layout || self.class.default_layout(default_template_format)
|
225
220
|
active_layout = case layout
|
226
221
|
when String then layout
|
227
|
-
when Symbol then
|
222
|
+
when Symbol then __send__(layout)
|
228
223
|
when Proc then layout.call(self)
|
229
224
|
end
|
230
225
|
|
@@ -240,51 +235,24 @@ module ActionController #:nodoc:
|
|
240
235
|
end
|
241
236
|
end
|
242
237
|
|
243
|
-
protected
|
244
|
-
def render_with_a_layout(options = nil, extra_options = {}, &block) #:nodoc:
|
245
|
-
template_with_options = options.is_a?(Hash)
|
246
|
-
|
247
|
-
if (layout = pick_layout(template_with_options, options)) && apply_layout?(template_with_options, options)
|
248
|
-
options = options.merge :layout => false if template_with_options
|
249
|
-
logger.info("Rendering template within #{layout}") if logger
|
250
|
-
|
251
|
-
content_for_layout = render_with_no_layout(options, extra_options, &block)
|
252
|
-
erase_render_results
|
253
|
-
add_variables_to_assigns
|
254
|
-
@template.instance_variable_set("@content_for_layout", content_for_layout)
|
255
|
-
response.layout = layout
|
256
|
-
status = template_with_options ? options[:status] : nil
|
257
|
-
render_for_text(@template.render_file(layout, true), status)
|
258
|
-
else
|
259
|
-
render_with_no_layout(options, extra_options, &block)
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
|
-
|
264
238
|
private
|
265
|
-
def apply_layout?(template_with_options, options)
|
266
|
-
return false if options == :update
|
267
|
-
template_with_options ? candidate_for_layout?(options) : !template_exempt_from_layout?
|
268
|
-
end
|
269
|
-
|
270
239
|
def candidate_for_layout?(options)
|
271
|
-
|
272
|
-
|
273
|
-
!template_exempt_from_layout?(options[:template] || default_template_name(options[:action]))
|
240
|
+
options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty? &&
|
241
|
+
!@template.__send__(:_exempt_from_layout?, options[:template] || default_template_name(options[:action]))
|
274
242
|
end
|
275
243
|
|
276
|
-
def pick_layout(
|
277
|
-
if
|
278
|
-
case layout = options
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
244
|
+
def pick_layout(options)
|
245
|
+
if options.has_key?(:layout)
|
246
|
+
case layout = options.delete(:layout)
|
247
|
+
when FalseClass
|
248
|
+
nil
|
249
|
+
when NilClass, TrueClass
|
250
|
+
active_layout if action_has_layout? && !@template.__send__(:_exempt_from_layout?, default_template_name)
|
251
|
+
else
|
252
|
+
active_layout(layout)
|
285
253
|
end
|
286
254
|
else
|
287
|
-
active_layout if action_has_layout?
|
255
|
+
active_layout if action_has_layout? && candidate_for_layout?(options)
|
288
256
|
end
|
289
257
|
end
|
290
258
|
|
@@ -304,7 +272,13 @@ module ActionController #:nodoc:
|
|
304
272
|
end
|
305
273
|
|
306
274
|
def layout_directory?(layout_name)
|
307
|
-
@template.
|
275
|
+
@template.__send__(:_pick_template, "#{File.join('layouts', layout_name)}.#{@template.template_format}") ? true : false
|
276
|
+
rescue ActionView::MissingTemplate
|
277
|
+
false
|
278
|
+
end
|
279
|
+
|
280
|
+
def default_template_format
|
281
|
+
response.template.template_format
|
308
282
|
end
|
309
283
|
end
|
310
284
|
end
|
@@ -114,7 +114,11 @@ module ActionController #:nodoc:
|
|
114
114
|
@request = controller.request
|
115
115
|
@response = controller.response
|
116
116
|
|
117
|
-
|
117
|
+
if ActionController::Base.use_accept_header
|
118
|
+
@mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts)
|
119
|
+
else
|
120
|
+
@mime_type_priority = [@request.format]
|
121
|
+
end
|
118
122
|
|
119
123
|
@order = []
|
120
124
|
@responses = {}
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Mime
|
2
4
|
SET = []
|
3
5
|
EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
|
@@ -18,8 +20,20 @@ module Mime
|
|
18
20
|
# end
|
19
21
|
class Type
|
20
22
|
@@html_types = Set.new [:html, :all]
|
23
|
+
cattr_reader :html_types
|
24
|
+
|
25
|
+
# These are the content types which browsers can generate without using ajax, flash, etc
|
26
|
+
# i.e. following a link, getting an image or posting a form. CSRF protection
|
27
|
+
# only needs to protect against these types.
|
28
|
+
@@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form, :text]
|
29
|
+
cattr_reader :browser_generated_types
|
30
|
+
|
31
|
+
|
21
32
|
@@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml]
|
22
|
-
|
33
|
+
def self.unverifiable_types
|
34
|
+
ActiveSupport::Deprecation.warn("unverifiable_types is deprecated and has no effect", caller)
|
35
|
+
@@unverifiable_types
|
36
|
+
end
|
23
37
|
|
24
38
|
# A simple helper class used in parsing the accept header
|
25
39
|
class AcceptItem #:nodoc:
|
@@ -72,57 +86,61 @@ module Mime
|
|
72
86
|
end
|
73
87
|
|
74
88
|
def parse(accept_header)
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
89
|
+
if accept_header !~ /,/
|
90
|
+
[Mime::Type.lookup(accept_header)]
|
91
|
+
else
|
92
|
+
# keep track of creation order to keep the subsequent sort stable
|
93
|
+
list = []
|
94
|
+
accept_header.split(/,/).each_with_index do |header, index|
|
95
|
+
params, q = header.split(/;\s*q=/)
|
96
|
+
if params
|
97
|
+
params.strip!
|
98
|
+
list << AcceptItem.new(index, params, q) unless params.empty?
|
99
|
+
end
|
82
100
|
end
|
83
|
-
|
84
|
-
list.sort!
|
101
|
+
list.sort!
|
85
102
|
|
86
|
-
|
87
|
-
|
88
|
-
|
103
|
+
# Take care of the broken text/xml entry by renaming or deleting it
|
104
|
+
text_xml = list.index("text/xml")
|
105
|
+
app_xml = list.index(Mime::XML.to_s)
|
89
106
|
|
90
|
-
|
91
|
-
|
92
|
-
|
107
|
+
if text_xml && app_xml
|
108
|
+
# set the q value to the max of the two
|
109
|
+
list[app_xml].q = [list[text_xml].q, list[app_xml].q].max
|
93
110
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
111
|
+
# make sure app_xml is ahead of text_xml in the list
|
112
|
+
if app_xml > text_xml
|
113
|
+
list[app_xml], list[text_xml] = list[text_xml], list[app_xml]
|
114
|
+
app_xml, text_xml = text_xml, app_xml
|
115
|
+
end
|
99
116
|
|
100
|
-
|
101
|
-
|
117
|
+
# delete text_xml from the list
|
118
|
+
list.delete_at(text_xml)
|
102
119
|
|
103
|
-
|
104
|
-
|
105
|
-
|
120
|
+
elsif text_xml
|
121
|
+
list[text_xml].name = Mime::XML.to_s
|
122
|
+
end
|
106
123
|
|
107
|
-
|
124
|
+
# Look for more specific XML-based types and sort them ahead of app/xml
|
108
125
|
|
109
|
-
|
110
|
-
|
111
|
-
|
126
|
+
if app_xml
|
127
|
+
idx = app_xml
|
128
|
+
app_xml_type = list[app_xml]
|
112
129
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
130
|
+
while(idx < list.length)
|
131
|
+
type = list[idx]
|
132
|
+
break if type.q < app_xml_type.q
|
133
|
+
if type.name =~ /\+xml$/
|
134
|
+
list[app_xml], list[idx] = list[idx], list[app_xml]
|
135
|
+
app_xml = idx
|
136
|
+
end
|
137
|
+
idx += 1
|
119
138
|
end
|
120
|
-
idx += 1
|
121
139
|
end
|
122
|
-
end
|
123
140
|
|
124
|
-
|
125
|
-
|
141
|
+
list.map! { |i| Mime::Type.lookup(i.name) }.uniq!
|
142
|
+
list
|
143
|
+
end
|
126
144
|
end
|
127
145
|
end
|
128
146
|
|
@@ -159,15 +177,19 @@ module Mime
|
|
159
177
|
end
|
160
178
|
|
161
179
|
# Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
|
162
|
-
# ActionController::
|
180
|
+
# ActionController::RequestForgeryProtection.
|
163
181
|
def verify_request?
|
164
|
-
|
182
|
+
browser_generated?
|
165
183
|
end
|
166
184
|
|
167
185
|
def html?
|
168
186
|
@@html_types.include?(to_sym) || @string =~ /html/
|
169
187
|
end
|
170
188
|
|
189
|
+
def browser_generated?
|
190
|
+
@@browser_generated_types.include?(to_sym)
|
191
|
+
end
|
192
|
+
|
171
193
|
private
|
172
194
|
def method_missing(method, *args)
|
173
195
|
if method.to_s =~ /(\w+)\?$/
|
@@ -17,4 +17,5 @@ Mime::Type.register "multipart/form-data", :multipart_form
|
|
17
17
|
Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
|
18
18
|
|
19
19
|
# http://www.ietf.org/rfc/rfc4627.txt
|
20
|
-
|
20
|
+
# http://www.json.org/JSONRequest.html
|
21
|
+
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'action_controller/integration'
|
2
|
+
require 'active_support/testing/performance'
|
3
|
+
require 'active_support/testing/default'
|
4
|
+
|
5
|
+
module ActionController
|
6
|
+
# An integration test that runs a code profiler on your test methods.
|
7
|
+
# Profiling output for combinations of each test method, measurement, and
|
8
|
+
# output format are written to your tmp/performance directory.
|
9
|
+
#
|
10
|
+
# By default, process_time is measured and both flat and graph_html output
|
11
|
+
# formats are written, so you'll have two output files per test method.
|
12
|
+
class PerformanceTest < ActionController::IntegrationTest
|
13
|
+
include ActiveSupport::Testing::Performance
|
14
|
+
include ActiveSupport::Testing::Default
|
15
|
+
end
|
16
|
+
end
|
@@ -73,7 +73,8 @@ module ActionController
|
|
73
73
|
#
|
74
74
|
def polymorphic_url(record_or_hash_or_array, options = {})
|
75
75
|
if record_or_hash_or_array.kind_of?(Array)
|
76
|
-
record_or_hash_or_array = record_or_hash_or_array.
|
76
|
+
record_or_hash_or_array = record_or_hash_or_array.compact
|
77
|
+
record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
|
77
78
|
end
|
78
79
|
|
79
80
|
record = extract_record(record_or_hash_or_array)
|
@@ -102,7 +103,13 @@ module ActionController
|
|
102
103
|
args << format if format
|
103
104
|
|
104
105
|
named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
|
105
|
-
|
106
|
+
|
107
|
+
url_options = options.except(:action, :routing_type, :format)
|
108
|
+
unless url_options.empty?
|
109
|
+
args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
|
110
|
+
end
|
111
|
+
|
112
|
+
__send__(named_route, *args)
|
106
113
|
end
|
107
114
|
|
108
115
|
# Returns the path component of a URL for the given record. It uses
|
@@ -114,19 +121,19 @@ module ActionController
|
|
114
121
|
|
115
122
|
%w(edit new formatted).each do |action|
|
116
123
|
module_eval <<-EOT, __FILE__, __LINE__
|
117
|
-
def #{action}_polymorphic_url(record_or_hash)
|
118
|
-
polymorphic_url(record_or_hash, :action => "#{action}")
|
124
|
+
def #{action}_polymorphic_url(record_or_hash, options = {})
|
125
|
+
polymorphic_url(record_or_hash, options.merge(:action => "#{action}"))
|
119
126
|
end
|
120
127
|
|
121
|
-
def #{action}_polymorphic_path(record_or_hash)
|
122
|
-
polymorphic_url(record_or_hash, :action => "#{action}", :routing_type => :path)
|
128
|
+
def #{action}_polymorphic_path(record_or_hash, options = {})
|
129
|
+
polymorphic_url(record_or_hash, options.merge(:action => "#{action}", :routing_type => :path))
|
123
130
|
end
|
124
131
|
EOT
|
125
132
|
end
|
126
133
|
|
127
134
|
private
|
128
135
|
def action_prefix(options)
|
129
|
-
options[:action] ? "#{options[:action]}_" : ""
|
136
|
+
options[:action] ? "#{options[:action]}_" : options[:format] ? "formatted_" : ""
|
130
137
|
end
|
131
138
|
|
132
139
|
def routing_type(options)
|
@@ -143,7 +150,7 @@ module ActionController
|
|
143
150
|
if parent.is_a?(Symbol) || parent.is_a?(String)
|
144
151
|
string << "#{parent}_"
|
145
152
|
else
|
146
|
-
string << "#{RecordIdentifier.
|
153
|
+
string << "#{RecordIdentifier.__send__("singular_class_name", parent)}_"
|
147
154
|
end
|
148
155
|
end
|
149
156
|
end
|
@@ -151,7 +158,7 @@ module ActionController
|
|
151
158
|
if record.is_a?(Symbol) || record.is_a?(String)
|
152
159
|
route << "#{record}_"
|
153
160
|
else
|
154
|
-
route << "#{RecordIdentifier.
|
161
|
+
route << "#{RecordIdentifier.__send__("#{inflection}_class_name", record)}_"
|
155
162
|
end
|
156
163
|
|
157
164
|
action_prefix(options) + namespace + route + routing_type(options).to_s
|
@@ -0,0 +1,303 @@
|
|
1
|
+
require 'action_controller/cgi_ext'
|
2
|
+
require 'action_controller/session/cookie_store'
|
3
|
+
|
4
|
+
module ActionController #:nodoc:
|
5
|
+
class RackRequest < AbstractRequest #:nodoc:
|
6
|
+
attr_accessor :session_options
|
7
|
+
attr_reader :cgi
|
8
|
+
|
9
|
+
class SessionFixationAttempt < StandardError #:nodoc:
|
10
|
+
end
|
11
|
+
|
12
|
+
DEFAULT_SESSION_OPTIONS = {
|
13
|
+
:database_manager => CGI::Session::CookieStore, # store data in cookie
|
14
|
+
:prefix => "ruby_sess.", # prefix session file names
|
15
|
+
:session_path => "/", # available to all paths in app
|
16
|
+
:session_key => "_session_id",
|
17
|
+
:cookie_only => true,
|
18
|
+
:session_http_only=> true
|
19
|
+
}
|
20
|
+
|
21
|
+
def initialize(env, session_options = DEFAULT_SESSION_OPTIONS)
|
22
|
+
@session_options = session_options
|
23
|
+
@env = env
|
24
|
+
@cgi = CGIWrapper.new(self)
|
25
|
+
super()
|
26
|
+
end
|
27
|
+
|
28
|
+
%w[ AUTH_TYPE GATEWAY_INTERFACE PATH_INFO
|
29
|
+
PATH_TRANSLATED REMOTE_HOST
|
30
|
+
REMOTE_IDENT REMOTE_USER SCRIPT_NAME
|
31
|
+
SERVER_NAME SERVER_PROTOCOL
|
32
|
+
|
33
|
+
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
|
34
|
+
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
|
35
|
+
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
|
36
|
+
define_method(env.sub(/^HTTP_/n, '').downcase) do
|
37
|
+
@env[env]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def query_string
|
42
|
+
qs = super
|
43
|
+
if !qs.blank?
|
44
|
+
qs
|
45
|
+
else
|
46
|
+
@env['QUERY_STRING']
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def body_stream #:nodoc:
|
51
|
+
@env['rack.input']
|
52
|
+
end
|
53
|
+
|
54
|
+
def key?(key)
|
55
|
+
@env.key?(key)
|
56
|
+
end
|
57
|
+
|
58
|
+
def cookies
|
59
|
+
return {} unless @env["HTTP_COOKIE"]
|
60
|
+
|
61
|
+
unless @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"]
|
62
|
+
@env["rack.request.cookie_string"] = @env["HTTP_COOKIE"]
|
63
|
+
@env["rack.request.cookie_hash"] = CGI::Cookie::parse(@env["rack.request.cookie_string"])
|
64
|
+
end
|
65
|
+
|
66
|
+
@env["rack.request.cookie_hash"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def server_port
|
70
|
+
@env['SERVER_PORT'].to_i
|
71
|
+
end
|
72
|
+
|
73
|
+
def server_software
|
74
|
+
@env['SERVER_SOFTWARE'].split("/").first
|
75
|
+
end
|
76
|
+
|
77
|
+
def session
|
78
|
+
unless defined?(@session)
|
79
|
+
if @session_options == false
|
80
|
+
@session = Hash.new
|
81
|
+
else
|
82
|
+
stale_session_check! do
|
83
|
+
if cookie_only? && query_parameters[session_options_with_string_keys['session_key']]
|
84
|
+
raise SessionFixationAttempt
|
85
|
+
end
|
86
|
+
case value = session_options_with_string_keys['new_session']
|
87
|
+
when true
|
88
|
+
@session = new_session
|
89
|
+
when false
|
90
|
+
begin
|
91
|
+
@session = CGI::Session.new(@cgi, session_options_with_string_keys)
|
92
|
+
# CGI::Session raises ArgumentError if 'new_session' == false
|
93
|
+
# and no session cookie or query param is present.
|
94
|
+
rescue ArgumentError
|
95
|
+
@session = Hash.new
|
96
|
+
end
|
97
|
+
when nil
|
98
|
+
@session = CGI::Session.new(@cgi, session_options_with_string_keys)
|
99
|
+
else
|
100
|
+
raise ArgumentError, "Invalid new_session option: #{value}"
|
101
|
+
end
|
102
|
+
@session['__valid_session']
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
@session
|
107
|
+
end
|
108
|
+
|
109
|
+
def reset_session
|
110
|
+
@session.delete if defined?(@session) && @session.is_a?(CGI::Session)
|
111
|
+
@session = new_session
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
# Delete an old session if it exists then create a new one.
|
116
|
+
def new_session
|
117
|
+
if @session_options == false
|
118
|
+
Hash.new
|
119
|
+
else
|
120
|
+
CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil
|
121
|
+
CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true))
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def cookie_only?
|
126
|
+
session_options_with_string_keys['cookie_only']
|
127
|
+
end
|
128
|
+
|
129
|
+
def stale_session_check!
|
130
|
+
yield
|
131
|
+
rescue ArgumentError => argument_error
|
132
|
+
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
|
133
|
+
begin
|
134
|
+
# Note that the regexp does not allow $1 to end with a ':'
|
135
|
+
$1.constantize
|
136
|
+
rescue LoadError, NameError => const_error
|
137
|
+
raise ActionController::SessionRestoreError, <<-end_msg
|
138
|
+
Session contains objects whose class definition isn\'t available.
|
139
|
+
Remember to require the classes for all objects kept in the session.
|
140
|
+
(Original exception: #{const_error.message} [#{const_error.class}])
|
141
|
+
end_msg
|
142
|
+
end
|
143
|
+
|
144
|
+
retry
|
145
|
+
else
|
146
|
+
raise
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def session_options_with_string_keys
|
151
|
+
@session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class RackResponse < AbstractResponse #:nodoc:
|
156
|
+
def initialize(request)
|
157
|
+
@cgi = request.cgi
|
158
|
+
@writer = lambda { |x| @body << x }
|
159
|
+
@block = nil
|
160
|
+
super()
|
161
|
+
end
|
162
|
+
|
163
|
+
# Retrieve status from instance variable if has already been delete
|
164
|
+
def status
|
165
|
+
@status || super
|
166
|
+
end
|
167
|
+
|
168
|
+
def out(output = $stdout, &block)
|
169
|
+
# Nasty hack because CGI sessions are closed after the normal
|
170
|
+
# prepare! statement
|
171
|
+
set_cookies!
|
172
|
+
|
173
|
+
@block = block
|
174
|
+
@status = headers.delete("Status")
|
175
|
+
if [204, 304].include?(status.to_i)
|
176
|
+
headers.delete("Content-Type")
|
177
|
+
[status, headers.to_hash, []]
|
178
|
+
else
|
179
|
+
[status, headers.to_hash, self]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
alias to_a out
|
183
|
+
|
184
|
+
def each(&callback)
|
185
|
+
if @body.respond_to?(:call)
|
186
|
+
@writer = lambda { |x| callback.call(x) }
|
187
|
+
@body.call(self, self)
|
188
|
+
elsif @body.is_a?(String)
|
189
|
+
@body.each_line(&callback)
|
190
|
+
else
|
191
|
+
@body.each(&callback)
|
192
|
+
end
|
193
|
+
|
194
|
+
@writer = callback
|
195
|
+
@block.call(self) if @block
|
196
|
+
end
|
197
|
+
|
198
|
+
def write(str)
|
199
|
+
@writer.call str.to_s
|
200
|
+
str
|
201
|
+
end
|
202
|
+
|
203
|
+
def close
|
204
|
+
@body.close if @body.respond_to?(:close)
|
205
|
+
end
|
206
|
+
|
207
|
+
def empty?
|
208
|
+
@block == nil && @body.empty?
|
209
|
+
end
|
210
|
+
|
211
|
+
def prepare!
|
212
|
+
super
|
213
|
+
|
214
|
+
convert_language!
|
215
|
+
convert_expires!
|
216
|
+
set_status!
|
217
|
+
# set_cookies!
|
218
|
+
end
|
219
|
+
|
220
|
+
private
|
221
|
+
def convert_language!
|
222
|
+
headers["Content-Language"] = headers.delete("language") if headers["language"]
|
223
|
+
end
|
224
|
+
|
225
|
+
def convert_expires!
|
226
|
+
headers["Expires"] = headers.delete("") if headers["expires"]
|
227
|
+
end
|
228
|
+
|
229
|
+
def convert_content_type!
|
230
|
+
super
|
231
|
+
headers['Content-Type'] = headers.delete('type') || "text/html"
|
232
|
+
headers['Content-Type'] += "; charset=" + headers.delete('charset') if headers['charset']
|
233
|
+
end
|
234
|
+
|
235
|
+
def set_content_length!
|
236
|
+
super
|
237
|
+
headers["Content-Length"] = headers["Content-Length"].to_s if headers["Content-Length"]
|
238
|
+
end
|
239
|
+
|
240
|
+
def set_status!
|
241
|
+
self.status ||= "200 OK"
|
242
|
+
end
|
243
|
+
|
244
|
+
def set_cookies!
|
245
|
+
# Convert 'cookie' header to 'Set-Cookie' headers.
|
246
|
+
# Because Set-Cookie header can appear more the once in the response body,
|
247
|
+
# we store it in a line break separated string that will be translated to
|
248
|
+
# multiple Set-Cookie header by the handler.
|
249
|
+
if cookie = headers.delete('cookie')
|
250
|
+
cookies = []
|
251
|
+
|
252
|
+
case cookie
|
253
|
+
when Array then cookie.each { |c| cookies << c.to_s }
|
254
|
+
when Hash then cookie.each { |_, c| cookies << c.to_s }
|
255
|
+
else cookies << cookie.to_s
|
256
|
+
end
|
257
|
+
|
258
|
+
@cgi.output_cookies.each { |c| cookies << c.to_s } if @cgi.output_cookies
|
259
|
+
|
260
|
+
headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].flatten.compact
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
class CGIWrapper < ::CGI
|
266
|
+
attr_reader :output_cookies
|
267
|
+
|
268
|
+
def initialize(request, *args)
|
269
|
+
@request = request
|
270
|
+
@args = *args
|
271
|
+
@input = request.body
|
272
|
+
|
273
|
+
super *args
|
274
|
+
end
|
275
|
+
|
276
|
+
def params
|
277
|
+
@params ||= @request.params
|
278
|
+
end
|
279
|
+
|
280
|
+
def cookies
|
281
|
+
@request.cookies
|
282
|
+
end
|
283
|
+
|
284
|
+
def query_string
|
285
|
+
@request.query_string
|
286
|
+
end
|
287
|
+
|
288
|
+
# Used to wrap the normal args variable used inside CGI.
|
289
|
+
def args
|
290
|
+
@args
|
291
|
+
end
|
292
|
+
|
293
|
+
# Used to wrap the normal env_table variable used inside CGI.
|
294
|
+
def env_table
|
295
|
+
@request.env
|
296
|
+
end
|
297
|
+
|
298
|
+
# Used to wrap the normal stdinput variable used inside CGI.
|
299
|
+
def stdinput
|
300
|
+
@input
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|