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.

Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +178 -0
  3. data/lib/action_controller/base.rb +1 -1
  4. data/lib/action_controller/metal/params_wrapper.rb +11 -4
  5. data/lib/action_controller/metal/redirecting.rb +1 -1
  6. data/lib/action_controller/metal/request_forgery_protection.rb +3 -0
  7. data/lib/action_controller/metal/responder.rb +1 -1
  8. data/lib/action_controller/metal/strong_parameters.rb +27 -8
  9. data/lib/action_controller/test_case.rb +3 -0
  10. data/lib/action_dispatch.rb +0 -1
  11. data/lib/action_dispatch/http/mime_negotiation.rb +1 -1
  12. data/lib/action_dispatch/http/mime_type.rb +4 -1
  13. data/lib/action_dispatch/journey/formatter.rb +2 -2
  14. data/lib/action_dispatch/journey/visitors.rb +24 -4
  15. data/lib/action_dispatch/middleware/cookies.rb +7 -7
  16. data/lib/action_dispatch/middleware/exception_wrapper.rb +1 -1
  17. data/lib/action_dispatch/middleware/session/cookie_store.rb +2 -2
  18. data/lib/action_dispatch/middleware/static.rb +3 -3
  19. data/lib/action_dispatch/routing/inspector.rb +6 -5
  20. data/lib/action_dispatch/routing/mapper.rb +57 -33
  21. data/lib/action_dispatch/routing/redirection.rb +18 -8
  22. data/lib/action_dispatch/routing/route_set.rb +26 -32
  23. data/lib/action_pack/version.rb +1 -1
  24. data/lib/action_view/helpers/asset_tag_helper.rb +2 -2
  25. data/lib/action_view/helpers/atom_feed_helper.rb +1 -1
  26. data/lib/action_view/helpers/csrf_helper.rb +4 -2
  27. data/lib/action_view/helpers/date_helper.rb +7 -3
  28. data/lib/action_view/helpers/form_helper.rb +2 -2
  29. data/lib/action_view/helpers/form_options_helper.rb +1 -1
  30. data/lib/action_view/helpers/form_tag_helper.rb +1 -1
  31. data/lib/action_view/helpers/number_helper.rb +4 -4
  32. data/lib/action_view/helpers/tag_helper.rb +1 -1
  33. data/lib/action_view/helpers/tags/collection_helpers.rb +1 -1
  34. data/lib/action_view/helpers/tags/label.rb +1 -2
  35. data/lib/action_view/helpers/text_helper.rb +6 -1
  36. data/lib/action_view/helpers/translation_helper.rb +9 -1
  37. data/lib/action_view/helpers/url_helper.rb +1 -1
  38. data/lib/action_view/template.rb +2 -2
  39. data/lib/action_view/template/error.rb +2 -2
  40. data/lib/action_view/template/text.rb +1 -1
  41. 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
- # # Assign an array of values to a cookie.
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] # => "david"
46
- # cookies.size # => 2
47
- # cookies[:lat_lon] # => [47.68, -122.37]
48
- # cookies.signed[:login] # => "XJ-122"
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 or list of values (as an array).
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.exists?(full_path)
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 his +user_id+ without
19
- # knowing your app's secret key, but can easily read his +user_id+. This
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.dup
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(unescape_path(path)))
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
- name_width, verb_width, path_width = widths(routes)
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
- raise ArgumentError, "missing :controller"
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
- raise ArgumentError, "missing :action"
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
- options = { :path => path, :as => path, :module => path,
780
- :shallow_path => path, :shallow_prefix => path }.merge!(options)
781
- scope(options) { yield }
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
- scope(parent_resource.member_scope) do
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
- with_exclusive_scope do
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, :shallow_path => @scope[:path]) do
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, resource = parent_resource)
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
- with_scope_level(kind, resource) do
1548
- scope(parent_resource.resource_scope) do
1549
- yield
1550
- end
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 shallow_scoping? #:nodoc:
1576
- shallow? && @scope[:scope_level] == :member
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
- prefix.to_s
1607
+ @scope[:path].to_s
1585
1608
  else
1586
- "#{prefix}/#{action_path(action, path)}"
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, shallow_scoping? ? @scope[:shallow_prefix] : name_prefix, member_name]
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
- if relative_path?(url_options[:path])
94
- url_options[:path] = "/#{url_options[:path]}"
95
- url_options[:script_name] = request.script_name
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 a invalid encoding then
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
- @path_parts = @route.required_parts
167
- @arg_size = @path_parts.size
168
- @string_route = @route.optimized_path
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
- path = @string_route.dup
186
- klass = Journey::Router::Utils
186
+ params = Hash[parameterize_args(args)]
187
+ missing_keys = missing_keys(params)
187
188
 
188
- @path_parts.zip(args) do |part, arg|
189
- parameterized_arg = arg.to_param
189
+ unless missing_keys.empty?
190
+ raise_generation_error(params, missing_keys)
191
+ end
190
192
 
191
- if parameterized_arg.nil? || parameterized_arg.empty?
192
- raise_generation_error(args)
193
- end
193
+ @optimized_path.map{ |segment| replace_segment(params, segment) }.join
194
+ end
194
195
 
195
- # Replace each route parameter
196
- # e.g. :id for regular parameter or *path for globbing
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 raise_generation_error(args)
208
- parts, missing_keys = [], []
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
- parts << [part, arg]
218
- end
208
+ def missing_keys(args)
209
+ args.select{ |part, arg| arg.nil? || arg.empty? }.keys
210
+ end
219
211
 
220
- message = "No route matches #{Hash[parts].inspect}"
221
- message << " missing required keys: #{missing_keys.inspect}"
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 accross different
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.
@@ -1,7 +1,7 @@
1
1
  module ActionPack
2
2
  # Returns the version of the currently loaded ActionPack as a Gem::Version
3
3
  def self.version
4
- Gem::Version.new "4.0.3"
4
+ Gem::Version.new "4.0.4.rc1"
5
5
  end
6
6
 
7
7
  module VERSION #:nodoc: