actionpack 4.0.1 → 4.2.11.1

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 (241) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +402 -1173
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -7
  5. data/lib/abstract_controller/base.rb +39 -7
  6. data/lib/abstract_controller/callbacks.rb +32 -53
  7. data/lib/abstract_controller/collector.rb +11 -1
  8. data/lib/abstract_controller/helpers.rb +26 -16
  9. data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
  10. data/lib/abstract_controller/rendering.rb +57 -127
  11. data/lib/abstract_controller/url_for.rb +1 -1
  12. data/lib/abstract_controller.rb +1 -2
  13. data/lib/action_controller/base.rb +19 -10
  14. data/lib/action_controller/caching/fragments.rb +7 -1
  15. data/lib/action_controller/caching.rb +2 -12
  16. data/lib/action_controller/log_subscriber.rb +29 -20
  17. data/lib/action_controller/metal/conditional_get.rb +37 -12
  18. data/lib/action_controller/metal/data_streaming.rb +1 -1
  19. data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
  20. data/lib/action_controller/metal/exceptions.rb +1 -1
  21. data/lib/action_controller/metal/flash.rb +17 -0
  22. data/lib/action_controller/metal/force_ssl.rb +2 -2
  23. data/lib/action_controller/metal/head.rb +8 -6
  24. data/lib/action_controller/metal/helpers.rb +6 -2
  25. data/lib/action_controller/metal/http_authentication.rb +45 -23
  26. data/lib/action_controller/metal/instrumentation.rb +9 -6
  27. data/lib/action_controller/metal/live.rb +173 -20
  28. data/lib/action_controller/metal/mime_responds.rb +127 -232
  29. data/lib/action_controller/metal/params_wrapper.rb +16 -9
  30. data/lib/action_controller/metal/rack_delegation.rb +1 -1
  31. data/lib/action_controller/metal/redirecting.rb +34 -26
  32. data/lib/action_controller/metal/renderers.rb +39 -12
  33. data/lib/action_controller/metal/rendering.rb +41 -14
  34. data/lib/action_controller/metal/request_forgery_protection.rb +147 -19
  35. data/lib/action_controller/metal/streaming.rb +19 -21
  36. data/lib/action_controller/metal/strong_parameters.rb +166 -22
  37. data/lib/action_controller/metal/testing.rb +0 -1
  38. data/lib/action_controller/metal/url_for.rb +11 -12
  39. data/lib/action_controller/metal.rb +14 -8
  40. data/lib/action_controller/model_naming.rb +1 -1
  41. data/lib/action_controller/railtie.rb +5 -1
  42. data/lib/action_controller/test_case.rb +160 -94
  43. data/lib/action_controller.rb +2 -18
  44. data/lib/action_dispatch/http/cache.rb +5 -4
  45. data/lib/action_dispatch/http/filter_parameters.rb +2 -2
  46. data/lib/action_dispatch/http/filter_redirect.rb +5 -4
  47. data/lib/action_dispatch/http/headers.rb +46 -10
  48. data/lib/action_dispatch/http/mime_negotiation.rb +31 -4
  49. data/lib/action_dispatch/http/mime_type.rb +25 -26
  50. data/lib/action_dispatch/http/mime_types.rb +1 -0
  51. data/lib/action_dispatch/http/parameter_filter.rb +1 -1
  52. data/lib/action_dispatch/http/parameters.rb +25 -41
  53. data/lib/action_dispatch/http/request.rb +49 -32
  54. data/lib/action_dispatch/http/response.rb +127 -25
  55. data/lib/action_dispatch/http/upload.rb +9 -21
  56. data/lib/action_dispatch/http/url.rb +97 -70
  57. data/lib/action_dispatch/journey/formatter.rb +35 -19
  58. data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
  59. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
  60. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -33
  61. data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
  62. data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
  63. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
  64. data/lib/action_dispatch/journey/nodes/node.rb +4 -0
  65. data/lib/action_dispatch/journey/parser.rb +51 -59
  66. data/lib/action_dispatch/journey/parser.y +12 -10
  67. data/lib/action_dispatch/journey/path/pattern.rb +16 -19
  68. data/lib/action_dispatch/journey/route.rb +8 -19
  69. data/lib/action_dispatch/journey/router/strexp.rb +9 -6
  70. data/lib/action_dispatch/journey/router/utils.rb +54 -18
  71. data/lib/action_dispatch/journey/router.rb +53 -75
  72. data/lib/action_dispatch/journey/routes.rb +4 -0
  73. data/lib/action_dispatch/journey/scanner.rb +5 -5
  74. data/lib/action_dispatch/journey/visitors.rb +81 -60
  75. data/lib/action_dispatch/journey/visualizer/fsm.css +0 -4
  76. data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
  77. data/lib/action_dispatch/middleware/callbacks.rb +7 -7
  78. data/lib/action_dispatch/middleware/cookies.rb +119 -43
  79. data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -13
  80. data/lib/action_dispatch/middleware/exception_wrapper.rb +60 -20
  81. data/lib/action_dispatch/middleware/flash.rb +37 -24
  82. data/lib/action_dispatch/middleware/params_parser.rb +2 -2
  83. data/lib/action_dispatch/middleware/public_exceptions.rb +12 -3
  84. data/lib/action_dispatch/middleware/reloader.rb +11 -2
  85. data/lib/action_dispatch/middleware/remote_ip.rb +40 -54
  86. data/lib/action_dispatch/middleware/request_id.rb +1 -1
  87. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  88. data/lib/action_dispatch/middleware/session/cookie_store.rb +8 -7
  89. data/lib/action_dispatch/middleware/show_exceptions.rb +6 -2
  90. data/lib/action_dispatch/middleware/ssl.rb +10 -7
  91. data/lib/action_dispatch/middleware/static.rb +79 -23
  92. data/lib/action_dispatch/middleware/templates/rescues/{_request_and_response.erb → _request_and_response.html.erb} +0 -0
  93. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  94. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +21 -19
  95. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  96. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  97. data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +1 -1
  98. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  99. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  101. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  102. data/lib/action_dispatch/middleware/templates/rescues/{routing_error.erb → routing_error.html.erb} +3 -1
  103. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  104. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  105. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  106. data/lib/action_dispatch/middleware/templates/rescues/{unknown_action.erb → unknown_action.html.erb} +1 -1
  107. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  108. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +120 -64
  109. data/lib/action_dispatch/railtie.rb +5 -2
  110. data/lib/action_dispatch/request/session.rb +12 -0
  111. data/lib/action_dispatch/request/utils.rb +35 -0
  112. data/lib/action_dispatch/routing/endpoint.rb +10 -0
  113. data/lib/action_dispatch/routing/inspector.rb +11 -17
  114. data/lib/action_dispatch/routing/mapper.rb +519 -312
  115. data/lib/action_dispatch/routing/polymorphic_routes.rb +204 -79
  116. data/lib/action_dispatch/routing/redirection.rb +51 -26
  117. data/lib/action_dispatch/routing/route_set.rb +331 -206
  118. data/lib/action_dispatch/routing/routes_proxy.rb +5 -4
  119. data/lib/action_dispatch/routing/url_for.rb +19 -5
  120. data/lib/action_dispatch/routing.rb +9 -6
  121. data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
  122. data/lib/action_dispatch/testing/assertions/response.rb +9 -15
  123. data/lib/action_dispatch/testing/assertions/routing.rb +22 -22
  124. data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
  125. data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
  126. data/lib/action_dispatch/testing/assertions.rb +11 -7
  127. data/lib/action_dispatch/testing/integration.rb +31 -29
  128. data/lib/action_dispatch/testing/test_request.rb +1 -1
  129. data/lib/action_dispatch/testing/test_response.rb +1 -5
  130. data/lib/action_dispatch.rb +5 -8
  131. data/lib/action_pack/gem_version.rb +15 -0
  132. data/lib/action_pack/version.rb +4 -7
  133. data/lib/action_pack.rb +1 -1
  134. metadata +77 -159
  135. data/lib/abstract_controller/layouts.rb +0 -423
  136. data/lib/abstract_controller/view_paths.rb +0 -96
  137. data/lib/action_controller/deprecated/integration_test.rb +0 -5
  138. data/lib/action_controller/deprecated.rb +0 -7
  139. data/lib/action_controller/metal/responder.rb +0 -287
  140. data/lib/action_controller/record_identifier.rb +0 -31
  141. data/lib/action_controller/vendor/html-scanner.rb +0 -5
  142. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -24
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -7
  144. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -43
  145. data/lib/action_view/base.rb +0 -201
  146. data/lib/action_view/buffers.rb +0 -49
  147. data/lib/action_view/context.rb +0 -36
  148. data/lib/action_view/dependency_tracker.rb +0 -93
  149. data/lib/action_view/digestor.rb +0 -113
  150. data/lib/action_view/flows.rb +0 -76
  151. data/lib/action_view/helpers/active_model_helper.rb +0 -49
  152. data/lib/action_view/helpers/asset_tag_helper.rb +0 -320
  153. data/lib/action_view/helpers/asset_url_helper.rb +0 -355
  154. data/lib/action_view/helpers/atom_feed_helper.rb +0 -203
  155. data/lib/action_view/helpers/cache_helper.rb +0 -196
  156. data/lib/action_view/helpers/capture_helper.rb +0 -216
  157. data/lib/action_view/helpers/controller_helper.rb +0 -25
  158. data/lib/action_view/helpers/csrf_helper.rb +0 -30
  159. data/lib/action_view/helpers/date_helper.rb +0 -1083
  160. data/lib/action_view/helpers/debug_helper.rb +0 -39
  161. data/lib/action_view/helpers/form_helper.rb +0 -1880
  162. data/lib/action_view/helpers/form_options_helper.rb +0 -838
  163. data/lib/action_view/helpers/form_tag_helper.rb +0 -785
  164. data/lib/action_view/helpers/javascript_helper.rb +0 -117
  165. data/lib/action_view/helpers/number_helper.rb +0 -441
  166. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  167. data/lib/action_view/helpers/record_tag_helper.rb +0 -106
  168. data/lib/action_view/helpers/rendering_helper.rb +0 -90
  169. data/lib/action_view/helpers/sanitize_helper.rb +0 -256
  170. data/lib/action_view/helpers/tag_helper.rb +0 -173
  171. data/lib/action_view/helpers/tags/base.rb +0 -148
  172. data/lib/action_view/helpers/tags/check_box.rb +0 -64
  173. data/lib/action_view/helpers/tags/checkable.rb +0 -16
  174. data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -44
  175. data/lib/action_view/helpers/tags/collection_helpers.rb +0 -84
  176. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -36
  177. data/lib/action_view/helpers/tags/collection_select.rb +0 -28
  178. data/lib/action_view/helpers/tags/color_field.rb +0 -25
  179. data/lib/action_view/helpers/tags/date_field.rb +0 -13
  180. data/lib/action_view/helpers/tags/date_select.rb +0 -72
  181. data/lib/action_view/helpers/tags/datetime_field.rb +0 -22
  182. data/lib/action_view/helpers/tags/datetime_local_field.rb +0 -19
  183. data/lib/action_view/helpers/tags/datetime_select.rb +0 -8
  184. data/lib/action_view/helpers/tags/email_field.rb +0 -8
  185. data/lib/action_view/helpers/tags/file_field.rb +0 -8
  186. data/lib/action_view/helpers/tags/grouped_collection_select.rb +0 -29
  187. data/lib/action_view/helpers/tags/hidden_field.rb +0 -8
  188. data/lib/action_view/helpers/tags/label.rb +0 -66
  189. data/lib/action_view/helpers/tags/month_field.rb +0 -13
  190. data/lib/action_view/helpers/tags/number_field.rb +0 -18
  191. data/lib/action_view/helpers/tags/password_field.rb +0 -12
  192. data/lib/action_view/helpers/tags/radio_button.rb +0 -31
  193. data/lib/action_view/helpers/tags/range_field.rb +0 -8
  194. data/lib/action_view/helpers/tags/search_field.rb +0 -24
  195. data/lib/action_view/helpers/tags/select.rb +0 -40
  196. data/lib/action_view/helpers/tags/tel_field.rb +0 -8
  197. data/lib/action_view/helpers/tags/text_area.rb +0 -18
  198. data/lib/action_view/helpers/tags/text_field.rb +0 -29
  199. data/lib/action_view/helpers/tags/time_field.rb +0 -13
  200. data/lib/action_view/helpers/tags/time_select.rb +0 -8
  201. data/lib/action_view/helpers/tags/time_zone_select.rb +0 -20
  202. data/lib/action_view/helpers/tags/url_field.rb +0 -8
  203. data/lib/action_view/helpers/tags/week_field.rb +0 -13
  204. data/lib/action_view/helpers/tags.rb +0 -39
  205. data/lib/action_view/helpers/text_helper.rb +0 -443
  206. data/lib/action_view/helpers/translation_helper.rb +0 -107
  207. data/lib/action_view/helpers/url_helper.rb +0 -635
  208. data/lib/action_view/helpers.rb +0 -58
  209. data/lib/action_view/locale/en.yml +0 -56
  210. data/lib/action_view/log_subscriber.rb +0 -30
  211. data/lib/action_view/lookup_context.rb +0 -241
  212. data/lib/action_view/model_naming.rb +0 -12
  213. data/lib/action_view/path_set.rb +0 -77
  214. data/lib/action_view/railtie.rb +0 -43
  215. data/lib/action_view/record_identifier.rb +0 -84
  216. data/lib/action_view/renderer/abstract_renderer.rb +0 -47
  217. data/lib/action_view/renderer/partial_renderer.rb +0 -492
  218. data/lib/action_view/renderer/renderer.rb +0 -50
  219. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -103
  220. data/lib/action_view/renderer/template_renderer.rb +0 -96
  221. data/lib/action_view/routing_url_for.rb +0 -107
  222. data/lib/action_view/tasks/dependencies.rake +0 -17
  223. data/lib/action_view/template/error.rb +0 -138
  224. data/lib/action_view/template/handlers/builder.rb +0 -26
  225. data/lib/action_view/template/handlers/erb.rb +0 -146
  226. data/lib/action_view/template/handlers/raw.rb +0 -11
  227. data/lib/action_view/template/handlers.rb +0 -53
  228. data/lib/action_view/template/resolver.rb +0 -326
  229. data/lib/action_view/template/text.rb +0 -34
  230. data/lib/action_view/template/types.rb +0 -57
  231. data/lib/action_view/template.rb +0 -339
  232. data/lib/action_view/test_case.rb +0 -270
  233. data/lib/action_view/testing/resolvers.rb +0 -50
  234. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  235. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  236. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  237. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  238. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  239. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
  240. data/lib/action_view/vendor/html-scanner.rb +0 -20
  241. data/lib/action_view.rb +0 -93
