actionpack 3.2.19 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +850 -401
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -288
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +39 -37
  7. data/lib/abstract_controller/callbacks.rb +101 -82
  8. data/lib/abstract_controller/collector.rb +7 -3
  9. data/lib/abstract_controller/helpers.rb +25 -13
  10. data/lib/abstract_controller/layouts.rb +74 -74
  11. data/lib/abstract_controller/logger.rb +1 -2
  12. data/lib/abstract_controller/rendering.rb +30 -13
  13. data/lib/abstract_controller/translation.rb +16 -1
  14. data/lib/abstract_controller/url_for.rb +6 -6
  15. data/lib/abstract_controller/view_paths.rb +1 -1
  16. data/lib/abstract_controller.rb +1 -8
  17. data/lib/action_controller/base.rb +46 -22
  18. data/lib/action_controller/caching/fragments.rb +23 -53
  19. data/lib/action_controller/caching.rb +46 -33
  20. data/lib/action_controller/deprecated/integration_test.rb +3 -0
  21. data/lib/action_controller/deprecated.rb +5 -1
  22. data/lib/action_controller/log_subscriber.rb +16 -8
  23. data/lib/action_controller/metal/conditional_get.rb +76 -32
  24. data/lib/action_controller/metal/data_streaming.rb +20 -26
  25. data/lib/action_controller/metal/exceptions.rb +19 -6
  26. data/lib/action_controller/metal/flash.rb +24 -9
  27. data/lib/action_controller/metal/force_ssl.rb +70 -12
  28. data/lib/action_controller/metal/head.rb +25 -4
  29. data/lib/action_controller/metal/helpers.rb +5 -9
  30. data/lib/action_controller/metal/hide_actions.rb +0 -1
  31. data/lib/action_controller/metal/http_authentication.rb +107 -83
  32. data/lib/action_controller/metal/implicit_render.rb +1 -1
  33. data/lib/action_controller/metal/instrumentation.rb +2 -1
  34. data/lib/action_controller/metal/live.rb +175 -0
  35. data/lib/action_controller/metal/mime_responds.rb +161 -47
  36. data/lib/action_controller/metal/params_wrapper.rb +112 -74
  37. data/lib/action_controller/metal/rack_delegation.rb +9 -3
  38. data/lib/action_controller/metal/redirecting.rb +15 -20
  39. data/lib/action_controller/metal/renderers.rb +11 -9
  40. data/lib/action_controller/metal/rendering.rb +9 -1
  41. data/lib/action_controller/metal/request_forgery_protection.rb +112 -19
  42. data/lib/action_controller/metal/responder.rb +20 -19
  43. data/lib/action_controller/metal/streaming.rb +12 -18
  44. data/lib/action_controller/metal/strong_parameters.rb +520 -0
  45. data/lib/action_controller/metal/testing.rb +13 -18
  46. data/lib/action_controller/metal/url_for.rb +28 -25
  47. data/lib/action_controller/metal.rb +17 -32
  48. data/lib/action_controller/model_naming.rb +12 -0
  49. data/lib/action_controller/railtie.rb +33 -17
  50. data/lib/action_controller/railties/helpers.rb +22 -0
  51. data/lib/action_controller/record_identifier.rb +18 -72
  52. data/lib/action_controller/test_case.rb +251 -131
  53. data/lib/action_controller/vendor/html-scanner.rb +4 -19
  54. data/lib/action_controller.rb +15 -6
  55. data/lib/action_dispatch/http/cache.rb +63 -11
  56. data/lib/action_dispatch/http/filter_parameters.rb +18 -8
  57. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  58. data/lib/action_dispatch/http/headers.rb +49 -17
  59. data/lib/action_dispatch/http/mime_negotiation.rb +24 -1
  60. data/lib/action_dispatch/http/mime_type.rb +154 -100
  61. data/lib/action_dispatch/http/mime_types.rb +1 -1
  62. data/lib/action_dispatch/http/parameter_filter.rb +44 -46
  63. data/lib/action_dispatch/http/parameters.rb +28 -28
  64. data/lib/action_dispatch/http/rack_cache.rb +2 -3
  65. data/lib/action_dispatch/http/request.rb +64 -18
  66. data/lib/action_dispatch/http/response.rb +130 -35
  67. data/lib/action_dispatch/http/upload.rb +63 -20
  68. data/lib/action_dispatch/http/url.rb +98 -35
  69. data/lib/action_dispatch/journey/backwards.rb +5 -0
  70. data/lib/action_dispatch/journey/formatter.rb +146 -0
  71. data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
  72. data/lib/action_dispatch/journey/gtg/simulator.rb +44 -0
  73. data/lib/action_dispatch/journey/gtg/transition_table.rb +156 -0
  74. data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
  75. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  76. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  77. data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
  78. data/lib/action_dispatch/journey/nodes/node.rb +124 -0
  79. data/lib/action_dispatch/journey/parser.rb +206 -0
  80. data/lib/action_dispatch/journey/parser.y +47 -0
  81. data/lib/action_dispatch/journey/parser_extras.rb +23 -0
  82. data/lib/action_dispatch/journey/path/pattern.rb +196 -0
  83. data/lib/action_dispatch/journey/route.rb +124 -0
  84. data/lib/action_dispatch/journey/router/strexp.rb +24 -0
  85. data/lib/action_dispatch/journey/router/utils.rb +54 -0
  86. data/lib/action_dispatch/journey/router.rb +166 -0
  87. data/lib/action_dispatch/journey/routes.rb +75 -0
  88. data/lib/action_dispatch/journey/scanner.rb +61 -0
  89. data/lib/action_dispatch/journey/visitors.rb +197 -0
  90. data/lib/action_dispatch/journey/visualizer/fsm.css +34 -0
  91. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  92. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  93. data/lib/action_dispatch/journey.rb +5 -0
  94. data/lib/action_dispatch/middleware/callbacks.rb +9 -4
  95. data/lib/action_dispatch/middleware/cookies.rb +259 -114
  96. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -17
  97. data/lib/action_dispatch/middleware/exception_wrapper.rb +29 -3
  98. data/lib/action_dispatch/middleware/flash.rb +58 -58
  99. data/lib/action_dispatch/middleware/params_parser.rb +14 -29
  100. data/lib/action_dispatch/middleware/public_exceptions.rb +30 -14
  101. data/lib/action_dispatch/middleware/reloader.rb +6 -6
  102. data/lib/action_dispatch/middleware/remote_ip.rb +145 -39
  103. data/lib/action_dispatch/middleware/request_id.rb +2 -6
  104. data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
  105. data/lib/action_dispatch/middleware/session/cookie_store.rb +82 -28
  106. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
  107. data/lib/action_dispatch/middleware/show_exceptions.rb +12 -45
  108. data/lib/action_dispatch/middleware/ssl.rb +70 -0
  109. data/lib/action_dispatch/middleware/stack.rb +6 -1
  110. data/lib/action_dispatch/middleware/static.rb +2 -1
  111. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +14 -11
  112. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +25 -0
  113. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +7 -9
  114. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +15 -9
  115. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +127 -5
  116. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +7 -2
  117. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +30 -15
  118. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +39 -13
  119. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +6 -2
  120. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  121. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +144 -0
  122. data/lib/action_dispatch/railtie.rb +16 -6
  123. data/lib/action_dispatch/request/session.rb +181 -0
  124. data/lib/action_dispatch/routing/inspector.rb +240 -0
  125. data/lib/action_dispatch/routing/mapper.rb +540 -291
  126. data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -20
  127. data/lib/action_dispatch/routing/redirection.rb +46 -29
  128. data/lib/action_dispatch/routing/route_set.rb +207 -164
  129. data/lib/action_dispatch/routing/routes_proxy.rb +2 -0
  130. data/lib/action_dispatch/routing/url_for.rb +48 -33
  131. data/lib/action_dispatch/routing.rb +48 -83
  132. data/lib/action_dispatch/testing/assertions/dom.rb +3 -13
  133. data/lib/action_dispatch/testing/assertions/response.rb +32 -40
  134. data/lib/action_dispatch/testing/assertions/routing.rb +42 -41
  135. data/lib/action_dispatch/testing/assertions/selector.rb +17 -22
  136. data/lib/action_dispatch/testing/assertions/tag.rb +20 -23
  137. data/lib/action_dispatch/testing/integration.rb +65 -51
  138. data/lib/action_dispatch/testing/test_process.rb +9 -6
  139. data/lib/action_dispatch/testing/test_request.rb +7 -3
  140. data/lib/action_dispatch.rb +21 -15
  141. data/lib/action_pack/version.rb +7 -6
  142. data/lib/action_pack.rb +1 -1
  143. data/lib/action_view/base.rb +15 -34
  144. data/lib/action_view/buffers.rb +7 -1
  145. data/lib/action_view/context.rb +4 -4
  146. data/lib/action_view/dependency_tracker.rb +93 -0
  147. data/lib/action_view/digestor.rb +85 -0
  148. data/lib/action_view/flows.rb +1 -4
  149. data/lib/action_view/helpers/active_model_helper.rb +3 -4
  150. data/lib/action_view/helpers/asset_tag_helper.rb +215 -352
  151. data/lib/action_view/helpers/asset_url_helper.rb +355 -0
  152. data/lib/action_view/helpers/atom_feed_helper.rb +13 -10
  153. data/lib/action_view/helpers/cache_helper.rb +150 -18
  154. data/lib/action_view/helpers/capture_helper.rb +44 -31
  155. data/lib/action_view/helpers/csrf_helper.rb +0 -2
  156. data/lib/action_view/helpers/date_helper.rb +269 -248
  157. data/lib/action_view/helpers/debug_helper.rb +10 -11
  158. data/lib/action_view/helpers/form_helper.rb +931 -537
  159. data/lib/action_view/helpers/form_options_helper.rb +341 -166
  160. data/lib/action_view/helpers/form_tag_helper.rb +190 -90
  161. data/lib/action_view/helpers/javascript_helper.rb +23 -16
  162. data/lib/action_view/helpers/number_helper.rb +148 -329
  163. data/lib/action_view/helpers/output_safety_helper.rb +3 -3
  164. data/lib/action_view/helpers/record_tag_helper.rb +17 -22
  165. data/lib/action_view/helpers/rendering_helper.rb +2 -2
  166. data/lib/action_view/helpers/sanitize_helper.rb +3 -6
  167. data/lib/action_view/helpers/tag_helper.rb +46 -33
  168. data/lib/action_view/helpers/tags/base.rb +147 -0
  169. data/lib/action_view/helpers/tags/check_box.rb +64 -0
  170. data/lib/action_view/helpers/tags/checkable.rb +16 -0
  171. data/lib/action_view/helpers/tags/collection_check_boxes.rb +43 -0
  172. data/lib/action_view/helpers/tags/collection_helpers.rb +83 -0
  173. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
  174. data/lib/action_view/helpers/tags/collection_select.rb +28 -0
  175. data/lib/action_view/helpers/tags/color_field.rb +25 -0
  176. data/lib/action_view/helpers/tags/date_field.rb +13 -0
  177. data/lib/action_view/helpers/tags/date_select.rb +72 -0
  178. data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
  179. data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
  180. data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
  181. data/lib/action_view/helpers/tags/email_field.rb +8 -0
  182. data/lib/action_view/helpers/tags/file_field.rb +8 -0
  183. data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
  184. data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
  185. data/lib/action_view/helpers/tags/label.rb +65 -0
  186. data/lib/action_view/helpers/tags/month_field.rb +13 -0
  187. data/lib/action_view/helpers/tags/number_field.rb +18 -0
  188. data/lib/action_view/helpers/tags/password_field.rb +12 -0
  189. data/lib/action_view/helpers/tags/radio_button.rb +31 -0
  190. data/lib/action_view/helpers/tags/range_field.rb +8 -0
  191. data/lib/action_view/helpers/tags/search_field.rb +24 -0
  192. data/lib/action_view/helpers/tags/select.rb +40 -0
  193. data/lib/action_view/helpers/tags/tel_field.rb +8 -0
  194. data/lib/action_view/helpers/tags/text_area.rb +18 -0
  195. data/lib/action_view/helpers/tags/text_field.rb +29 -0
  196. data/lib/action_view/helpers/tags/time_field.rb +13 -0
  197. data/lib/action_view/helpers/tags/time_select.rb +8 -0
  198. data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
  199. data/lib/action_view/helpers/tags/url_field.rb +8 -0
  200. data/lib/action_view/helpers/tags/week_field.rb +13 -0
  201. data/lib/action_view/helpers/tags.rb +39 -0
  202. data/lib/action_view/helpers/text_helper.rb +130 -114
  203. data/lib/action_view/helpers/translation_helper.rb +32 -16
  204. data/lib/action_view/helpers/url_helper.rb +211 -270
  205. data/lib/action_view/helpers.rb +2 -4
  206. data/lib/action_view/locale/en.yml +1 -105
  207. data/lib/action_view/log_subscriber.rb +6 -4
  208. data/lib/action_view/lookup_context.rb +15 -28
  209. data/lib/action_view/model_naming.rb +12 -0
  210. data/lib/action_view/path_set.rb +8 -20
  211. data/lib/action_view/railtie.rb +6 -22
  212. data/lib/action_view/record_identifier.rb +84 -0
  213. data/lib/action_view/renderer/abstract_renderer.rb +25 -19
  214. data/lib/action_view/renderer/partial_renderer.rb +158 -81
  215. data/lib/action_view/renderer/renderer.rb +8 -12
  216. data/lib/action_view/renderer/streaming_template_renderer.rb +2 -5
  217. data/lib/action_view/renderer/template_renderer.rb +12 -10
  218. data/lib/action_view/routing_url_for.rb +107 -0
  219. data/lib/action_view/template/error.rb +22 -12
  220. data/lib/action_view/template/handlers/builder.rb +1 -1
  221. data/lib/action_view/template/handlers/erb.rb +40 -19
  222. data/lib/action_view/template/handlers/raw.rb +11 -0
  223. data/lib/action_view/template/handlers.rb +12 -9
  224. data/lib/action_view/template/resolver.rb +107 -53
  225. data/lib/action_view/template/text.rb +12 -8
  226. data/lib/action_view/template/types.rb +57 -0
  227. data/lib/action_view/template.rb +25 -23
  228. data/lib/action_view/test_case.rb +67 -42
  229. data/lib/{action_controller → action_view}/vendor/html-scanner/html/document.rb +0 -0
  230. data/lib/{action_controller → action_view}/vendor/html-scanner/html/node.rb +12 -12
  231. data/lib/{action_controller → action_view}/vendor/html-scanner/html/sanitizer.rb +13 -2
  232. data/lib/{action_controller → action_view}/vendor/html-scanner/html/selector.rb +9 -9
  233. data/lib/{action_controller → action_view}/vendor/html-scanner/html/tokenizer.rb +1 -1
  234. data/lib/{action_controller → action_view}/vendor/html-scanner/html/version.rb +0 -0
  235. data/lib/action_view/vendor/html-scanner.rb +20 -0
  236. data/lib/action_view.rb +17 -8
  237. metadata +184 -214
  238. data/lib/action_controller/caching/actions.rb +0 -185
  239. data/lib/action_controller/caching/pages.rb +0 -187
  240. data/lib/action_controller/caching/sweeping.rb +0 -97
  241. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  242. data/lib/action_controller/metal/compatibility.rb +0 -65
  243. data/lib/action_controller/metal/session_management.rb +0 -14
  244. data/lib/action_controller/railties/paths.rb +0 -25
  245. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  246. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  247. data/lib/action_dispatch/middleware/head.rb +0 -18
  248. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  249. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  250. data/lib/action_view/asset_paths.rb +0 -142
  251. data/lib/action_view/helpers/asset_paths.rb +0 -7
  252. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  253. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  254. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  255. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  256. data/lib/sprockets/assets.rake +0 -99
  257. data/lib/sprockets/bootstrap.rb +0 -37
  258. data/lib/sprockets/compressors.rb +0 -83
  259. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  260. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  261. data/lib/sprockets/helpers.rb +0 -6
  262. data/lib/sprockets/railtie.rb +0 -62
  263. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,9 +1,10 @@
