actionpack 3.2.22.5 → 4.0.0.beta1

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 (265) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +641 -418
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -288
  5. data/lib/abstract_controller.rb +1 -8
  6. data/lib/abstract_controller/asset_paths.rb +2 -2
  7. data/lib/abstract_controller/base.rb +39 -37
  8. data/lib/abstract_controller/callbacks.rb +101 -82
  9. data/lib/abstract_controller/collector.rb +7 -3
  10. data/lib/abstract_controller/helpers.rb +23 -11
  11. data/lib/abstract_controller/layouts.rb +68 -73
  12. data/lib/abstract_controller/logger.rb +1 -2
  13. data/lib/abstract_controller/rendering.rb +22 -13
  14. data/lib/abstract_controller/translation.rb +16 -1
  15. data/lib/abstract_controller/url_for.rb +6 -6
  16. data/lib/abstract_controller/view_paths.rb +1 -1
  17. data/lib/action_controller.rb +15 -6
  18. data/lib/action_controller/base.rb +46 -22
  19. data/lib/action_controller/caching.rb +46 -33
  20. data/lib/action_controller/caching/fragments.rb +23 -53
  21. data/lib/action_controller/deprecated.rb +5 -1
  22. data/lib/action_controller/deprecated/integration_test.rb +3 -0
  23. data/lib/action_controller/log_subscriber.rb +11 -8
  24. data/lib/action_controller/metal.rb +16 -30
  25. data/lib/action_controller/metal/conditional_get.rb +76 -32
  26. data/lib/action_controller/metal/data_streaming.rb +20 -26
  27. data/lib/action_controller/metal/exceptions.rb +19 -6
  28. data/lib/action_controller/metal/flash.rb +24 -9
  29. data/lib/action_controller/metal/force_ssl.rb +32 -9
  30. data/lib/action_controller/metal/head.rb +25 -4
  31. data/lib/action_controller/metal/helpers.rb +6 -9
  32. data/lib/action_controller/metal/hide_actions.rb +1 -2
  33. data/lib/action_controller/metal/http_authentication.rb +105 -87
  34. data/lib/action_controller/metal/implicit_render.rb +1 -1
  35. data/lib/action_controller/metal/instrumentation.rb +2 -1
  36. data/lib/action_controller/metal/live.rb +141 -0
  37. data/lib/action_controller/metal/mime_responds.rb +161 -47
  38. data/lib/action_controller/metal/params_wrapper.rb +112 -74
  39. data/lib/action_controller/metal/rack_delegation.rb +9 -3
  40. data/lib/action_controller/metal/redirecting.rb +15 -20
  41. data/lib/action_controller/metal/renderers.rb +11 -9
  42. data/lib/action_controller/metal/rendering.rb +8 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +112 -19
  44. data/lib/action_controller/metal/responder.rb +20 -19
  45. data/lib/action_controller/metal/streaming.rb +12 -18
  46. data/lib/action_controller/metal/strong_parameters.rb +516 -0
  47. data/lib/action_controller/metal/testing.rb +13 -18
  48. data/lib/action_controller/metal/url_for.rb +27 -25
  49. data/lib/action_controller/model_naming.rb +12 -0
  50. data/lib/action_controller/railtie.rb +33 -17
  51. data/lib/action_controller/railties/helpers.rb +22 -0
  52. data/lib/action_controller/record_identifier.rb +18 -72
  53. data/lib/action_controller/test_case.rb +215 -123
  54. data/lib/action_controller/vendor/html-scanner.rb +4 -19
  55. data/lib/action_dispatch.rb +27 -19
  56. data/lib/action_dispatch/http/cache.rb +63 -11
  57. data/lib/action_dispatch/http/filter_parameters.rb +18 -8
  58. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  59. data/lib/action_dispatch/http/headers.rb +27 -19
  60. data/lib/action_dispatch/http/mime_negotiation.rb +25 -2
  61. data/lib/action_dispatch/http/mime_type.rb +145 -113
  62. data/lib/action_dispatch/http/mime_types.rb +1 -1
  63. data/lib/action_dispatch/http/parameter_filter.rb +44 -46
  64. data/lib/action_dispatch/http/parameters.rb +12 -5
  65. data/lib/action_dispatch/http/rack_cache.rb +2 -3
  66. data/lib/action_dispatch/http/request.rb +49 -18
  67. data/lib/action_dispatch/http/response.rb +129 -35
  68. data/lib/action_dispatch/http/upload.rb +60 -17
  69. data/lib/action_dispatch/http/url.rb +53 -31
  70. data/lib/action_dispatch/journey.rb +5 -0
  71. data/lib/action_dispatch/journey/backwards.rb +5 -0
  72. data/lib/action_dispatch/journey/formatter.rb +146 -0
  73. data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
  74. data/lib/action_dispatch/journey/gtg/simulator.rb +44 -0
  75. data/lib/action_dispatch/journey/gtg/transition_table.rb +156 -0
  76. data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
  77. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  78. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  79. data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
  80. data/lib/action_dispatch/journey/nodes/node.rb +124 -0
  81. data/lib/action_dispatch/journey/parser.rb +206 -0
  82. data/lib/action_dispatch/journey/parser.y +47 -0
  83. data/lib/action_dispatch/journey/parser_extras.rb +23 -0
  84. data/lib/action_dispatch/journey/path/pattern.rb +196 -0
  85. data/lib/action_dispatch/journey/route.rb +116 -0
  86. data/lib/action_dispatch/journey/router.rb +164 -0
  87. data/lib/action_dispatch/journey/router/strexp.rb +24 -0
  88. data/lib/action_dispatch/journey/router/utils.rb +54 -0
  89. data/lib/action_dispatch/journey/routes.rb +75 -0
  90. data/lib/action_dispatch/journey/scanner.rb +61 -0
  91. data/lib/action_dispatch/journey/visitors.rb +189 -0
  92. data/lib/action_dispatch/journey/visualizer/fsm.css +34 -0
  93. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  94. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  95. data/lib/action_dispatch/middleware/callbacks.rb +9 -4
  96. data/lib/action_dispatch/middleware/cookies.rb +168 -57
  97. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -17
  98. data/lib/action_dispatch/middleware/exception_wrapper.rb +27 -3
  99. data/lib/action_dispatch/middleware/flash.rb +58 -58
  100. data/lib/action_dispatch/middleware/params_parser.rb +14 -29
  101. data/lib/action_dispatch/middleware/public_exceptions.rb +31 -14
  102. data/lib/action_dispatch/middleware/reloader.rb +6 -6
  103. data/lib/action_dispatch/middleware/remote_ip.rb +145 -39
  104. data/lib/action_dispatch/middleware/request_id.rb +2 -6
  105. data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
  106. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  107. data/lib/action_dispatch/middleware/session/cookie_store.rb +81 -7
  108. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
  109. data/lib/action_dispatch/middleware/show_exceptions.rb +12 -45
  110. data/lib/action_dispatch/middleware/ssl.rb +70 -0
  111. data/lib/action_dispatch/middleware/stack.rb +6 -1
  112. data/lib/action_dispatch/middleware/static.rb +5 -24
  113. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +14 -11
  114. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +25 -0
  115. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
  116. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +15 -9
  117. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -5
  118. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +7 -2
  119. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +30 -15
  120. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +39 -13
  121. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +6 -2
  122. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  123. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +144 -0
  124. data/lib/action_dispatch/railtie.rb +16 -6
  125. data/lib/action_dispatch/request/session.rb +181 -0
  126. data/lib/action_dispatch/routing.rb +41 -40
  127. data/lib/action_dispatch/routing/inspector.rb +240 -0
  128. data/lib/action_dispatch/routing/mapper.rb +501 -273
  129. data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -20
  130. data/lib/action_dispatch/routing/redirection.rb +46 -29
  131. data/lib/action_dispatch/routing/route_set.rb +203 -164
  132. data/lib/action_dispatch/routing/routes_proxy.rb +2 -0
  133. data/lib/action_dispatch/routing/url_for.rb +48 -33
  134. data/lib/action_dispatch/testing/assertions/dom.rb +3 -13
  135. data/lib/action_dispatch/testing/assertions/response.rb +32 -40
  136. data/lib/action_dispatch/testing/assertions/routing.rb +40 -39
  137. data/lib/action_dispatch/testing/assertions/selector.rb +15 -20
  138. data/lib/action_dispatch/testing/assertions/tag.rb +20 -23
  139. data/lib/action_dispatch/testing/integration.rb +41 -22
  140. data/lib/action_dispatch/testing/test_process.rb +9 -6
  141. data/lib/action_dispatch/testing/test_request.rb +7 -3
  142. data/lib/action_pack.rb +1 -1
  143. data/lib/action_pack/version.rb +4 -4
  144. data/lib/action_view.rb +17 -8
  145. data/lib/action_view/base.rb +15 -34
  146. data/lib/action_view/buffers.rb +1 -1
  147. data/lib/action_view/context.rb +4 -4
  148. data/lib/action_view/dependency_tracker.rb +91 -0
  149. data/lib/action_view/digestor.rb +85 -0
  150. data/lib/action_view/flows.rb +1 -4
  151. data/lib/action_view/helpers.rb +2 -4
  152. data/lib/action_view/helpers/active_model_helper.rb +3 -4
  153. data/lib/action_view/helpers/asset_tag_helper.rb +211 -353
  154. data/lib/action_view/helpers/asset_url_helper.rb +354 -0
  155. data/lib/action_view/helpers/atom_feed_helper.rb +13 -10
  156. data/lib/action_view/helpers/cache_helper.rb +150 -18
  157. data/lib/action_view/helpers/capture_helper.rb +42 -29
  158. data/lib/action_view/helpers/csrf_helper.rb +0 -2
  159. data/lib/action_view/helpers/date_helper.rb +268 -247
  160. data/lib/action_view/helpers/debug_helper.rb +10 -11
  161. data/lib/action_view/helpers/form_helper.rb +904 -547
  162. data/lib/action_view/helpers/form_options_helper.rb +341 -166
  163. data/lib/action_view/helpers/form_tag_helper.rb +188 -88
  164. data/lib/action_view/helpers/javascript_helper.rb +23 -16
  165. data/lib/action_view/helpers/number_helper.rb +148 -354
  166. data/lib/action_view/helpers/output_safety_helper.rb +3 -3
  167. data/lib/action_view/helpers/record_tag_helper.rb +17 -22
  168. data/lib/action_view/helpers/rendering_helper.rb +2 -4
  169. data/lib/action_view/helpers/sanitize_helper.rb +3 -6
  170. data/lib/action_view/helpers/tag_helper.rb +43 -37
  171. data/lib/action_view/helpers/tags.rb +39 -0
  172. data/lib/action_view/helpers/tags/base.rb +148 -0
  173. data/lib/action_view/helpers/tags/check_box.rb +64 -0
  174. data/lib/action_view/helpers/tags/checkable.rb +16 -0
  175. data/lib/action_view/helpers/tags/collection_check_boxes.rb +43 -0
  176. data/lib/action_view/helpers/tags/collection_helpers.rb +83 -0
  177. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
  178. data/lib/action_view/helpers/tags/collection_select.rb +28 -0
  179. data/lib/action_view/helpers/tags/color_field.rb +25 -0
  180. data/lib/action_view/helpers/tags/date_field.rb +13 -0
  181. data/lib/action_view/helpers/tags/date_select.rb +72 -0
  182. data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
  183. data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
  184. data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
  185. data/lib/action_view/helpers/tags/email_field.rb +8 -0
  186. data/lib/action_view/helpers/tags/file_field.rb +8 -0
  187. data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
  188. data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
  189. data/lib/action_view/helpers/tags/label.rb +65 -0
  190. data/lib/action_view/helpers/tags/month_field.rb +13 -0
  191. data/lib/action_view/helpers/tags/number_field.rb +18 -0
  192. data/lib/action_view/helpers/tags/password_field.rb +12 -0
  193. data/lib/action_view/helpers/tags/radio_button.rb +31 -0
  194. data/lib/action_view/helpers/tags/range_field.rb +8 -0
  195. data/lib/action_view/helpers/tags/search_field.rb +24 -0
  196. data/lib/action_view/helpers/tags/select.rb +41 -0
  197. data/lib/action_view/helpers/tags/tel_field.rb +8 -0
  198. data/lib/action_view/helpers/tags/text_area.rb +18 -0
  199. data/lib/action_view/helpers/tags/text_field.rb +29 -0
  200. data/lib/action_view/helpers/tags/time_field.rb +13 -0
  201. data/lib/action_view/helpers/tags/time_select.rb +8 -0
  202. data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
  203. data/lib/action_view/helpers/tags/url_field.rb +8 -0
  204. data/lib/action_view/helpers/tags/week_field.rb +13 -0
  205. data/lib/action_view/helpers/text_helper.rb +126 -113
  206. data/lib/action_view/helpers/translation_helper.rb +32 -16
  207. data/lib/action_view/helpers/url_helper.rb +200 -271
  208. data/lib/action_view/locale/en.yml +1 -105
  209. data/lib/action_view/log_subscriber.rb +6 -4
  210. data/lib/action_view/lookup_context.rb +15 -39
  211. data/lib/action_view/model_naming.rb +12 -0
  212. data/lib/action_view/path_set.rb +9 -39
  213. data/lib/action_view/railtie.rb +6 -22
  214. data/lib/action_view/record_identifier.rb +84 -0
  215. data/lib/action_view/renderer/abstract_renderer.rb +10 -19
  216. data/lib/action_view/renderer/partial_renderer.rb +144 -81
  217. data/lib/action_view/renderer/renderer.rb +2 -19
  218. data/lib/action_view/renderer/streaming_template_renderer.rb +2 -5
  219. data/lib/action_view/renderer/template_renderer.rb +14 -13
  220. data/lib/action_view/routing_url_for.rb +107 -0
  221. data/lib/action_view/template.rb +22 -21
  222. data/lib/action_view/template/error.rb +22 -12
  223. data/lib/action_view/template/handlers.rb +12 -9
  224. data/lib/action_view/template/handlers/builder.rb +1 -1
  225. data/lib/action_view/template/handlers/erb.rb +11 -16
  226. data/lib/action_view/template/handlers/raw.rb +11 -0
  227. data/lib/action_view/template/resolver.rb +111 -83
  228. data/lib/action_view/template/text.rb +12 -8
  229. data/lib/action_view/template/types.rb +57 -0
  230. data/lib/action_view/test_case.rb +66 -43
  231. data/lib/action_view/testing/resolvers.rb +3 -2
  232. data/lib/action_view/vendor/html-scanner.rb +20 -0
  233. data/lib/{action_controller → action_view}/vendor/html-scanner/html/document.rb +0 -0
  234. data/lib/{action_controller → action_view}/vendor/html-scanner/html/node.rb +12 -12
  235. data/lib/{action_controller → action_view}/vendor/html-scanner/html/sanitizer.rb +18 -7
  236. data/lib/{action_controller → action_view}/vendor/html-scanner/html/selector.rb +1 -1
  237. data/lib/{action_controller → action_view}/vendor/html-scanner/html/tokenizer.rb +1 -1
  238. data/lib/{action_controller → action_view}/vendor/html-scanner/html/version.rb +0 -0
  239. metadata +135 -125
  240. data/lib/action_controller/caching/actions.rb +0 -185
  241. data/lib/action_controller/caching/pages.rb +0 -187
  242. data/lib/action_controller/caching/sweeping.rb +0 -97
  243. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  244. data/lib/action_controller/metal/compatibility.rb +0 -65
  245. data/lib/action_controller/metal/session_management.rb +0 -14
  246. data/lib/action_controller/railties/paths.rb +0 -25
  247. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  248. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  249. data/lib/action_dispatch/middleware/head.rb +0 -18
  250. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  251. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  252. data/lib/action_view/asset_paths.rb +0 -142
  253. data/lib/action_view/helpers/asset_paths.rb +0 -7
  254. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  255. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  256. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  257. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  258. data/lib/sprockets/assets.rake +0 -99
  259. data/lib/sprockets/bootstrap.rb +0 -37
  260. data/lib/sprockets/compressors.rb +0 -83
  261. data/lib/sprockets/helpers.rb +0 -6
  262. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  263. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  264. data/lib/sprockets/railtie.rb +0 -62
  265. data/lib/sprockets/static_compiler.rb +0 -56
@@ -1,7 +1,6 @@
1
1
  module ActionView
2
2
  class AbstractRenderer #:nodoc:
3
- delegate :find_template, :find_file, :template_exists?, :with_fallbacks, :update_details,
4
- :with_layout_format, :formats, :to => :@lookup_context
3
+ delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
5
4
 
6
5
  def initialize(lookup_context)
7
6
  @lookup_context = lookup_context
@@ -12,30 +11,22 @@ module ActionView
12
11
  end
13
12
 
14
13
  protected
15
-
14
+
16
15
  def extract_details(options)
17
- details = {}
18
- @lookup_context.registered_details.each do |key|
16
+ @lookup_context.registered_details.each_with_object({}) do |key, details|
19
17
  next unless value = options[key]
20
- details[key] = Array.wrap(value)
21
- end
22
- details
23
- end
24
-
25
- def extract_format(value, details)
26
- if value.is_a?(String) && value.sub!(formats_regexp, "")
27
- ActiveSupport::Deprecation.warn "Passing the format in the template name is deprecated. " \
28
- "Please pass render with :formats => [:#{$1}] instead.", caller
29
- details[:formats] ||= [$1.to_sym]
18
+ details[key] = Array(value)
30
19
  end
31
20
  end
32
21
 
33
- def formats_regexp
34
- @@formats_regexp ||= /\.(#{Mime::SET.symbols.join('|')})$/
35
- end
36
-
37
22
  def instrument(name, options={})
38
23
  ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
39
24
  end
25
+
26
+ def prepend_formats(formats)
27
+ formats = Array(formats)
28
+ return if formats.empty? || @lookup_context.html_fallback_for_js
29
+ @lookup_context.formats = formats | @lookup_context.formats
30
+ end
40
31
  end
41
32
  end
@@ -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]
@@ -281,6 +322,8 @@ module ActionView
281
322
  @block = block