@@ -1,42 +1,37 @@
1
1
  require 'action_dispatch/journey'
2
2
  require 'forwardable'
3
- require 'thread_safe'
3
+ require 'active_support/concern'
4
4
  require 'active_support/core_ext/object/to_query'
5
5
  require 'active_support/core_ext/hash/slice'
6
6
  require 'active_support/core_ext/module/remove_method'
7
7
  require 'active_support/core_ext/array/extract_options'
8
+ require 'active_support/core_ext/string/filters'
8
9
  require 'action_controller/metal/exceptions'
10
+ require 'action_dispatch/http/request'
11
+ require 'action_dispatch/routing/endpoint'
9
12
 
10
13
  module ActionDispatch
11
14
  module Routing
12
- class RouteSet #:nodoc:
15
+ # :stopdoc:
16
+ class RouteSet
13
17
  # Since the router holds references to many parts of the system
14
18
  # like engines, controllers and the application itself, inspecting
15
19
  # the route set can actually be really slow, therefore we default
16
20
  # alias inspect to to_s.
17
21
  alias inspect to_s
18
22
 
19
- PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
23
+ mattr_accessor :relative_url_root
20
24
 
21
- class Dispatcher #:nodoc:
22
- def initialize(options={})
23
- @defaults = options[:defaults]
24
- @glob_param = options.delete(:glob)
25
- @controller_class_names = ThreadSafe::Cache.new
25
+ class Dispatcher < Routing::Endpoint
26
+ def initialize(defaults)
27
+ @defaults = defaults
26
28
  end
27
29
 
28
- def call(env)
29
- params = env[PARAMETERS_KEY]
30
+ def dispatcher?; true; end
30
31
 
31
- # If any of the path parameters has a invalid encoding then
32
- # raise since it's likely to trigger errors further on.
33
- params.each do |key, value|
34
- next unless value.respond_to?(:valid_encoding?)
35
-
36
- unless value.valid_encoding?
37
- raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
38
- end
39
- end
32
+ def serve(req)
33
+ req.check_path_parameters!
34
+ params = req.path_parameters
40
35
 
41
36
  prepare_params!(params)
42
37
 
@@ -45,13 +40,12 @@ module ActionDispatch
45
40
  return [404, {'X-Cascade' => 'pass'}, []]
46
41
  end
47
42
 
48
- dispatch(controller, params[:action], env)
43
+ dispatch(controller, params[:action], req.env)
49
44
  end
50
45
 
51
46
  def prepare_params!(params)
52
47
  normalize_controller!(params)
53
48
  merge_default_action!(params)
54
- split_glob_param!(params) if @glob_param
55
49
  end
56
50
 
57
51
  # If this is a default_controller (i.e. a controller specified by the user)
@@ -72,7 +66,7 @@ module ActionDispatch
72
66
  private
73
67
 
74
68
  def controller_reference(controller_param)