1
- require 'journey'
1
+ require 'action_dispatch/journey'
2
2
  require 'forwardable'
3
- require 'active_support/core_ext/object/blank'
3
+ require 'thread_safe'
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
+ require 'active_support/core_ext/array/extract_options'
7
8
  require 'action_controller/metal/exceptions'
8
9
 
9
10
  module ActionDispatch
@@ -21,11 +22,22 @@ module ActionDispatch
21
22
  def initialize(options={})
22
23
  @defaults = options[:defaults]
23
24
  @glob_param = options.delete(:glob)
24
- @controllers = {}
25
+ @controller_class_names = ThreadSafe::Cache.new
25
26
  end
26
27
 
27
28
  def call(env)
28
29
  params = env[PARAMETERS_KEY]
30
+
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
40
+
29
41
  prepare_params!(params)
30
42
 
31
43
  # Just raise undefined constant errors if a controller was specified as default.
@@ -60,13 +72,8 @@ module ActionDispatch
60
72
  private
61
73
 
62
74
  def controller_reference(controller_param)
63
- controller_name = "#{controller_param.camelize}Controller"
64
-
65
- unless controller = @controllers[controller_param]
66
- controller = @controllers[controller_param] =
67
- ActiveSupport::Dependencies.reference(controller_name)
68
- end
69
- controller.get(controller_name)
75
+ const_name = @controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
76
+ ActiveSupport::Dependencies.constantize(const_name)
70
77
  end
71
78
 
72
79
  def dispatch(controller, action, env)
@@ -94,14 +101,13 @@ module ActionDispatch
94
101
  attr_reader :routes, :helpers, :module
95
102
 
96
103
  def initialize
97
- @routes = {}
104
+ @routes = {}
98
105
  @helpers = []
99
-
100
- @module = Module.new
106
+ @module = Module.new
101
107
  end
102
108
 
103
109
  def helper_names
104
- self.module.instance_methods.map(&:to_s)
110
+ @helpers.map(&:to_s)
105
111
  end
106
112
 
107
113
  def clear!
@@ -139,90 +145,125 @@ module ActionDispatch
139
145
  routes.length
140
146
  end
141
147
 
142
- def reset!
143
- old_routes = routes.dup
144
- clear!
145
- old_routes.each do |name, route|
146
- add(name, route)
148
+ class UrlHelper # :nodoc:
149
+ def self.create(route, options)
150
+ if optimize_helper?(route)
151
+ OptimizedUrlHelper.new(route, options)
152
+ else
153
+ new route, options
154
+ end
147
155
  end
148
- end
149
156
 
150
- def install(destinations = [ActionController::Base, ActionView::Base], regenerate = false)
151
- reset! if regenerate
152
- Array(destinations).each do |dest|
153
- dest.__send__(:include, @module)
157
+ def self.optimize_helper?(route)
158
+ route.requirements.except(:controller, :action).empty?
154
159
  end
155
- end
156
160
 
157
- private
158
- def url_helper_name(name, kind = :url)
159
- :"#{name}_#{kind}"
160
- end
161
+ class OptimizedUrlHelper < UrlHelper # :nodoc:
162
+ attr_reader :arg_size
161
163
 
162
- def hash_access_name(name, kind = :url)
163
- :"hash_for_#{name}_#{kind}"
164
- end
164
+ def initialize(route, options)
165
+ super
166
+ @path_parts = @route.required_parts
167
+ @arg_size = @path_parts.size
168
+ @string_route = @route.optimized_path
169
+ end
165
170
 
166
- def define_named_route_methods(name, route)
167
- {:url => {:only_path => false}, :path => {:only_path => true}}.each do |kind, opts|
168
- hash = route.defaults.merge(:use_route => name).merge(opts)
169
- define_hash_access route, name, kind, hash
170
- define_url_helper route, name, kind, hash
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)
175
+ options[:path] = optimized_helper(args)
176
+ ActionDispatch::Http::URL.url_for(options)
177
+ else
178
+ super
179
+ end
180
+ end
181
+
182
+ private
183
+
184
+ def optimized_helper(args)
185
+ path = @string_route.dup
186
+ klass = Journey::Router::Utils
187
+
188
+ @path_parts.zip(args) do |part, arg|
189
+ # Replace each route parameter
190
+ # e.g. :id for regular parameter or *path for globbing
191
+ # with ruby string interpolation code
192
+ path.gsub!(/(\*|:)#{part}/, klass.escape_fragment(arg.to_param))
193
+ end
194
+ path
195
+ end
196
+
197
+ def optimize_routes_generation?(t)
198
+ t.send(:optimize_routes_generation?)
171
199
  end
172
200
  end
173
201
 
174
- def define_hash_access(route, name, kind, options)
175
- selector = hash_access_name(name, kind)
202
+ def initialize(route, options)
203
+ @options = options
204
+ @segment_keys = route.segment_keys
205
+ @route = route
206
+ end
176
207
 
177
- # We use module_eval to avoid leaks
178
- @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
179
- remove_possible_method :#{selector}
180
- def #{selector}(*args)
181
- options = args.extract_options!
182
- result = #{options.inspect}
208
+ def call(t, args)
209
+ t.url_for(handle_positional_args(t, args, @options, @segment_keys))
210
+ end
183
211
 
184
- if args.size > 0
185
- result[:_positional_args] = args
186
- result[:_positional_keys] = #{route.segment_keys.inspect}
187
- end
212
+ def handle_positional_args(t, args, options, keys)
213
+ inner_options = args.extract_options!
214
+ result = options.dup
188
215
 
189
- result.merge(options)
216
+ if args.size > 0
217
+ if args.size < keys.size - 1 # take format into account
218
+ keys -= t.url_options.keys if t.respond_to?(:url_options)
219
+ keys -= options.keys
190
220
  end
191
- protected :#{selector}
192
- END_EVAL
193
- helpers << selector
221
+ keys -= inner_options.keys
222
+ result.merge!(Hash[keys.zip(args)])
223
+ end
224
+
225
+ result.merge!(inner_options)
194
226
  end
227
+ end
195
228
 
196
- # Create a url helper allowing ordered parameters to be associated
197
- # with corresponding dynamic segments, so you can do:
198
- #
199
- # foo_url(bar, baz, bang)
200
- #
201
- # Instead of:
202
- #
203
- # foo_url(:bar => bar, :baz => baz, :bang => bang)
204
- #
205
- # Also allow options hash, so you can do:
206
- #
207
- # foo_url(bar, baz, bang, :sort_by => 'baz')
208
- #
209
- def define_url_helper(route, name, kind, options)
210
- selector = url_helper_name(name, kind)
211
- hash_access_method = hash_access_name(name, kind)
212
-
213
- @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
214
- remove_possible_method :#{selector}
215
- def #{selector}(*args)
216
- url_for(#{hash_access_method}(*args))
217
- end
218
- END_EVAL
219
- helpers << selector
229
+ private
230
+ # Create a url helper allowing ordered parameters to be associated
231
+ # with corresponding dynamic segments, so you can do:
232
+ #
233
+ # foo_url(bar, baz, bang)
234
+ #
235
+ # Instead of:
236
+ #
237
+ # foo_url(bar: bar, baz: baz, bang: bang)
238
+ #
239
+ # Also allow options hash, so you can do:
240
+ #
241
+ # foo_url(bar, baz, bang, sort_by: 'baz')
242
+ #
243
+ def define_url_helper(route, name, options)
244
+ helper = UrlHelper.create(route, options.dup)
245
+
246
+ @module.remove_possible_method name
247
+ @module.module_eval do
248
+ define_method(name) do |*args|
249
+ helper.call self, args
250
+ end
220
251
  end
252
+
253
+ helpers << name
254
+ end
255
+
256
+ def define_named_route_methods(name, route)
257
+ define_url_helper route, :"#{name}_path",
258
+ route.defaults.merge(:use_route => name, :only_path => true)
259
+ define_url_helper route, :"#{name}_url",
260
+ route.defaults.merge(:use_route => name, :only_path => false)
261
+ end
221
262
  end
222
263
 
223
264
  attr_accessor :formatter, :set, :named_routes, :default_scope, :router
224
265
  attr_accessor :disable_clear_and_finalize, :resources_path_names
225
- attr_accessor :default_url_options, :request_class, :valid_conditions
266
+ attr_accessor :default_url_options, :request_class
226
267
 
227
268
  alias :routes :set
228
269
 
@@ -234,17 +275,7 @@ module ActionDispatch
234
275
  self.named_routes = NamedRouteCollection.new
235
276
  self.resources_path_names = self.class.default_resources_path_names.dup
236
277
  self.default_url_options = {}
237
-
238
278
  self.request_class = request_class
239
- @valid_conditions = {}
240
-
241
- request_class.public_instance_methods.each { |m|
242
- @valid_conditions[m.to_sym] = true
243
- }
244
- @valid_conditions[:controller] = true
245
- @valid_conditions[:action] = true
246
-
247
- self.valid_conditions.delete(:id)
248
279
 
249
280
  @append = []
250
281
  @prepend = []
@@ -300,14 +331,15 @@ module ActionDispatch
300
331
  @prepend.each { |blk| eval_block(blk) }
301
332
  end
302
333
 
303
- def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)
304
- Array(destinations).each { |d| d.module_eval { include Helpers } }
305
- named_routes.install(destinations, regenerate_code)
306
- end
307
-
308
- module MountedHelpers
334
+ module MountedHelpers #:nodoc:
335
+ extend ActiveSupport::Concern
336
+ include UrlFor
309
337
  end
310
338
 
339
+ # Contains all the mounted helpers accross different
340
+ # engines and the `main_app` helper for the application.
341
+ # You can include this in your classes if you want to
342
+ # access routes for other engines.
311
343
  def mounted_helpers
312
344
  MountedHelpers
313
345
  end
@@ -318,13 +350,13 @@ module ActionDispatch
318
350
  routes = self
319
351
  MountedHelpers.class_eval do
320
352
  define_method "_#{name}" do
321
- RoutesProxy.new(routes, self._routes_context)
353
+ RoutesProxy.new(routes, _routes_context)
322
354
  end
323
355
  end
324
356
 
325
- MountedHelpers.class_eval <<-RUBY
357
+ MountedHelpers.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
326
358
  def #{name}
327
- @#{name} ||= _#{name}
359
+ @_#{name} ||= _#{name}
328
360
  end
329
361
  RUBY
330
362
  end
@@ -333,28 +365,36 @@ module ActionDispatch
333
365
  @url_helpers ||= begin
334
366
  routes = self
335
367
 
336
- helpers = Module.new do
368
+ Module.new do
337
369
  extend ActiveSupport::Concern
338
370
  include UrlFor
339
371
 
372
+ # Define url_for in the singleton level so one can do:
373
+ # Rails.application.routes.url_helpers.url_for(args)
340
374
  @_routes = routes
341
375
  class << self
342
- delegate :url_for, :to => '@_routes'
376
+ delegate :url_for, :optimize_routes_generation?, :to => '@_routes'
343
377
  end
378
+
379
+ # Make named_routes available in the module singleton
380
+ # as well, so one can do:
381
+ # Rails.application.routes.url_helpers.posts_path
344
382
  extend routes.named_routes.module
345
383
 
346
- # ROUTES TODO: install_helpers isn't great... can we make a module with the stuff that
347
- # we can include?
348
- # Yes plz - JP
384
+ # Any class that includes this module will get all
385
+ # named routes...
386
+ include routes.named_routes.module
387
+
388
+ # plus a singleton class method called _routes ...
349
389
  included do
350
- routes.install_helpers(self)
351
390
  singleton_class.send(:redefine_method, :_routes) { routes }
352
391
  end
353
392
 
393
+ # And an instance method _routes. Note that
394
+ # UrlFor (included in this module) add extra
395
+ # conveniences for working with @_routes.
354
396
  define_method(:_routes) { @_routes || routes }
355
397
  end
356
-
357
- helpers
358
398
  end
359
399
  end
360
400
 
@@ -365,8 +405,16 @@ module ActionDispatch
365
405
  def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
366
406
  raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
367
407
 
408
+ if name && named_routes[name]
409
+ raise ArgumentError, "Invalid route name, already in use: '#{name}' \n" \
410
+ "You may have defined two routes with the same name using the `:as` option, or " \
411
+ "you may be overriding a route already defined by a resource with the same naming. " \
412
+ "For the latter, you can restrict the routes created with `resources` as explained here: \n" \
413
+ "http://guides.rubyonrails.org/routing.html#restricting-the-routes-created"
414
+ end
415
+
368
416
  path = build_path(conditions.delete(:path_info), requirements, SEPARATORS, anchor)
369
- conditions = build_conditions(conditions, valid_conditions, path.names.map { |x| x.to_sym })
417
+ conditions = build_conditions(conditions, path.names.map { |x| x.to_sym })
370
418
 
371
419
  route = @set.add_route(app, path, conditions, defaults, name)
372
420
  named_routes[name] = route if name
@@ -403,21 +451,22 @@ module ActionDispatch
403
451
  end
404
452
  private :build_path
405
453
 
406
- def build_conditions(current_conditions, req_predicates, path_values)
454
+ def build_conditions(current_conditions, path_values)
407
455
  conditions = current_conditions.dup
408
456
 
409
- verbs = conditions[:request_method] || []
410
-
411
457
  # Rack-Mount requires that :request_method be a regular expression.
412
458
  # :request_method represents the HTTP verb that matches this route.
413
459
  #
414
460
  # Here we munge values before they get sent on to rack-mount.
461
+ verbs = conditions[:request_method] || []
415
462
  unless verbs.empty?
416
463
  conditions[:request_method] = %r[^#{verbs.join('|')}$]
417
464
  end
418
- conditions.delete_if { |k,v| !(req_predicates.include?(k) || path_values.include?(k)) }
419
465
 
420
- conditions
466
+ conditions.keep_if do |k, _|
467
+ k == :action || k == :controller || k == :required_defaults ||
468
+ @request_class.public_method_defined?(k) || path_values.include?(k)
469
+ end
421
470
  end
422
471
  private :build_conditions
423
472
 
@@ -434,12 +483,11 @@ module ActionDispatch
434
483
 
435
484
  attr_reader :options, :recall, :set, :named_route
436
485
 
437
- def initialize(options, recall, set, extras = false)
486
+ def initialize(options, recall, set)
438
487
  @named_route = options.delete(:use_route)
439
488
  @options = options.dup
440
489
  @recall = recall.dup
441
490
  @set = set
442
- @extras = extras
443
491
 
444
492
  normalize_options!
445
493
  normalize_controller_action_id!
@@ -458,9 +506,7 @@ module ActionDispatch
458
506
 
459
507
  def use_recall_for(key)
460
508
  if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
461
- if named_route_exists?
462
- @options[key] = @recall.delete(key) if segment_keys.include?(key)
463
- else
509
+ if !named_route_exists? || segment_keys.include?(key)
464
510
  @options[key] = @recall.delete(key)
465
511
  end
466
512
  end
@@ -470,7 +516,7 @@ module ActionDispatch
470
516
  # If an explicit :controller was given, always make :action explicit
471
517
  # too, so that action expiry works as expected for things like
472
518
  #
473
- # generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
519
+ # generate({controller: 'content'}, {controller: 'content', action: 'show'})
474
520
  #
475
521
  # (the above is from the unit tests). In the above case, because the
476
522
  # controller was explicitly given, but no action, the action is implied to
@@ -499,7 +545,7 @@ module ActionDispatch
499
545
  use_recall_for(:id)
500
546
  end
501
547
 
502
- # if the current controller is "foo/bar/baz" and :controller => "baz/bat"
548
+ # if the current controller is "foo/bar/baz" and controller: "baz/bat"
503
549
  # is specified, the controller becomes "foo/baz/bat"
504
550
  def use_relative_controller!
505
551
  if !named_route && different_controller? && !controller.start_with?("/")
@@ -515,8 +561,8 @@ module ActionDispatch
515
561
  @options[:controller] = controller.sub(%r{^/}, '') if controller
516
562
  end
517
563
 
518
- # This handles the case of :action => nil being explicitly passed.
519
- # It is identical to :action => "index"
564
+ # This handles the case of action: nil being explicitly passed.
565
+ # It is identical to action: "index"
520
566
  def handle_nil_action!
521
567
  if options.has_key?(:action) && options[:action].nil?
522
568
  options[:action] = 'index'
@@ -524,20 +570,10 @@ module ActionDispatch
524
570
  recall[:action] = options.delete(:action) if options[:action] == 'index'
525
571
  end
526
572
 
573
+ # Generates a path from routes, returns [path, params].
574
+ # If no route is generated the formatter will raise ActionController::UrlGenerationError
527
575
  def generate
528
- path, params = @set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE)
529
-
530
- raise_routing_error unless path
531
-
532
- return [path, params.keys] if @extras
533
-
534
- [path, params]
535
- rescue Journey::Router::RoutingError
536
- raise_routing_error
537
- end
538
-
539
- def raise_routing_error
540
- raise ActionController::RoutingError, "No route matches #{options.inspect}"
576
+ @set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE)
541
577
  end
