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,4 +1,4 @@
1
- require 'active_support/core_ext/object/blank'
1
+ require 'thread_safe'
2
2
 
3
3
  module ActionView
4
4
  # = Action View Partials
@@ -10,16 +10,16 @@ module ActionView
10
10
  #
11
11
  # In a template for Advertiser#account:
12
12
  #
13
- # <%= render :partial => "account" %>
13
+ # <%= render partial: "account" %>
14
14
  #
15
15
  # This would render "advertiser/_account.html.erb".
16
16
  #
17
17
  # In another template for Advertiser#buy, we could have:
18
18
  #
19
- # <%= render :partial => "account", :locals => { :account => @buyer } %>
19
+ # <%= render partial: "account", locals: { account: @buyer } %>
20
20
  #
21
21
  # <% @advertisements.each do |ad| %>
22
- # <%= render :partial => "ad", :locals => { :ad => ad } %>
22
+ # <%= render partial: "ad", locals: { ad: ad } %>
23
23
  # <% end %>
24
24
  #
25
25
  # This would first render "advertiser/_account.html.erb" with @buyer passed in as the local variable +account+, then
@@ -30,21 +30,21 @@ module ActionView
30
30
  # By default <tt>ActionView::PartialRenderer</tt> doesn't have any local variables.
31
31
  # The <tt>:object</tt> option can be used to pass an object to the partial. For instance:
32
32
  #
33
- # <%= render :partial => "account", :object => @buyer %>
33
+ # <%= render partial: "account", object: @buyer %>
34
34
  #
35
- # would provide the +@buyer+ object to the partial, available under the local variable +account+ and is
35
+ # would provide the <tt>@buyer</tt> object to the partial, available under the local variable +account+ and is
36
36
  # equivalent to:
37
37
  #
38
- # <%= render :partial => "account", :locals => { :account => @buyer } %>
38
+ # <%= render partial: "account", locals: { account: @buyer } %>
39
39
  #
40
40
  # With the <tt>:as</tt> option we can specify a different name for said local variable. For example, if we
41
41
  # wanted it to be +user+ instead of +account+ we'd do:
42
42
  #
43
- # <%= render :partial => "account", :object => @buyer, :as => 'user' %>
43
+ # <%= render partial: "account", object: @buyer, as: 'user' %>
44
44
  #
45
45
  # This is equivalent to
46
46
  #
47
- # <%= render :partial => "account", :locals => { :user => @buyer } %>
47
+ # <%= render partial: "account", locals: { user: @buyer } %>
48
48
  #
49
49
  # == Rendering a collection of partials
50
50
  #
@@ -53,7 +53,7 @@ module ActionView
53
53
  # accepts an array and renders a partial by the same name as the elements contained within. So the three-lined
54
54
  # example in "Using partials" can be rewritten with a single line:
55
55
  #
56
- # <%= render :partial => "ad", :collection => @advertisements %>
56
+ # <%= render partial: "ad", collection: @advertisements %>
57
57
  #
58
58
  # This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An
59
59
  # iteration counter will automatically be made available to the template with a name of the form
@@ -64,12 +64,12 @@ module ActionView
64
64
  # You can specify a partial to be rendered between elements via the <tt>:spacer_template</tt> option.
65
65
  # The following example will render <tt>advertiser/_ad_divider.html.erb</tt> between each ad partial:
66
66
  #
67
- # <%= render :partial => "ad", :collection => @advertisements, :spacer_template => "ad_divider" %>
67
+ # <%= render partial: "ad", collection: @advertisements, spacer_template: "ad_divider" %>
68
68
  #
69
69
  # If the given <tt>:collection</tt> is nil or empty, <tt>render</tt> will return nil. This will allow you
70
70
  # to specify a text which will displayed instead by using this form:
71
71
  #
72
- # <%= render(:partial => "ad", :collection => @advertisements) || "There's no ad to be displayed" %>
72
+ # <%= render(partial: "ad", collection: @advertisements) || "There's no ad to be displayed" %>
73
73
  #
74
74
  # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
75
75
  # just keep domain objects, like Active Records, in there.
@@ -78,43 +78,42 @@ module ActionView
78
78
  #
79
79
  # Two controllers can share a set of partials and render them like this:
80
80
  #
81
- # <%= render :partial => "advertisement/ad", :locals => { :ad => @advertisement } %>
81
+ # <%= render partial: "advertisement/ad", locals: { ad: @advertisement } %>
82
82
  #
83
83
  # This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from.
84
84
  #
85
85
  # == Rendering objects that respond to `to_partial_path`
86
86
  #
87
87
  # Instead of explicitly naming the location of a partial, you can also let PartialRenderer do the work
88
- # and pick the proper path by checking `to_proper_path` method. If the object passed to render is a collection,
89
- # all objects must return the same path.
88
+ # and pick the proper path by checking `to_partial_path` method.
90
89
  #
91
90
  # # @account.to_partial_path returns 'accounts/account', so it can be used to replace:
92
- # # <%= render :partial => "accounts/account", :locals => { :account => @account} %>
93
- # <%= render :partial => @account %>
91
+ # # <%= render partial: "accounts/account", locals: { account: @account} %>
92
+ # <%= render partial: @account %>
94
93
  #
95
94
  # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`,
96
95
  # # that's why we can replace:
97
- # # <%= render :partial => "posts/post", :collection => @posts %>
98
- # <%= render :partial => @posts %>
96
+ # # <%= render partial: "posts/post", collection: @posts %>
97
+ # <%= render partial: @posts %>
99
98
  #
100
99
  # == Rendering the default case
101
100
  #
102
101
  # If you're not going to be using any of the options like collections or layouts, you can also use the short-hand
103
102
  # defaults of render to render partials. Examples:
104
103
  #
105
- # # Instead of <%= render :partial => "account" %>
104
+ # # Instead of <%= render partial: "account" %>
106
105
  # <%= render "account" %>
107
106
  #
108
- # # Instead of <%= render :partial => "account", :locals => { :account => @buyer } %>
109
- # <%= render "account", :account => @buyer %>
107
+ # # Instead of <%= render partial: "account", locals: { account: @buyer } %>
108
+ # <%= render "account", account: @buyer %>
110
109
  #
111
110
  # # @account.to_partial_path returns 'accounts/account', so it can be used to replace:
112
- # # <%= render :partial => "accounts/account", :locals => { :account => @account} %>
111
+ # # <%= render partial: "accounts/account", locals: { account: @account} %>
113
112
  # <%= render @account %>
114
113
  #
115
114
  # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`,
116
115
  # # that's why we can replace:
117
- # # <%= render :partial => "posts/post", :collection => @posts %>
116
+ # # <%= render partial: "posts/post", collection: @posts %>
118
117
  # <%= render @posts %>
119
118
  #
120
119
  # == Rendering partials with layouts
@@ -125,10 +124,10 @@ module ActionView
125
124
  #
126
125
  # <%# app/views/users/index.html.erb &>
127
126
  # Here's the administrator:
128
- # <%= render :partial => "user", :layout => "administrator", :locals => { :user => administrator } %>
127
+ # <%= render partial: "user", layout: "administrator", locals: { user: administrator } %>
129
128
  #
130
129
  # Here's the editor:
131
- # <%= render :partial => "user", :layout => "editor", :locals => { :user => editor } %>
130
+ # <%= render partial: "user", layout: "editor", locals: { user: editor } %>
132
131
  #
133
132
  # <%# app/views/users/_user.html.erb &>
134
133
  # Name: <%= user.name %>
@@ -159,10 +158,51 @@ module ActionView
159
158
  # Name: <%= user.name %>
160
159
  # </div>
161
160
  #
161
+ # If a collection is given, the layout will be rendered once for each item in
162
+ # the collection. Just think these two snippets have the same output:
163
+ #
164
+ # <%# app/views/users/_user.html.erb %>
165
+ # Name: <%= user.name %>
166
+ #
167
+ # <%# app/views/users/index.html.erb %>
168
+ # <%# This does not use layouts %>
169
+ # <ul>
170
+ # <% users.each do |user| -%>
171
+ # <li>
172
+ # <%= render partial: "user", locals: { user: user } %>
173
+ # </li>
174
+ # <% end -%>
175
+ # </ul>
176
+ #
177
+ # <%# app/views/users/_li_layout.html.erb %>
178
+ # <li>
179
+ # <%= yield %>
180
+ # </li>
181
+ #
182
+ # <%# app/views/users/index.html.erb %>
183
+ # <ul>
184
+ # <%= render partial: "user", layout: "li_layout", collection: users %>
185
+ # </ul>
186
+ #
187
+ # Given two users whose names are Alice and Bob, these snippets return:
188
+ #
189
+ # <ul>
190
+ # <li>
191
+ # Name: Alice
192
+ # </li>
193
+ # <li>
194
+ # Name: Bob
195
+ # </li>
196
+ # </ul>
197
+ #
198
+ # The current object being rendered, as well as the object_counter, will be
199
+ # available as local variables inside the layout template under the same names
200
+ # as available in the partial.
201
+ #
162
202
  # You can also apply a layout to a block within any template:
163
203
  #
164
204
  # <%# app/views/users/_chief.html.erb &>
165
- # <%= render(:layout => "administrator", :locals => { :user => chief }) do %>
205
+ # <%= render(layout: "administrator", locals: { user: chief }) do %>
166
206
  # Title: <%= chief.title %>
167
207
  # <% end %>
168
208
  #
@@ -185,7 +225,7 @@ module ActionView
185
225
  # </div>
186
226
  #
187
227
  # <%# app/views/users/index.html.erb &>
188
- # <%= render :layout => @users do |user| %>
228
+ # <%= render layout: @users do |user| %>
189
229
  # Title: <%= user.title %>
190
230
  # <% end %>
191
231
  #
@@ -201,7 +241,7 @@ module ActionView
201
241
  # </div>
202
242
  #
203
243
  # <%# app/views/users/index.html.erb &>
204
- # <%= render :layout => @users do |user, section| %>
244
+ # <%= render layout: @users do |user, section| %>
205
245
  # <%- case section when :header -%>
206
246
  # Title: <%= user.title %>
207
247
  # <%- when :footer -%>
@@ -209,12 +249,13 @@ module ActionView
209
249
  # <%- end -%>
210
250
  # <% end %>
211
251
  class PartialRenderer < AbstractRenderer
212
- PARTIAL_NAMES = Hash.new { |h,k| h[k] = {} }
252
+ PREFIXED_PARTIAL_NAMES = ThreadSafe::Cache.new do |h, k|
253
+ h[k] = ThreadSafe::Cache.new
254
+ end
213
255
 
214
256
  def initialize(*)
215
257
  super
216
258
  @context_prefix = @lookup_context.prefixes.first
217
- @partial_names = PARTIAL_NAMES[@context_prefix]
218
259
  end
219
260
 
220
261
  def render(context, options, block)
@@ -244,7 +285,7 @@ module ActionView
244
285
  return nil if @collection.blank?
245
286
 
246
287
  if @options.key?(:spacer_template)
247
- spacer = find_template(@options[:spacer_template]).render(@view, @locals)
288
+ spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals)
248
289
  end
249
290
 
250
291
  result = @template ? collection_with_template : collection_without_template
@@ -252,11 +293,11 @@ module ActionView
252
293
  end
253
294
 
254
295
  def render_partial
255
- locals, view, block = @locals, @view, @block
296
+ view, locals, block = @view, @locals, @block
256
297
  object, as = @object, @variable
257
298
 
258
299
  if !block && (layout = @options[:layout])
259
- layout = find_template(layout.to_s)
300
+ layout = find_template(layout.to_s, @template_keys)
260
301
  end
261
302
 
262
303
  object ||= locals[as]
@@ -272,6 +313,13 @@ module ActionView
272
313
 
273
314
  private
274
315
 
316
+ # Sets up instance variables needed for rendering a partial. This method
317
+ # finds the options and details and extracts them. The method also contains
318
+ # logic that handles the type of object passed in as the partial.
319
+ #
320
+ # If +options[:partial]+ is a string, then the +@path+ instance variable is
321
+ # set to that string. Otherwise, the +options[:partial]+ object must
322
+ # respond to +to_partial_path+ in order to setup the path.
275
323
  def setup(context, options, block)
276
324
  @view = context
277
325
  partial = options[:partial]
@@ -281,6 +329,8 @@ module ActionView
281
329
  @block = block
282
330
  @details = extract_details(options)
283
331
 
332
+ prepend_formats(options[:formats])
333
+
284
334
  if String === partial
285
335
  @object = options[:object]
286
336
  @path = partial
@@ -296,19 +346,18 @@ module ActionView
296
346
  end
297
347
  end
298
348
 
299
- if @path
300
- @variable, @variable_counter = retrieve_variable(@path)
301
- else
302
- paths.map! { |path| retrieve_variable(path).unshift(path) }
349
+ if as = options[:as]
350
+ raise_invalid_identifier(as) unless as.to_s =~ /\A[a-z_]\w*\z/
351
+ as = as.to_sym
303
352
  end
304
353
 
305
- if String === partial && @variable.to_s !~ /^[a-z_][a-zA-Z_0-9]*$/
306
- raise ArgumentError.new("The partial name (#{partial}) is not a valid Ruby identifier; " +
307
- "make sure your partial name starts with a letter or underscore, " +
308
- "and is followed by any combinations of letters, numbers, or underscores.")
354
+ if @path
355
+ @variable, @variable_counter = retrieve_variable(@path, as)
356
+ @template_keys = retrieve_template_keys
357
+ else
358
+ paths.map! { |path| retrieve_variable(path, as).unshift(path) }
309
359
  end
310
360
 
311
- extract_format(@path, @details)
312
361
  self
313
362
  end
314
363
 
@@ -320,73 +369,82 @@ module ActionView
320
369
  end
321
370
 
322
371
  def collection_from_object
323
- if @object.respond_to?(:to_ary)
324
- @object.to_ary
325
- end
372
+ @object.to_ary if @object.respond_to?(:to_ary)
326
373
  end
327
374
 
328
375
  def find_partial
329
376
  if path = @path
330
- locals = @locals.keys
331
- locals << @variable
332
- locals << @variable_counter if @collection
333
- find_template(path, locals)
377
+ find_template(path, @template_keys)
334
378
  end
335
379
  end
336
380
 
337
- def find_template(path=@path, locals=@locals.keys)
381
+ def find_template(path, locals)
338
382
  prefixes = path.include?(?/) ? [] : @lookup_context.prefixes
339
383
  @lookup_context.find_template(path, prefixes, true, locals, @details)
340
384
  end
341
385
 
342
386
  def collection_with_template
343
- segments, locals, template = [], @locals, @template
387
+ view, locals, template = @view, @locals, @template
344
388
  as, counter = @variable, @variable_counter
345
389
 
346
- locals[counter] = -1
347
-
348
- @collection.each do |object|
349
- locals[counter] += 1
350
- locals[as] = object
351
- segments << template.render(@view, locals)
390
+ if layout = @options[:layout]
391
+ layout = find_template(layout, @template_keys)
352
392
  end
353
393
 
354
- segments
394
+ index = -1
395
+ @collection.map do |object|
396
+ locals[as] = object
397
+ locals[counter] = (index += 1)
398
+
399
+ content = template.render(view, locals)
400
+ content = layout.render(view, locals) { content } if layout
401
+ content
402
+ end
355
403
  end
356
404
 
357
405
  def collection_without_template
358
- segments, locals, collection_data = [], @locals, @collection_data
359
- index, template, cache = -1, nil, {}
360
- keys = @locals.keys
406
+ view, locals, collection_data = @view, @locals, @collection_data
407
+ cache = {}
408
+ keys = @locals.keys
361
409
 
362
- @collection.each_with_index do |object, i|
363
- path, *data = collection_data[i]
364
- template = (cache[path] ||= find_template(path, keys + data))
365
- locals[data[0]] = object
366
- locals[data[1]] = (index += 1)
367
- segments << template.render(@view, locals)
368
- end
410
+ index = -1
411
+ @collection.map do |object|
412
+ index += 1
413
+ path, as, counter = collection_data[index]
369
414
 
370
- @template = template
371
- segments
415
+ locals[as] = object
416
+ locals[counter] = index
417
+
418
+ template = (cache[path] ||= find_template(path, keys + [as, counter]))
419
+ template.render(view, locals)
420
+ end
372
421
  end
373
422
 
423
+ # Obtains the path to where the object's partial is located. If the object
424
+ # responds to +to_partial_path+, then +to_partial_path+ will be called and
425
+ # will provide the path. If the object does not respond to +to_partial_path+,
426
+ # then an +ArgumentError+ is raised.
427
+ #
428
+ # If +prefix_partial_path_with_controller_namespace+ is true, then this
429
+ # method will prefix the partial paths with a namespace.
374
430
  def partial_path(object = @object)
375
431
  object = object.to_model if object.respond_to?(:to_model)
376
432
 
377
433
  path = if object.respond_to?(:to_partial_path)
378
434
  object.to_partial_path
379
435
  else
380
- klass = object.class
381
- if klass.respond_to?(:model_name)
382
- ActiveSupport::Deprecation.warn "ActiveModel-compatible objects whose classes return a #model_name that responds to #partial_path are deprecated. Please respond to #to_partial_path directly instead."
383
- klass.model_name.partial_path
384
- else
385
- raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object that returns a valid partial path.")
386
- end
436
+ raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.")
437
+ end
438
+
439
+ if @view.prefix_partial_path_with_controller_namespace
440
+ prefixed_partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
441
+ else
442
+ path
387
443
  end
444
+ end
388
445
 
389
- @partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
446
+ def prefixed_partial_names
447
+ @prefixed_partial_names ||= PREFIXED_PARTIAL_NAMES[@context_prefix]
390
448
  end
391
449
 
392
450
  def merge_prefix_into_object_path(prefix, object_path)
@@ -406,10 +464,29 @@ module ActionView
406
464
  end
407
465
  end
408
466
 
409
- def retrieve_variable(path)
410
- variable = @options.fetch(:as) { path[%r'_?(\w+)(\.\w+)*$', 1] }.try(:to_sym)
467
+ def retrieve_template_keys
468
+ keys = @locals.keys
469
+ keys << @variable if @object || @collection
470
+ keys << @variable_counter if @collection
471
+ keys
472
+ end
473
+
474
+ def retrieve_variable(path, as)
475
+ variable = as || begin
476
+ base = path[-1] == "/" ? "" : File.basename(path)
477
+ raise_invalid_identifier(path) unless base =~ /\A_?([a-z]\w*)(\.\w+)*\z/
478
+ $1.to_sym
479
+ end
411
480
  variable_counter = :"#{variable}_counter" if @collection
412
481
  [variable, variable_counter]
413
482
  end
483
+
484
+ IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " +
485
+ "make sure your partial name starts with a lowercase letter or underscore, " +
486
+ "and is followed by any combination of letters, numbers and underscores."
487
+
488
+ def raise_invalid_identifier(path)
489
+ raise ArgumentError.new(IDENTIFIER_ERROR_MESSAGE % (path))
490
+ end
414
491
  end
415
492
  end
@@ -2,6 +2,12 @@ module ActionView
2
2
  # This is the main entry point for rendering. It basically delegates
3
3
  # to other objects like TemplateRenderer and PartialRenderer which
4
4
  # actually renders the template.
5
+ #
6
+ # The Renderer will parse the options from the +render+ or +render_body+
7
+ # method and render a partial or a template based on the options. The
8
+ # +TemplateRenderer+ and +PartialRenderer+ objects are wrappers which do all
9
+ # the setup and logic necessary to render a view and a new object is created
10
+ # each time +render+ is called.
5
11
  class Renderer
6
12
  attr_accessor :lookup_context
7
13
 
@@ -33,22 +39,12 @@ module ActionView
33
39
 
34
40
  # Direct accessor to template rendering.
35
41
  def render_template(context, options) #:nodoc:
36
- _template_renderer.render(context, options)
42
+ TemplateRenderer.new(@lookup_context).render(context, options)
37
43
  end
38
44
 
39
45
  # Direct access to partial rendering.
40
46
  def render_partial(context, options, &block) #:nodoc:
41
- _partial_renderer.render(context, options, block)
42
- end
43
-
44
- private
45
-
46
- def _template_renderer #:nodoc:
47
- @_template_renderer ||= TemplateRenderer.new(@lookup_context)
48
- end
49
-
50
- def _partial_renderer #:nodoc:
51
- @_partial_renderer ||= PartialRenderer.new(@lookup_context)
47
+ PartialRenderer.new(@lookup_context).render(context, options, block)
52
48
  end
53
49
  end
54
50
  end
@@ -1,7 +1,4 @@
1
- # 1.9 ships with Fibers but we need to require the extra
2
- # methods explicitly. We only load those extra methods if
3
- # Fiber is available in the first place.
4
- require 'fiber' if defined?(Fiber)
1
+ require 'fiber'
5
2
 
6
3
  module ActionView
7
4
  # == TODO
@@ -33,7 +30,7 @@ module ActionView
33
30
  # This is the same logging logic as in ShowExceptions middleware.
34
31
  # TODO Once "exceptron" is in, refactor this piece to simply re-use exceptron.
35
32
  def log_error(exception) #:nodoc:
36
- logger = ActionController::Base.logger
33
+ logger = ActionView::Base.logger
37
34
  return unless logger
38
35
 
39
36
  message = "\n#{exception.class} (#{exception.message}):\n"
@@ -1,18 +1,17 @@
1
1
  require 'active_support/core_ext/object/try'
2
- require 'active_support/core_ext/array/wrap'
3
2
 
4
3
  module ActionView
5
4
  class TemplateRenderer < AbstractRenderer #:nodoc:
6
5
  def render(context, options)
7
6
  @view = context
8
7
  @details = extract_details(options)
9
- extract_format(options[:file] || options[:template], @details)
10
8
  template = determine_template(options)
11
9
  context = @lookup_context
12
10
 
11
+ prepend_formats(template.formats)
12
+
13
13
  unless context.rendered_format
14
- context.formats = template.formats unless template.formats.empty?
15
- context.rendered_format = context.formats.first
14
+ context.rendered_format = template.formats.first || formats.last
16
15
  end
17
16
 
18
17
  render_template(template, options[:layout], options[:locals])
@@ -20,26 +19,29 @@ module ActionView
20
19
 
21
20
  # Determine the template to be rendered using the given options.
22
21
  def determine_template(options) #:nodoc:
23
- keys = options[:locals].try(:keys) || []
22
+ keys = options.fetch(:locals, {}).keys
24
23
 
25
24
  if options.key?(:text)
26
- Template::Text.new(options[:text], formats.try(:first))
25
+ Template::Text.new(options[:text], formats.first)
27
26
  elsif options.key?(:file)
28
27
  with_fallbacks { find_template(options[:file], nil, false, keys, @details) }
29
28
  elsif options.key?(:inline)
30
29
  handler = Template.handler_for_extension(options[:type] || "erb")
31
30
  Template.new(options[:inline], "inline template", handler, :locals => keys)
32
31
  elsif options.key?(:template)
33
- options[:template].respond_to?(:render) ?
34
- options[:template] : find_template(options[:template], options[:prefixes], false, keys, @details)
32
+ if options[:template].respond_to?(:render)
33
+ options[:template]
34
+ else
35
+ find_template(options[:template], options[:prefixes], false, keys, @details)
36
+ end
35
37
  else
36
38
  raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file or :text option."
37
39
  end
38
40
  end
39
41
 
40
- # Renders the given template. An string representing the layout can be
42
+ # Renders the given template. A string representing the layout can be
41
43
  # supplied as well.
42
- def render_template(template, layout_name = nil, locals = {}) #:nodoc:
44
+ def render_template(template, layout_name = nil, locals = nil) #:nodoc:
43
45
  view, locals = @view, locals || {}
44
46
 
45
47
  render_with_layout(layout_name, locals) do |layout|
@@ -0,0 +1,107 @@
1
+ module ActionView
2
+ module RoutingUrlFor
3
+
4
+ # Returns the URL for the set of +options+ provided. This takes the
5
+ # same options as +url_for+ in Action Controller (see the
6
+ # documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
7
+ # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action"
8
+ # instead of the fully qualified URL like "http://example.com/controller/action".
9
+ #
10
+ # ==== Options
11
+ # * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
12
+ # * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
13
+ # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
14
+ # is currently not recommended since it breaks caching.
15
+ # * <tt>:host</tt> - Overrides the default (current) host if provided.
16
+ # * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
17
+ # * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
18
+ # * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
19
+ #
20
+ # ==== Relying on named routes
21
+ #
22
+ # Passing a record (like an Active Record) instead of a hash as the options parameter will
23
+ # trigger the named route for that record. The lookup will happen on the name of the class. So passing a
24
+ # Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
25
+ # +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
26
+ #
27
+ # ==== Implicit Controller Namespacing
28
+ #
29
+ # Controllers passed in using the +:controller+ option will retain their namespace unless it is an absolute one.
30
+ #
31
+ # ==== Examples
32
+ # <%= url_for(action: 'index') %>
33
+ # # => /blog/
34
+ #
35
+ # <%= url_for(action: 'find', controller: 'books') %>
36
+ # # => /books/find
37
+ #
38
+ # <%= url_for(action: 'login', controller: 'members', only_path: false, protocol: 'https') %>
39
+ # # => https://www.example.com/members/login/
40
+ #
41
+ # <%= url_for(action: 'play', anchor: 'player') %>
42
+ # # => /messages/play/#player
43
+ #
44
+ # <%= url_for(action: 'jump', anchor: 'tax&ship') %>
45
+ # # => /testing/jump/#tax&ship
46
+ #
47
+ # <%= url_for(Workshop.new) %>
48
+ # # relies on Workshop answering a persisted? call (and in this case returning false)
49
+ # # => /workshops
50
+ #
51
+ # <%= url_for(@workshop) %>
52
+ # # calls @workshop.to_param which by default returns the id
53
+ # # => /workshops/5
54
+ #
55
+ # # to_param can be re-defined in a model to provide different URL names:
56
+ # # => /workshops/1-workshop-name
57
+ #
58
+ # <%= url_for("http://www.example.com") %>
59
+ # # => http://www.example.com
60
+ #
61
+ # <%= url_for(:back) %>
62
+ # # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
63
+ # # => http://www.example.com
64
+ #
65
+ # <%= url_for(:back) %>
66
+ # # if request.env["HTTP_REFERER"] is not set or is blank
67
+ # # => javascript:history.back()
68
+ #
69
+ # <%= url_for(action: 'index', controller: 'users') %>
70
+ # # Assuming an "admin" namespace
71
+ # # => /admin/users
72
+ #
73
+ # <%= url_for(action: 'index', controller: '/users') %>
74
+ # # Specify absolute path with beginning slash
75
+ # # => /users
76
+ def url_for(options = nil)
77
+ case options
78
+ when String
79
+ options
80
+ when nil, Hash
81
+ options ||= {}
82
+ options = { :only_path => options[:host].nil? }.merge!(options.symbolize_keys)
83
+ super
84
+ when :back
85
+ _back_url
86
+ else
87
+ polymorphic_path(options)
88
+ end
89
+ end
90
+
91
+ def url_options #:nodoc:
92
+ return super unless controller.respond_to?(:url_options)
93
+ controller.url_options
94
+ end
95
+
96
+ def _routes_context #:nodoc:
97
+ controller
98
+ end
99
+ protected :_routes_context
100
+
101
+ def optimize_routes_generation? #:nodoc:
102
+ controller.respond_to?(:optimize_routes_generation?, true) ?
103
+ controller.optimize_routes_generation? : super
104
+ end
105
+ protected :optimize_routes_generation?
106
+ end
107
+ end