75
- const_name = @controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
69
+ const_name = "#{controller_param.camelize}Controller"
76
70
  ActiveSupport::Dependencies.constantize(const_name)
77
71
  end
78
72
 
@@ -87,47 +81,79 @@ module ActionDispatch
87
81
  def merge_default_action!(params)
88
82
  params[:action] ||= 'index'
89
83
  end
90
-
91
- def split_glob_param!(params)
92
- params[@glob_param] = params[@glob_param].split('/').map { |v| URI.parser.unescape(v) }
93
- end
94
84
  end
95
85
 
96
86
  # A NamedRouteCollection instance is a collection of named routes, and also
97
87
  # maintains an anonymous module that can be used to install helpers for the
98
88
  # named routes.
99
- class NamedRouteCollection #:nodoc:
89
+ class NamedRouteCollection
100
90
  include Enumerable
101
- attr_reader :routes, :helpers, :module
91
+ attr_reader :routes, :url_helpers_module
102
92
 
103
93
  def initialize
104
94
  @routes = {}
105
- @helpers = []
106
- @module = Module.new
95
+ @path_helpers = Set.new
96
+ @url_helpers = Set.new
97
+ @url_helpers_module = Module.new
98
+ @path_helpers_module = Module.new
99
+ end
100
+
101
+ def route_defined?(name)
102
+ key = name.to_sym
103
+ @path_helpers.include?(key) || @url_helpers.include?(key)
104
+ end
105
+
106
+ def helpers
107
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
108
+ `named_routes.helpers` is deprecated, please use `route_defined?(route_name)`
109
+ to see if a named route was defined.
110
+ MSG
111
+ @path_helpers + @url_helpers
107
112
  end
108
113
 
109
114
  def helper_names
110
- @helpers.map(&:to_s)
115
+ @path_helpers.map(&:to_s) + @url_helpers.map(&:to_s)
111
116
  end
112
117
 
113
118
  def clear!
114
- @helpers.each do |helper|
115
- @module.remove_possible_method helper
119
+ @path_helpers.each do |helper|
120
+ @path_helpers_module.send :undef_method, helper
121
+ end
122
+
123
+ @url_helpers.each do |helper|
124
+ @url_helpers_module.send :undef_method, helper
116
125
  end
117
126
 
118
127
  @routes.clear
119
- @helpers.clear
128
+ @path_helpers.clear
129
+ @url_helpers.clear
120
130
  end
121
131
 
122
132
  def add(name, route)
123
- routes[name.to_sym] = route
124
- define_named_route_methods(name, route)
133
+ key = name.to_sym
134
+ path_name = :"#{name}_path"
135
+ url_name = :"#{name}_url"
136
+
137
+ if routes.key? key
138
+ @path_helpers_module.send :undef_method, path_name
139
+ @url_helpers_module.send :undef_method, url_name
140
+ end
141
+ routes[key] = route
142
+ define_url_helper @path_helpers_module, route, path_name, route.defaults, name, LEGACY
143
+ define_url_helper @url_helpers_module, route, url_name, route.defaults, name, UNKNOWN
144
+
145
+ @path_helpers << path_name
146
+ @url_helpers << url_name
125
147
  end
126
148
 
127
149
  def get(name)
128
150
  routes[name.to_sym]
129
151
  end
130
152
 
153
+ def key?(name)
154
+ routes.key? name.to_sym
155
+ end
156
+
131
157
  alias []= add
132
158
  alias [] get
133
159
  alias clear clear!
@@ -145,35 +171,54 @@ module ActionDispatch
145
171
  routes.length
146
172
  end
147
173
 
148
- class UrlHelper # :nodoc:
149
- def self.create(route, options)
174
+ def path_helpers_module(warn = false)
175
+ if warn
176
+ mod = @path_helpers_module
177
+ helpers = @path_helpers
178
+ Module.new do
179
+ include mod
180
+
181
+ helpers.each do |meth|
182
+ define_method(meth) do |*args, &block|
183
+ ActiveSupport::Deprecation.warn("The method `#{meth}` cannot be used here as a full URL is required. Use `#{meth.to_s.sub(/_path$/, '_url')}` instead")
184
+ super(*args, &block)
185
+ end
186
+ end
187
+ end
188
+ else
189
+ @path_helpers_module
190
+ end
191
+ end
192
+
193
+ class UrlHelper
194
+ def self.create(route, options, route_name, url_strategy)
150
195
  if optimize_helper?(route)
151
- OptimizedUrlHelper.new(route, options)
196
+ OptimizedUrlHelper.new(route, options, route_name, url_strategy)
152
197
  else
153
- new route, options
198
+ new route, options, route_name, url_strategy
154
199
  end
155
200
  end
156
201
 
157
202
  def self.optimize_helper?(route)
158
- route.requirements.except(:controller, :action).empty?
203
+ !route.glob? && route.path.requirements.empty?
159
204
  end
160
205
 
161
- class OptimizedUrlHelper < UrlHelper # :nodoc:
206
+ attr_reader :url_strategy, :route_name
207
+
208
+ class OptimizedUrlHelper < UrlHelper
162
209
  attr_reader :arg_size
163
210
 
164
- def initialize(route, options)
211
+ def initialize(route, options, route_name, url_strategy)
165
212
  super
166
- @path_parts = @route.required_parts
167
- @arg_size = @path_parts.size
168
- @string_route = @route.optimized_path
213
+ @required_parts = @route.required_parts
214
+ @arg_size = @required_parts.size
169
215
  end
170
216
 
171
- def call(t, args)
172
- if args.size == arg_size && !args.last.is_a?(Hash) && optimize_routes_generation?(t)
173
- options = @options.dup
174
- options.merge!(t.url_options) if t.respond_to?(:url_options)
217
+ def call(t, args, inner_options)
218
+ if args.size == arg_size && !inner_options && optimize_routes_generation?(t)
219
+ options = t.url_options.merge @options
175
220
  options[:path] = optimized_helper(args)
176
- ActionDispatch::Http::URL.url_for(options)
221
+ url_strategy.call options
177
222
  else
178
223
  super
179
224
  end
@@ -182,73 +227,99 @@ module ActionDispatch
182
227
  private
183
228
 
184
229
  def optimized_helper(args)
185
- path = @string_route.dup
186
- klass = Journey::Router::Utils
187
-
188
- @path_parts.zip(args) do |part, arg|
189
- parameterized_arg = arg.to_param
190
-
191
- if parameterized_arg.nil? || parameterized_arg.empty?
192
- raise_generation_error(args)
193
- end
230
+ params = parameterize_args(args)
231
+ missing_keys = missing_keys(params)
194
232
 
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))
233
+ unless missing_keys.empty?
234
+ raise_generation_error(params, missing_keys)
199
235
  end
200
- path
236
+
237
+ @route.format params
201
238
  end
202
239
 
203
240
  def optimize_routes_generation?(t)
204
241
  t.send(:optimize_routes_generation?)
205
242
  end
206
243
 
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
244
+ def parameterize_args(args)
245
+ params = {}
246
+ @required_parts.zip(args.map(&:to_param)) { |k,v| params[k] = v }
247
+ params
248
+ end
216
249
 
217
- parts << [part, arg]
218
- end
250
+ def missing_keys(args)
251
+ args.select{ |part, arg| arg.nil? || arg.empty? }.keys
252
+ end
219
253
 
220
- message = "No route matches #{Hash[parts].inspect}"
221
- message << " missing required keys: #{missing_keys.inspect}"
254
+ def raise_generation_error(args, missing_keys)
255
+ constraints = Hash[@route.requirements.merge(args).sort_by{|k,v| k.to_s}]
256
+ message = "No route matches #{constraints.inspect}"
257
+ message << " missing required keys: #{missing_keys.sort.inspect}"
222
258
 
223
259
  raise ActionController::UrlGenerationError, message
224
260
  end
225
261
  end
226
262
 
227
- def initialize(route, options)
263
+ def initialize(route, options, route_name, url_strategy)
228
264
  @options = options
229
- @segment_keys = route.segment_keys
265
+ @segment_keys = route.segment_keys.uniq
230
266
  @route = route
267
+ @url_strategy = url_strategy
268
+ @route_name = route_name
231
269
  end
232
270
 
233
- def call(t, args)
234
- t.url_for(handle_positional_args(t, args, @options, @segment_keys))
235
- end
271
+ def call(t, args, inner_options)
272
+ controller_options = t.url_options
273
+ options = controller_options.merge @options
274
+ hash = handle_positional_args(controller_options,
275
+ deprecate_string_options(inner_options) || {},
276
+ args,
277
+ options,
278
+ @segment_keys)
236
279
 
237
- def handle_positional_args(t, args, options, keys)
238
- inner_options = args.extract_options!
239
- result = options.dup
280
+ t._routes.url_for(hash, route_name, url_strategy)
281
+ end
240
282
 
283
+ def handle_positional_args(controller_options, inner_options, args, result, path_params)
241
284
  if args.size > 0
242
- if args.size < keys.size - 1 # take format into account
243
- keys -= t.url_options.keys if t.respond_to?(:url_options)
244
- keys -= options.keys
285
+ # take format into account
286
+ if path_params.include?(:format)
287
+ path_params_size = path_params.size - 1
288
+ else
289
+ path_params_size = path_params.size
245
290
  end
246
- keys -= inner_options.keys
247
- result.merge!(Hash[keys.zip(args)])
291
+
292
+ if args.size < path_params_size
293
+ path_params -= controller_options.keys
294
+ path_params -= result.keys
295
+ end
296
+ path_params.each { |param|
297
+ value = inner_options.fetch(param) { args.shift }
298
+
299
+ unless param == :format && value.nil?
300
+ result[param] = value
301
+ end
302
+ }
248
303
  end
249
304
 
250
305
  result.merge!(inner_options)
251
306
  end
307
+
308
+ DEPRECATED_STRING_OPTIONS = %w[controller action]
309
+
310
+ def deprecate_string_options(options)
311
+ options ||= {}
312
+ deprecated_string_options = options.keys & DEPRECATED_STRING_OPTIONS
313
+ if deprecated_string_options.any?
314
+ msg = "Calling URL helpers with string keys #{deprecated_string_options.join(", ")} is deprecated. Use symbols instead."
315
+ ActiveSupport::Deprecation.warn(msg)
316
+ deprecated_string_options.each do |option|
317
+ value = options.delete(option)
318
+ options[option.to_sym] = value
319
+ end
320
+ end
321
+ options
322
+ end
252
323
  end
253
324
 
254
325
  private
@@ -265,26 +336,48 @@ module ActionDispatch
265
336
  #
266
337
  # foo_url(bar, baz, bang, sort_by: 'baz')
267
338
  #
268
- def define_url_helper(route, name, options)
269
- helper = UrlHelper.create(route, options.dup)
270
-
271
- @module.remove_possible_method name
272
- @module.module_eval do
339
+ def define_url_helper(mod, route, name, opts, route_key, url_strategy)
340
+ helper = UrlHelper.create(route, opts, route_key, url_strategy)
341
+ mod.module_eval do
273
342
  define_method(name) do |*args|
274
- helper.call self, args
343
+ options = nil
344
+ options = args.pop if args.last.is_a? Hash
345
+ helper.call self, args, options
275
346
  end
276
347
  end
277
-
278
- helpers << name
279
348
  end
349
+ end
280
350
 
281
- def define_named_route_methods(name, route)
282
- define_url_helper route, :"#{name}_path",
283
- route.defaults.merge(:use_route => name, :only_path => true)
284
- define_url_helper route, :"#{name}_url",
285
- route.defaults.merge(:use_route => name, :only_path => false)
351
+ # strategy for building urls to send to the client
352
+ PATH = ->(options) { ActionDispatch::Http::URL.path_for(options) }
353
+ FULL = ->(options) { ActionDispatch::Http::URL.full_url_for(options) }
354
+ UNKNOWN = ->(options) { ActionDispatch::Http::URL.url_for(options) }
355
+ LEGACY = ->(options) {
356
+ if options.key?(:only_path)
357
+ if options[:only_path]
358
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
359
+ You are calling a `*_path` helper with the `only_path` option
360
+ explicitly set to `true`. This option will stop working on
361
+ path helpers in Rails 5. Simply remove the `only_path: true`
362
+ argument from your call as it is redundant when applied to a
363
+ path helper.
364
+ MSG
365
+
366
+ PATH.call(options)
367
+ else
368
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
369
+ You are calling a `*_path` helper with the `only_path` option
370
+ explicitly set to `false`. This option will stop working on
371
+ path helpers in Rails 5. Use the corresponding `*_url` helper
372
+ instead.
373
+ MSG
374
+
375
+ FULL.call(options)
376
+ end
377
+ else
378
+ PATH.call(options)
286
379
  end
287
- end
380
+ }
288
381
 
289
382
  attr_accessor :formatter, :set, :named_routes, :default_scope, :router
290
383
  attr_accessor :disable_clear_and_finalize, :resources_path_names
@@ -298,7 +391,7 @@ module ActionDispatch
298
391
 
299
392
  def initialize(request_class = ActionDispatch::Request)
300
393
  self.named_routes = NamedRouteCollection.new
301
- self.resources_path_names = self.class.default_resources_path_names.dup
394
+ self.resources_path_names = self.class.default_resources_path_names
302
395
  self.default_url_options = {}
303
396
  self.request_class = request_class
304
397
 
@@ -308,9 +401,7 @@ module ActionDispatch
308
401
  @finalized = false
309
402
 
310
403
  @set = Journey::Routes.new
311
- @router = Journey::Router.new(@set, {
312
- :parameters_key => PARAMETERS_KEY,
313
- :request_class => request_class})
404
+ @router = Journey::Router.new @set
314
405
  @formatter = Journey::Formatter.new @set
315
406
  end
316
407
 
@@ -341,6 +432,7 @@ module ActionDispatch
341
432
  mapper.instance_exec(&block)
342
433
  end
343
434
  end
435
+ private :eval_block
344
436
 
345
437
  def finalize!
346
438
  return if @finalized
@@ -356,12 +448,16 @@ module ActionDispatch
356
448
  @prepend.each { |blk| eval_block(blk) }
357
449
  end
358
450
 
359
- module MountedHelpers #:nodoc:
451
+ def dispatcher(defaults)
452
+ Routing::RouteSet::Dispatcher.new(defaults)
453
+ end
454
+
455
+ module MountedHelpers
360
456
  extend ActiveSupport::Concern
361
457
  include UrlFor
362
458
  end
363
459
 
364
- # Contains all the mounted helpers accross different
460
+ # Contains all the mounted helpers across different
365
461
  # engines and the `main_app` helper for the application.
366
462
  # You can include this in your classes if you want to
367
463
  # access routes for other engines.
@@ -373,9 +469,11 @@ module ActionDispatch
373
469
  return if MountedHelpers.method_defined?(name)
374
470
 
375
471
  routes = self
472
+ helpers = routes.url_helpers
473
+
376
474
  MountedHelpers.class_eval do
377
475
  define_method "_#{name}" do
378
- RoutesProxy.new(routes, _routes_context)
476
+ RoutesProxy.new(routes, _routes_context, helpers)
379
477
  end
380
478
  end
381
479
 
@@ -386,40 +484,57 @@ module ActionDispatch
386
484
  RUBY
387
485
  end
388
486
 
389
- def url_helpers
390
- @url_helpers ||= begin
391
- routes = self
487
+ def url_helpers(supports_path = true)
488
+ routes = self
392
489
 
393
- Module.new do
394
- extend ActiveSupport::Concern
395
- include UrlFor
490
+ Module.new do
491
+ extend ActiveSupport::Concern
492
+ include UrlFor
493
+
494
+ # Define url_for in the singleton level so one can do:
495
+ # Rails.application.routes.url_helpers.url_for(args)
496
+ @_routes = routes
497
+ class << self
498
+ delegate :url_for, :optimize_routes_generation?, to: '@_routes'
499
+ attr_reader :_routes
500
+ def url_options; {}; end
501
+ end
396
502
 
397
- # Define url_for in the singleton level so one can do:
398
- # Rails.application.routes.url_helpers.url_for(args)
399
- @_routes = routes
400
- class << self
401
- delegate :url_for, :optimize_routes_generation?, :to => '@_routes'
402
- end
503
+ url_helpers = routes.named_routes.url_helpers_module
403
504
 
404
- # Make named_routes available in the module singleton
405
- # as well, so one can do:
406
- # Rails.application.routes.url_helpers.posts_path
407
- extend routes.named_routes.module
505
+ # Make named_routes available in the module singleton
506
+ # as well, so one can do:
507
+ # Rails.application.routes.url_helpers.posts_path
508
+ extend url_helpers
408
509
 
409
- # Any class that includes this module will get all
410
- # named routes...
411
- include routes.named_routes.module
510
+ # Any class that includes this module will get all
511
+ # named routes...
512
+ include url_helpers
412
513
 
413
- # plus a singleton class method called _routes ...
414
- included do
415
- singleton_class.send(:redefine_method, :_routes) { routes }
416
- end
514
+ if supports_path
515
+ path_helpers = routes.named_routes.path_helpers_module
516
+ else
517
+ path_helpers = routes.named_routes.path_helpers_module(true)
518
+ end
417
519
 
418
- # And an instance method _routes. Note that
419
- # UrlFor (included in this module) add extra
420
- # conveniences for working with @_routes.
421
- define_method(:_routes) { @_routes || routes }
520
+ include path_helpers
521
+ extend path_helpers
522
+
523
+ # plus a singleton class method called _routes ...
524
+ included do
525
+ singleton_class.send(:redefine_method, :_routes) { routes }
422
526
  end
527
+
528
+ # And an instance method _routes. Note that
529
+ # UrlFor (included in this module) add extra
530
+ # conveniences for working with @_routes.
531
+ define_method(:_routes) { @_routes || routes }
532
+
533
+ define_method(:_generate_paths_by_default) do
534
+ supports_path
535
+ end
536
+
537
+ private :_generate_paths_by_default
423
538
  end
424
539
  end
425
540
 
@@ -438,7 +553,9 @@ module ActionDispatch
438
553
  "http://guides.rubyonrails.org/routing.html#restricting-the-routes-created"
439
554
  end
440
555
 
441
- path = build_path(conditions.delete(:path_info), requirements, SEPARATORS, anchor)
556
+ path = conditions.delete :path_info
557
+ ast = conditions.delete :parsed_path_info
558
+ path = build_path(path, ast, requirements, anchor)
442
559
  conditions = build_conditions(conditions, path.names.map { |x| x.to_sym })
443
560
 
444
561
  route = @set.add_route(app, path, conditions, defaults, name)
@@ -446,8 +563,9 @@ module ActionDispatch
446
563
  route
447
564
  end
448
565
 
449
- def build_path(path, requirements, separators, anchor)
566
+ def build_path(path, ast, requirements, anchor)
450
567
  strexp = Journey::Router::Strexp.new(
568
+ ast,
451
569
  path,
452
570
  requirements,
453
571
  SEPARATORS,
@@ -495,7 +613,7 @@ module ActionDispatch
495
613
  end
496
614
  private :build_conditions
497
615
 
498
- class Generator #:nodoc:
616
+ class Generator
499
617
  PARAMETERIZE = lambda do |name, value|
500
618
  if name == :controller
501
619
  value
@@ -508,17 +626,18 @@ module ActionDispatch
508
626
 
509
627
  attr_reader :options, :recall, :set, :named_route
510
628
 
511
- def initialize(options, recall, set)
512
- @named_route = options.delete(:use_route)
629
+ def initialize(named_route, options, recall, set)
630
+ @named_route = named_route
513
631
  @options = options.dup
514
632
  @recall = recall.dup
515
633
  @set = set
516
634
 
635
+ normalize_recall!
517
636
  normalize_options!
518
637
  normalize_controller_action_id!
519
638
  use_relative_controller!
520
639
  normalize_controller!
521
- handle_nil_action!
640
+ normalize_action!
522
641
  end
523
642
 
524
643
  def controller
@@ -537,6 +656,11 @@ module ActionDispatch
537
656
  end
538
657
  end
539
658
 
659
+ # Set 'index' as default action for recall
660
+ def normalize_recall!
661
+ @recall[:action] ||= 'index'
662
+ end
663
+
540
664
  def normalize_options!
541
665
  # If an explicit :controller was given, always make :action explicit
542
666
  # too, so that action expiry works as expected for things like
@@ -552,8 +676,8 @@ module ActionDispatch
552
676
  options[:controller] = options[:controller].to_s
553
677
  end
554
678
 
555
- if options[:action]
556
- options[:action] = options[:action].to_s
679
+ if options.key?(:action)
680
+ options[:action] = (options[:action] || 'index').to_s
557
681
  end
558
682
  end
559
683
 
@@ -563,8 +687,6 @@ module ActionDispatch
563
687
  # :controller, :action or :id is not found, don't pull any
564
688
  # more keys from the recall.
565
689
  def normalize_controller_action_id!
566
- @recall[:action] ||= 'index' if current_controller
567
-
568
690
  use_recall_for(:controller) or return
569
691
  use_recall_for(:action) or return
570
692
  use_recall_for(:id)
@@ -586,19 +708,17 @@ module ActionDispatch
586
708
  @options[:controller] = controller.sub(%r{^/}, '') if controller
587
709
  end
588
710
 
589
- # This handles the case of action: nil being explicitly passed.
590
- # It is identical to action: "index"
591
- def handle_nil_action!
592
- if options.has_key?(:action) && options[:action].nil?
593
- options[:action] = 'index'
711
+ # Move 'index' action from options to recall
712
+ def normalize_action!
713
+ if @options[:action] == 'index'
714
+ @recall[:action] = @options.delete(:action)
594
715
  end
595
- recall[:action] = options.delete(:action) if options[:action] == 'index'
596
716
  end
597
717
 
598
718
  # Generates a path from routes, returns [path, params].
599
719
  # If no route is generated the formatter will raise ActionController::UrlGenerationError
600
720
  def generate
601
- @set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE)
721
+ @set.formatter.generate(named_route, options, recall, PARAMETERIZE)
602
722
  end
603
723
 
604
724
  def different_controller?
@@ -623,61 +743,78 @@ module ActionDispatch
623
743
  end
624
744
 
625
745
  def generate_extras(options, recall={})
626
- path, params = generate(options, recall)
746
+ route_key = options.delete :use_route
747
+ path, params = generate(route_key, options, recall)
627
748
  return path, params.keys
628
749
  end
629
750
 
630
- def generate(options, recall = {})
631
- Generator.new(options, recall, self).generate
751
+ def generate(route_key, options, recall = {})
752
+ Generator.new(route_key, options, recall, self).generate
632
753
  end
754
+ private :generate
633
755
 
634
756
  RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
635
757
  :trailing_slash, :anchor, :params, :only_path, :script_name,
636
- :original_script_name]
758
+ :original_script_name, :relative_url_root]
637
759
 
638
- def mounted?
639
- false
760
+ def optimize_routes_generation?
761
+ default_url_options.empty?
640
762
  end
641
763
 
642
- def optimize_routes_generation?
643
- !mounted? && default_url_options.empty?
764
+ def find_script_name(options)
765
+ options.delete(:script_name) || find_relative_url_root(options) || ''
644
766
  end
645
767
 
646
- def _generate_prefix(options = {})
647
- nil
768
+ def find_relative_url_root(options)
769
+ options.delete(:relative_url_root) || relative_url_root
648
770
  end
649
771
 
650
- # The +options+ argument must be +nil+ or a hash whose keys are *symbols*.
651
- def url_for(options)
652
- options = default_url_options.merge(options || {})
772
+ def path_for(options, route_name = nil)
773
+ url_for(options, route_name, PATH)
774
+ end
775
+
776
+ # The +options+ argument must be a hash whose keys are *symbols*.
777
+ def url_for(options, route_name = nil, url_strategy = UNKNOWN)
778
+ options = default_url_options.merge options
779
+
780
+ user = password = nil
781
+
782
+ if options[:user] && options[:password]
783
+ user = options.delete :user
784
+ password = options.delete :password
785
+ end
653
786
 
654
- user, password = extract_authentication(options)
655
- recall = options.delete(:_recall)
787
+ recall = options.delete(:_recall) { {} }
656
788
 
657
- original_script_name = options.delete(:original_script_name).presence
658
- script_name = options.delete(:script_name).presence || _generate_prefix(options)
789
+ original_script_name = options.delete(:original_script_name)
790
+ script_name = find_script_name options
659
791
 
660
- if script_name && original_script_name
792
+ if original_script_name
661
793
  script_name = original_script_name + script_name
662
794
  end
663
795
 
664
- path_options = options.except(*RESERVED_OPTIONS)
665
- path_options = yield(path_options) if block_given?
796
+ path_options = options.dup
797
+ RESERVED_OPTIONS.each { |ro| path_options.delete ro }
666
798
 
667
- path, params = generate(path_options, recall || {})
668
- params.merge!(options[:params] || {})
799
+ path, params = generate(route_name, path_options, recall)
669
800
 
670
- ActionDispatch::Http::URL.url_for(options.merge!({
671
- :path => path,
672
- :script_name => script_name,
673
- :params => params,
674
- :user => user,
675
- :password => password
676
- }))
801
+ if options.key? :params
802
+ params.merge! options[:params]
803
+ end
804
+
805
+ options[:path] = path
806
+ options[:script_name] = script_name
807
+ options[:params] = params
808
+ options[:user] = user
809
+ options[:password] = password
810
+
811
+ url_strategy.call options
677
812
  end
678
813
 
679
814
  def call(env)
680
- @router.call(env)
815
+ req = request_class.new(env)
816
+ req.path_info = Journey::Router::Utils.normalize_path(req.path_info)
817
+ @router.serve(req)
681
818
  end
682
819
 
683
820
  def recognize_path(path, environment = {})
@@ -691,8 +828,8 @@ module ActionDispatch
691
828
  raise ActionController::RoutingError, e.message
692
829
  end
693
830
 
694
- req = @request_class.new(env)
695
- @router.recognize(req) do |route, _matches, params|
831
+ req = request_class.new(env)
832
+ @router.recognize(req) do |route, params|
696
833
  params.merge!(extras)
697
834
  params.each do |key, value|
698
835
  if value.is_a?(String)
@@ -700,14 +837,12 @@ module ActionDispatch
700
837
  params[key] = URI.parser.unescape(value)
701
838
  end
702
839
  end
703
- old_params = env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
704
- env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] = (old_params || {}).merge(params)
705
- dispatcher = route.app
706
- while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
707
- dispatcher = dispatcher.app
708
- end
840
+ old_params = req.path_parameters
841
+ req.path_parameters = old_params.merge params
842
+ app = route.app
843
+ if app.matches?(req) && app.dispatcher?
844
+ dispatcher = app.app
709
845
 
710
- if dispatcher.is_a?(Dispatcher)
711
846
  if dispatcher.controller(params, false)
712
847
  dispatcher.prepare_params!(params)
713
848
  return params
@@ -719,17 +854,7 @@ module ActionDispatch
719
854
 
720
855
  raise ActionController::RoutingError, "No route matches #{path.inspect}"
721
856
  end
722
-
723
- private
724
-
725
- def extract_authentication(options)
726
- if options[:user] && options[:password]
727
- [options.delete(:user), options.delete(:password)]
728
- else
729
- nil
730
- end
731
- end
732
-
733
857
  end
858
+ # :startdoc:
734
859
  end
735
860
  end