actionpack 4.0.3 → 4.0.4.rc1
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 +178 -0
- data/lib/action_controller/base.rb +1 -1
- data/lib/action_controller/metal/params_wrapper.rb +11 -4
- data/lib/action_controller/metal/redirecting.rb +1 -1
- data/lib/action_controller/metal/request_forgery_protection.rb +3 -0
- data/lib/action_controller/metal/responder.rb +1 -1
- data/lib/action_controller/metal/strong_parameters.rb +27 -8
- data/lib/action_controller/test_case.rb +3 -0
- data/lib/action_dispatch.rb +0 -1
- data/lib/action_dispatch/http/mime_negotiation.rb +1 -1
- data/lib/action_dispatch/http/mime_type.rb +4 -1
- data/lib/action_dispatch/journey/formatter.rb +2 -2
- data/lib/action_dispatch/journey/visitors.rb +24 -4
- data/lib/action_dispatch/middleware/cookies.rb +7 -7
- data/lib/action_dispatch/middleware/exception_wrapper.rb +1 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +2 -2
- data/lib/action_dispatch/middleware/static.rb +3 -3
- data/lib/action_dispatch/routing/inspector.rb +6 -5
- data/lib/action_dispatch/routing/mapper.rb +57 -33
- data/lib/action_dispatch/routing/redirection.rb +18 -8
- data/lib/action_dispatch/routing/route_set.rb +26 -32
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +2 -2
- data/lib/action_view/helpers/atom_feed_helper.rb +1 -1
- data/lib/action_view/helpers/csrf_helper.rb +4 -2
- data/lib/action_view/helpers/date_helper.rb +7 -3
- data/lib/action_view/helpers/form_helper.rb +2 -2
- data/lib/action_view/helpers/form_options_helper.rb +1 -1
- data/lib/action_view/helpers/form_tag_helper.rb +1 -1
- data/lib/action_view/helpers/number_helper.rb +4 -4
- data/lib/action_view/helpers/tag_helper.rb +1 -1
- data/lib/action_view/helpers/tags/collection_helpers.rb +1 -1
- data/lib/action_view/helpers/tags/label.rb +1 -2
- data/lib/action_view/helpers/text_helper.rb +6 -1
- data/lib/action_view/helpers/translation_helper.rb +9 -1
- data/lib/action_view/helpers/url_helper.rb +1 -1
- data/lib/action_view/template.rb +2 -2
- data/lib/action_view/template/error.rb +2 -2
- data/lib/action_view/template/text.rb +1 -1
- metadata +9 -9
@@ -23,8 +23,8 @@ module ActionDispatch
|
|
23
23
|
# # This cookie will be deleted when the user's browser is closed.
|
24
24
|
# cookies[:user_name] = "david"
|
25
25
|
#
|
26
|
-
# #
|
27
|
-
# cookies[:lat_lon] = [47.68, -122.37]
|
26
|
+
# # Cookie values are String based. Other data types need to be serialized.
|
27
|
+
# cookies[:lat_lon] = JSON.generate([47.68, -122.37])
|
28
28
|
#
|
29
29
|
# # Sets a cookie that expires in 1 hour.
|
30
30
|
# cookies[:login] = { value: "XJ-122", expires: 1.hour.from_now }
|
@@ -42,10 +42,10 @@ module ActionDispatch
|
|
42
42
|
#
|
43
43
|
# Examples of reading:
|
44
44
|
#
|
45
|
-
# cookies[:user_name]
|
46
|
-
# cookies.size
|
47
|
-
# cookies[:lat_lon]
|
48
|
-
# cookies.signed[:login]
|
45
|
+
# cookies[:user_name] # => "david"
|
46
|
+
# cookies.size # => 2
|
47
|
+
# JSON.parse(cookies[:lat_lon]) # => [47.68, -122.37]
|
48
|
+
# cookies.signed[:login] # => "XJ-122"
|
49
49
|
#
|
50
50
|
# Example for deleting:
|
51
51
|
#
|
@@ -63,7 +63,7 @@ module ActionDispatch
|
|
63
63
|
#
|
64
64
|
# The option symbols for setting cookies are:
|
65
65
|
#
|
66
|
-
# * <tt>:value</tt> - The cookie's value
|
66
|
+
# * <tt>:value</tt> - The cookie's value.
|
67
67
|
# * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
|
68
68
|
# of the application.
|
69
69
|
# * <tt>:domain</tt> - The domain for which this cookie applies so you can
|
@@ -96,7 +96,7 @@ module ActionDispatch
|
|
96
96
|
def source_fragment(path, line)
|
97
97
|
return unless Rails.respond_to?(:root) && Rails.root
|
98
98
|
full_path = Rails.root.join(path)
|
99
|
-
if File.
|
99
|
+
if File.exist?(full_path)
|
100
100
|
File.open(full_path, "r") do |file|
|
101
101
|
start = [line - 3, 0].max
|
102
102
|
lines = file.each_line.drop(start).take(6)
|
@@ -15,8 +15,8 @@ module ActionDispatch
|
|
15
15
|
# best possible option given your application's configuration.
|
16
16
|
#
|
17
17
|
# If you only have secret_token set, your cookies will be signed, but
|
18
|
-
# not encrypted. This means a user cannot alter
|
19
|
-
# knowing your app's secret key, but can easily read
|
18
|
+
# not encrypted. This means a user cannot alter their +user_id+ without
|
19
|
+
# knowing your app's secret key, but can easily read their +user_id+. This
|
20
20
|
# was the default for Rails 3 apps.
|
21
21
|
#
|
22
22
|
# If you have secret_key_base set, your cookies will be encrypted. This
|
@@ -11,9 +11,10 @@ module ActionDispatch
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def match?(path)
|
14
|
-
path = path
|
14
|
+
path = unescape_path(path)
|
15
|
+
return false unless path.valid_encoding?
|
15
16
|
|
16
|
-
full_path = path.empty? ? @root : File.join(@root, escape_glob_chars(
|
17
|
+
full_path = path.empty? ? @root : File.join(@root, escape_glob_chars(path))
|
17
18
|
paths = "#{full_path}#{ext}"
|
18
19
|
|
19
20
|
matches = Dir[paths]
|
@@ -40,7 +41,6 @@ module ActionDispatch
|
|
40
41
|
end
|
41
42
|
|
42
43
|
def escape_glob_chars(path)
|
43
|
-
path.force_encoding('binary') if path.respond_to? :force_encoding
|
44
44
|
path.gsub(/[*?{}\[\]]/, "\\\\\\&")
|
45
45
|
end
|
46
46
|
end
|
@@ -69,7 +69,7 @@ module ActionDispatch
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def internal?
|
72
|
-
controller.to_s =~ %r{\Arails/(info|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}}
|
72
|
+
controller.to_s =~ %r{\Arails/(info|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}\z}
|
73
73
|
end
|
74
74
|
|
75
75
|
def engine?
|
@@ -179,7 +179,8 @@ module ActionDispatch
|
|
179
179
|
|
180
180
|
private
|
181
181
|
def draw_section(routes)
|
182
|
-
|
182
|
+
header_lengths = ['Prefix', 'Verb', 'URI Pattern'].map(&:length)
|
183
|
+
name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max)
|
183
184
|
|
184
185
|
routes.map do |r|
|
185
186
|
"#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
|
@@ -193,9 +194,9 @@ module ActionDispatch
|
|
193
194
|
end
|
194
195
|
|
195
196
|
def widths(routes)
|
196
|
-
[routes.map { |r| r[:name].length }.max,
|
197
|
-
routes.map { |r| r[:verb].length }.max,
|
198
|
-
routes.map { |r| r[:path].length }.max]
|
197
|
+
[routes.map { |r| r[:name].length }.max || 0,
|
198
|
+
routes.map { |r| r[:verb].length }.max || 0,
|
199
|
+
routes.map { |r| r[:path].length }.max || 0]
|
199
200
|
end
|
200
201
|
end
|
201
202
|
|
@@ -226,11 +226,13 @@ module ActionDispatch
|
|
226
226
|
action = action.to_s unless action.is_a?(Regexp)
|
227
227
|
|
228
228
|
if controller.blank? && segment_keys.exclude?(:controller)
|
229
|
-
|
229
|
+
message = "Missing :controller key on routes definition, please check your routes."
|
230
|
+
raise ArgumentError, message
|
230
231
|
end
|
231
232
|
|
232
233
|
if action.blank? && segment_keys.exclude?(:action)
|
233
|
-
|
234
|
+
message = "Missing :action key on routes definition, please check your routes."
|
235
|
+
raise ArgumentError, message
|
234
236
|
end
|
235
237
|
|
236
238
|
if controller.is_a?(String) && controller !~ /\A[a-z_0-9\/]*\z/
|
@@ -695,6 +697,11 @@ module ActionDispatch
|
|
695
697
|
options[:path] = args.flatten.join('/') if args.any?
|
696
698
|
options[:constraints] ||= {}
|
697
699
|
|
700
|
+
unless shallow?
|
701
|
+
options[:shallow_path] ||= options[:path] if options.key?(:path)
|
702
|
+
options[:shallow_prefix] ||= options[:as] if options.key?(:as)
|
703
|
+
end
|
704
|
+
|
698
705
|
if options[:constraints].is_a?(Hash)
|
699
706
|
defaults = options[:constraints].select do
|
700
707
|
|k, v| URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum))
|
@@ -776,9 +783,16 @@ module ActionDispatch
|
|
776
783
|
# end
|
777
784
|
def namespace(path, options = {})
|
778
785
|
path = path.to_s
|
779
|
-
|
780
|
-
|
781
|
-
|
786
|
+
|
787
|
+
defaults = {
|
788
|
+
module: path,
|
789
|
+
path: options.fetch(:path, path),
|
790
|
+
as: options.fetch(:as, path),
|
791
|
+
shallow_path: options.fetch(:path, path),
|
792
|
+
shallow_prefix: options.fetch(:as, path)
|
793
|
+
}
|
794
|
+
|
795
|
+
scope(defaults.merge!(options)) { yield }
|
782
796
|
end
|
783
797
|
|
784
798
|
# === Parameter Restriction
|
@@ -1307,8 +1321,10 @@ module ActionDispatch
|
|
1307
1321
|
end
|
1308
1322
|
|
1309
1323
|
with_scope_level(:member) do
|
1310
|
-
|
1311
|
-
yield
|
1324
|
+
if shallow?
|
1325
|
+
shallow_scope(parent_resource.member_scope) { yield }
|
1326
|
+
else
|
1327
|
+
scope(parent_resource.member_scope) { yield }
|
1312
1328
|
end
|
1313
1329
|
end
|
1314
1330
|
end
|
@@ -1331,16 +1347,8 @@ module ActionDispatch
|
|
1331
1347
|
end
|
1332
1348
|
|
1333
1349
|
with_scope_level(:nested) do
|
1334
|
-
if shallow?
|
1335
|
-
|
1336
|
-
if @scope[:shallow_path].blank?
|
1337
|
-
scope(parent_resource.nested_scope, nested_options) { yield }
|
1338
|
-
else
|
1339
|
-
scope(@scope[:shallow_path], :as => @scope[:shallow_prefix]) do
|
1340
|
-
scope(parent_resource.nested_scope, nested_options) { yield }
|
1341
|
-
end
|
1342
|
-
end
|
1343
|
-
end
|
1350
|
+
if shallow? && nesting_depth > 1
|
1351
|
+
shallow_scope(parent_resource.nested_scope, nested_options) { yield }
|
1344
1352
|
else
|
1345
1353
|
scope(parent_resource.nested_scope, nested_options) { yield }
|
1346
1354
|
end
|
@@ -1357,7 +1365,7 @@ module ActionDispatch
|
|
1357
1365
|
end
|
1358
1366
|
|
1359
1367
|
def shallow
|
1360
|
-
scope(:shallow => true
|
1368
|
+
scope(:shallow => true) do
|
1361
1369
|
yield
|
1362
1370
|
end
|
1363
1371
|
end
|
@@ -1477,6 +1485,13 @@ module ActionDispatch
|
|
1477
1485
|
return true
|
1478
1486
|
end
|
1479
1487
|
|
1488
|
+
if options.delete(:shallow)
|
1489
|
+
shallow do
|
1490
|
+
send(method, resources.pop, options, &block)
|
1491
|
+
end
|
1492
|
+
return true
|
1493
|
+
end
|
1494
|
+
|
1480
1495
|
if resource_scope?
|
1481
1496
|
nested { send(method, resources.pop, options, &block) }
|
1482
1497
|
return true
|
@@ -1534,21 +1549,23 @@ module ActionDispatch
|
|
1534
1549
|
end
|
1535
1550
|
end
|
1536
1551
|
|
1537
|
-
def with_scope_level(kind
|
1552
|
+
def with_scope_level(kind)
|
1538
1553
|
old, @scope[:scope_level] = @scope[:scope_level], kind
|
1539
|
-
old_resource, @scope[:scope_level_resource] = @scope[:scope_level_resource], resource
|
1540
1554
|
yield
|
1541
1555
|
ensure
|
1542
1556
|
@scope[:scope_level] = old
|
1543
|
-
@scope[:scope_level_resource] = old_resource
|
1544
1557
|
end
|
1545
1558
|
|
1546
1559
|
def resource_scope(kind, resource) #:nodoc:
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1560
|
+
old_resource, @scope[:scope_level_resource] = @scope[:scope_level_resource], resource
|
1561
|
+
@nesting.push(resource)
|
1562
|
+
|
1563
|
+
with_scope_level(kind) do
|
1564
|
+
scope(parent_resource.resource_scope) { yield }
|
1551
1565
|
end
|
1566
|
+
ensure
|
1567
|
+
@nesting.pop
|
1568
|
+
@scope[:scope_level_resource] = old_resource
|
1552
1569
|
end
|
1553
1570
|
|
1554
1571
|
def nested_options #:nodoc:
|
@@ -1560,6 +1577,10 @@ module ActionDispatch
|
|
1560
1577
|
options
|
1561
1578
|
end
|
1562
1579
|
|
1580
|
+
def nesting_depth #:nodoc:
|
1581
|
+
@nesting.size
|
1582
|
+
end
|
1583
|
+
|
1563
1584
|
def param_constraint? #:nodoc:
|
1564
1585
|
@scope[:constraints] && @scope[:constraints][parent_resource.param].is_a?(Regexp)
|
1565
1586
|
end
|
@@ -1572,18 +1593,20 @@ module ActionDispatch
|
|
1572
1593
|
flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
|
1573
1594
|
end
|
1574
1595
|
|
1575
|
-
def
|
1576
|
-
|
1596
|
+
def shallow_scope(path, options = {}) #:nodoc:
|
1597
|
+
old_name_prefix, old_path = @scope[:as], @scope[:path]
|
1598
|
+
@scope[:as], @scope[:path] = @scope[:shallow_prefix], @scope[:shallow_path]
|
1599
|
+
|
1600
|
+
scope(path, options) { yield }
|
1601
|
+
ensure
|
1602
|
+
@scope[:as], @scope[:path] = old_name_prefix, old_path
|
1577
1603
|
end
|
1578
1604
|
|
1579
1605
|
def path_for_action(action, path) #:nodoc:
|
1580
|
-
prefix = shallow_scoping? ?
|
1581
|
-
"#{@scope[:shallow_path]}/#{parent_resource.shallow_scope}" : @scope[:path]
|
1582
|
-
|
1583
1606
|
if canonical_action?(action, path.blank?)
|
1584
|
-
|
1607
|
+
@scope[:path].to_s
|
1585
1608
|
else
|
1586
|
-
"#{
|
1609
|
+
"#{@scope[:path]}/#{action_path(action, path)}"
|
1587
1610
|
end
|
1588
1611
|
end
|
1589
1612
|
|
@@ -1620,7 +1643,7 @@ module ActionDispatch
|
|
1620
1643
|
when :new
|
1621
1644
|
[prefix, :new, name_prefix, member_name]
|
1622
1645
|
when :member
|
1623
|
-
[prefix,
|
1646
|
+
[prefix, name_prefix, member_name]
|
1624
1647
|
when :root
|
1625
1648
|
[name_prefix, collection_name, prefix]
|
1626
1649
|
else
|
@@ -1761,6 +1784,7 @@ module ActionDispatch
|
|
1761
1784
|
@set = set
|
1762
1785
|
@scope = { :path_names => @set.resources_path_names }
|
1763
1786
|
@concerns = {}
|
1787
|
+
@nesting = []
|
1764
1788
|
end
|
1765
1789
|
|
1766
1790
|
include Base
|
@@ -26,14 +26,19 @@ module ActionDispatch
|
|
26
26
|
end
|
27
27
|
|
28
28
|
uri = URI.parse(path(req.symbolized_path_parameters, req))
|
29
|
+
|
30
|
+
unless uri.host
|
31
|
+
if relative_path?(uri.path)
|
32
|
+
uri.path = "#{req.script_name}/#{uri.path}"
|
33
|
+
elsif uri.path.empty?
|
34
|
+
uri.path = req.script_name.empty? ? "/" : req.script_name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
29
38
|
uri.scheme ||= req.scheme
|
30
39
|
uri.host ||= req.host
|
31
40
|
uri.port ||= req.port unless req.standard_port?
|
32
41
|
|
33
|
-
if relative_path?(uri.path)
|
34
|
-
uri.path = "#{req.script_name}/#{uri.path}"
|
35
|
-
end
|
36
|
-
|
37
42
|
body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
|
38
43
|
|
39
44
|
headers = {
|
@@ -90,11 +95,16 @@ module ActionDispatch
|
|
90
95
|
url_options[:path] = (url_options[:path] % escape_path(params))
|
91
96
|
end
|
92
97
|
|
93
|
-
|
94
|
-
|
95
|
-
|
98
|
+
unless options[:host] || options[:domain]
|
99
|
+
if relative_path?(url_options[:path])
|
100
|
+
url_options[:path] = "/#{url_options[:path]}"
|
101
|
+
url_options[:script_name] = request.script_name
|
102
|
+
elsif url_options[:path].empty?
|
103
|
+
url_options[:path] = request.script_name.empty? ? "/" : ""
|
104
|
+
url_options[:script_name] = request.script_name
|
105
|
+
end
|
96
106
|
end
|
97
|
-
|
107
|
+
|
98
108
|
ActionDispatch::Http::URL.url_for url_options
|
99
109
|
end
|
100
110
|
|
@@ -28,7 +28,7 @@ module ActionDispatch
|
|
28
28
|
def call(env)
|
29
29
|
params = env[PARAMETERS_KEY]
|
30
30
|
|
31
|
-
# If any of the path parameters has
|
31
|
+
# If any of the path parameters has an invalid encoding then
|
32
32
|
# raise since it's likely to trigger errors further on.
|
33
33
|
params.each do |key, value|
|
34
34
|
next unless value.respond_to?(:valid_encoding?)
|
@@ -163,9 +163,10 @@ module ActionDispatch
|
|
163
163
|
|
164
164
|
def initialize(route, options)
|
165
165
|
super
|
166
|
-
@
|
167
|
-
@
|
168
|
-
@
|
166
|
+
@klass = Journey::Router::Utils
|
167
|
+
@required_parts = @route.required_parts
|
168
|
+
@arg_size = @required_parts.size
|
169
|
+
@optimized_path = @route.optimized_path
|
169
170
|
end
|
170
171
|
|
171
172
|
def call(t, args)
|
@@ -182,43 +183,36 @@ module ActionDispatch
|
|
182
183
|
private
|
183
184
|
|
184
185
|
def optimized_helper(args)
|
185
|
-
|
186
|
-
|
186
|
+
params = Hash[parameterize_args(args)]
|
187
|
+
missing_keys = missing_keys(params)
|
187
188
|
|
188
|
-
|
189
|
-
|
189
|
+
unless missing_keys.empty?
|
190
|
+
raise_generation_error(params, missing_keys)
|
191
|
+
end
|
190
192
|
|
191
|
-
|
192
|
-
|
193
|
-
end
|
193
|
+
@optimized_path.map{ |segment| replace_segment(params, segment) }.join
|
194
|
+
end
|
194
195
|
|
195
|
-
|
196
|
-
|
197
|
-
# with ruby string interpolation code
|
198
|
-
path.gsub!(/(\*|:)#{part}/, klass.escape_fragment(parameterized_arg))
|
199
|
-
end
|
200
|
-
path
|
196
|
+
def replace_segment(params, segment)
|
197
|
+
Symbol === segment ? @klass.escape_fragment(params[segment]) : segment
|
201
198
|
end
|
202
199
|
|
203
200
|
def optimize_routes_generation?(t)
|
204
201
|
t.send(:optimize_routes_generation?)
|
205
202
|
end
|
206
203
|
|
207
|
-
def
|
208
|
-
|
209
|
-
|
210
|
-
@path_parts.zip(args) do |part, arg|
|
211
|
-
parameterized_arg = arg.to_param
|
212
|
-
|
213
|
-
if parameterized_arg.nil? || parameterized_arg.empty?
|
214
|
-
missing_keys << part
|
215
|
-
end
|
204
|
+
def parameterize_args(args)
|
205
|
+
@required_parts.zip(args.map(&:to_param))
|
206
|
+
end
|
216
207
|
|
217
|
-
|
218
|
-
|
208
|
+
def missing_keys(args)
|
209
|
+
args.select{ |part, arg| arg.nil? || arg.empty? }.keys
|
210
|
+
end
|
219
211
|
|
220
|
-
|
221
|
-
|
212
|
+
def raise_generation_error(args, missing_keys)
|
213
|
+
constraints = Hash[@route.requirements.merge(args).sort]
|
214
|
+
message = "No route matches #{constraints.inspect}"
|
215
|
+
message << " missing required keys: #{missing_keys.sort.inspect}"
|
222
216
|
|
223
217
|
raise ActionController::UrlGenerationError, message
|
224
218
|
end
|
@@ -226,7 +220,7 @@ module ActionDispatch
|
|
226
220
|
|
227
221
|
def initialize(route, options)
|
228
222
|
@options = options
|
229
|
-
@segment_keys = route.segment_keys
|
223
|
+
@segment_keys = route.segment_keys.uniq
|
230
224
|
@route = route
|
231
225
|
end
|
232
226
|
|
@@ -361,7 +355,7 @@ module ActionDispatch
|
|
361
355
|
include UrlFor
|
362
356
|
end
|
363
357
|
|
364
|
-
# Contains all the mounted helpers
|
358
|
+
# Contains all the mounted helpers across different
|
365
359
|
# engines and the `main_app` helper for the application.
|
366
360
|
# You can include this in your classes if you want to
|
367
361
|
# access routes for other engines.
|