282
323
  @details = extract_details(options)
283
324
 
325
+ prepend_formats(options[:formats])
326
+
284
327
  if String === partial
285
328
  @object = options[:object]
286
329
  @path = partial
@@ -296,19 +339,18 @@ module ActionView
296
339
  end
297
340
  end
298
341
 
299
- if @path
300
- @variable, @variable_counter = retrieve_variable(@path)
301
- else
302
- paths.map! { |path| retrieve_variable(path).unshift(path) }
342
+ if as = options[:as]
343
+ raise_invalid_identifier(as) unless as.to_s =~ /\A[a-z_]\w*\z/
344
+ as = as.to_sym
303
345
  end
304
346
 
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.")
347
+ if @path
348
+ @variable, @variable_counter = retrieve_variable(@path, as)
349
+ @template_keys = retrieve_template_keys
350
+ else
351
+ paths.map! { |path| retrieve_variable(path, as).unshift(path) }
309
352
  end
310
353
 
311
- extract_format(@path, @details)
312
354
  self
313
355
  end
314
356
 
@@ -320,55 +362,55 @@ module ActionView
320
362
  end
321
363
 
322
364
  def collection_from_object
323
- if @object.respond_to?(:to_ary)
324
- @object.to_ary
325
- end
365
+ @object.to_ary if @object.respond_to?(:to_ary)
326
366
  end
327
367
 
328
368
  def find_partial
329
369
  if path = @path
330
- locals = @locals.keys
331
- locals << @variable
332
- locals << @variable_counter if @collection
333
- find_template(path, locals)
370
+ find_template(path, @template_keys)
334
371
  end
335
372
  end
336
373
 
337
- def find_template(path=@path, locals=@locals.keys)
374
+ def find_template(path, locals)
338
375
  prefixes = path.include?(?/) ? [] : @lookup_context.prefixes
339
376
  @lookup_context.find_template(path, prefixes, true, locals, @details)
340
377
  end
341
378
 
342
379
  def collection_with_template
343
- segments, locals, template = [], @locals, @template
380
+ view, locals, template = @view, @locals, @template
344
381
  as, counter = @variable, @variable_counter
345
382
 
346
- locals[counter] = -1
347
-
348
- @collection.each do |object|
349
- locals[counter] += 1
350
- locals[as] = object
351
- segments << template.render(@view, locals)
383
+ if layout = @options[:layout]
384
+ layout = find_template(layout, @template_keys)
352
385
  end
353
386
 
354
- segments
387
+ index = -1
388
+ @collection.map do |object|
389
+ locals[as] = object
390
+ locals[counter] = (index += 1)
391
+
392
+ content = template.render(view, locals)
393
+ content = layout.render(view, locals) { content } if layout
394
+ content
395
+ end
355
396
  end
356
397
 
357
398
  def collection_without_template
358
- segments, locals, collection_data = [], @locals, @collection_data
359
- index, template, cache = -1, nil, {}
360
- keys = @locals.keys
399
+ view, locals, collection_data = @view, @locals, @collection_data
400
+ cache = {}
401
+ keys = @locals.keys
361
402
 
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
403
+ index = -1
404
+ @collection.map do |object|
405
+ index += 1
406
+ path, as, counter = collection_data[index]
369
407
 
370
- @template = template
371
- segments
408
+ locals[as] = object
409
+ locals[counter] = index
410
+
411
+ template = (cache[path] ||= find_template(path, keys + [as, counter]))
412
+ template.render(view, locals)
413
+ end
372
414
  end
373
415
 
374
416
  def partial_path(object = @object)
@@ -377,16 +419,18 @@ module ActionView
377
419
  path = if object.respond_to?(:to_partial_path)
378
420
  object.to_partial_path
379
421
  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
422
+ raise ArgumentError.new("'#{object.inspect}' is not an ActiveModel-compatible object. It must implement :to_partial_path.")
423
+ end
424
+
425
+ if @view.prefix_partial_path_with_controller_namespace
426
+ prefixed_partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
427
+ else
428
+ path
387
429
  end
430
+ end
388
431
 
389
- @partial_names[path] ||= merge_prefix_into_object_path(@context_prefix, path.dup)
432
+ def prefixed_partial_names
433
+ @prefixed_partial_names ||= PREFIXED_PARTIAL_NAMES[@context_prefix]
390
434
  end
391
435
 
392
436
  def merge_prefix_into_object_path(prefix, object_path)
@@ -406,10 +450,29 @@ module ActionView
406
450
  end
407
451
  end
408
452
 
409
- def retrieve_variable(path)
410
- variable = @options.fetch(:as) { path[%r'_?(\w+)(\.\w+)*$', 1] }.try(:to_sym)
453
+ def retrieve_template_keys
454
+ keys = @locals.keys
455
+ keys << @variable if @object || @collection
456
+ keys << @variable_counter if @collection
457
+ keys
458
+ end
459
+
460
+ def retrieve_variable(path, as)
461
+ variable = as || begin
462
+ base = path[-1] == "/" ? "" : File.basename(path)
463
+ raise_invalid_identifier(path) unless base =~ /\A_?([a-z]\w*)(\.\w+)*\z/
464
+ $1.to_sym
465
+ end
411
466
  variable_counter = :"#{variable}_counter" if @collection
412
467
  [variable, variable_counter]
413
468
  end
469
+
470
+ IDENTIFIER_ERROR_MESSAGE = "The partial name (%s) is not a valid Ruby identifier; " +
471
+ "make sure your partial name starts with a lowercase letter or underscore, " +
472
+ "and is followed by any combination of letters, numbers and underscores."
473
+
474
+ def raise_invalid_identifier(path)
475
+ raise ArgumentError.new(IDENTIFIER_ERROR_MESSAGE % (path))
476
+ end
414
477
  end
415
478
  end