542
578
 
543
579
  def different_controller?
@@ -562,41 +598,53 @@ module ActionDispatch
562
598
  end
563
599
 
564
600
  def generate_extras(options, recall={})
565
- generate(options, recall, true)
601
+ path, params = generate(options, recall)
602
+ return path, params.keys
566
603
  end
567
604
 
568
- def generate(options, recall = {}, extras = false)
569
- Generator.new(options, recall, self, extras).generate
605
+ def generate(options, recall = {})
606
+ Generator.new(options, recall, self).generate
570
607
  end
571
608
 
572
609
  RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
573
- :trailing_slash, :anchor, :params, :only_path, :script_name]
610
+ :trailing_slash, :anchor, :params, :only_path, :script_name,
611
+ :original_script_name]
612
+
613
+ def mounted?
614
+ false
615
+ end
616
+
617
+ def optimize_routes_generation?
618
+ !mounted? && default_url_options.empty?
619
+ end
574
620
 
575
621
  def _generate_prefix(options = {})
576
622
  nil
577
623
  end
578
624
 
625
+ # The +options+ argument must be +nil+ or a hash whose keys are *symbols*.
579
626
  def url_for(options)
580
- finalize!
581
- options = (options || {}).reverse_merge!(default_url_options)
582
-
583
- handle_positional_args(options)
627
+ options = default_url_options.merge(options || {})
584
628
 
585
629
  user, password = extract_authentication(options)
586
- path_segments = options.delete(:_path_segments)
587
- script_name = options.delete(:script_name)
630
+ recall = options.delete(:_recall)
631
+
632
+ original_script_name = options.delete(:original_script_name).presence
633
+ script_name = options.delete(:script_name).presence || _generate_prefix(options)
588
634
 
589
- path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s
635
+ if script_name && original_script_name
636
+ script_name = original_script_name + script_name
637
+ end
590
638
 
591
639
  path_options = options.except(*RESERVED_OPTIONS)
592
640
  path_options = yield(path_options) if block_given?
593
641
 
594
- path_addition, params = generate(path_options, path_segments || {})
595
- path << path_addition
642
+ path, params = generate(path_options, recall || {})
596
643
  params.merge!(options[:params] || {})
597
644
 
598
645
  ActionDispatch::Http::URL.url_for(options.merge!({
599
646
  :path => path,
647
+ :script_name => script_name,
600
648
  :params => params,
601
649
  :user => user,
602
650
  :password => password
@@ -604,7 +652,6 @@ module ActionDispatch
604
652
  end
605
653
 
606
654
  def call(env)
607
- finalize!
608
655
  @router.call(env)
609
656
  end
610
657
 
@@ -614,28 +661,34 @@ module ActionDispatch
614
661
  extras = environment[:extras] || {}
615
662
 
616
663
  begin
617
- env = Rack::MockRequest.env_for(path, {:method => method, :params => extras})
664
+ env = Rack::MockRequest.env_for(path, {:method => method})
618
665
  rescue URI::InvalidURIError => e
619
666
  raise ActionController::RoutingError, e.message
620
667
  end
621
668
 
622
669
  req = @request_class.new(env)
623
- @router.recognize(req) do |route, matches, params|
670
+ @router.recognize(req) do |route, _matches, params|
671
+ params.merge!(extras)
624
672
  params.each do |key, value|
625
673
  if value.is_a?(String)
626
- value = value.dup.force_encoding(Encoding::BINARY) if value.encoding_aware?
674
+ value = value.dup.force_encoding(Encoding::BINARY)
627
675
  params[key] = URI.parser.unescape(value)
628
676
  end
629
677
  end
630
-
678
+ old_params = env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
679
+ env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] = (old_params || {}).merge(params)
631
680
  dispatcher = route.app
632
681
  while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
633
682
  dispatcher = dispatcher.app
634
683
  end
635
684
 
636
- if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false)
637
- dispatcher.prepare_params!(params)
638
- return params
685
+ if dispatcher.is_a?(Dispatcher)
686
+ if dispatcher.controller(params, false)
687
+ dispatcher.prepare_params!(params)
688
+ return params
689
+ else
690
+ raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
691
+ end
639
692
  end
640
693
  end
641
694
 
@@ -652,16 +705,6 @@ module ActionDispatch
652
705
  end
653
706
  end
654
707
 
655
- def handle_positional_args(options)
656
- return unless args = options.delete(:_positional_args)
657
-
658
- keys = options.delete(:_positional_keys)
659
- keys -= options.keys if args.size < keys.size - 1 # take format into account
660
-
661
- # Tell url_for to skip default_url_options
662
- options.merge!(Hash[args.zip(keys).map { |v, k| [k, v] }])
663
- end
664
-
665
708
  end
666
709
  end
667
710
  end
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/array/extract_options'
2
+
1
3
  module ActionDispatch
2
4
  module Routing
3
5
  class RoutesProxy #:nodoc: