actionpack 4.0.1 → 4.2.11.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (241) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +402 -1173
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -7
  5. data/lib/abstract_controller/base.rb +39 -7
  6. data/lib/abstract_controller/callbacks.rb +32 -53
  7. data/lib/abstract_controller/collector.rb +11 -1
  8. data/lib/abstract_controller/helpers.rb +26 -16
  9. data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
  10. data/lib/abstract_controller/rendering.rb +57 -127
  11. data/lib/abstract_controller/url_for.rb +1 -1
  12. data/lib/abstract_controller.rb +1 -2
  13. data/lib/action_controller/base.rb +19 -10
  14. data/lib/action_controller/caching/fragments.rb +7 -1
  15. data/lib/action_controller/caching.rb +2 -12
  16. data/lib/action_controller/log_subscriber.rb +29 -20
  17. data/lib/action_controller/metal/conditional_get.rb +37 -12
  18. data/lib/action_controller/metal/data_streaming.rb +1 -1
  19. data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
  20. data/lib/action_controller/metal/exceptions.rb +1 -1
  21. data/lib/action_controller/metal/flash.rb +17 -0
  22. data/lib/action_controller/metal/force_ssl.rb +2 -2
  23. data/lib/action_controller/metal/head.rb +8 -6
  24. data/lib/action_controller/metal/helpers.rb +6 -2
  25. data/lib/action_controller/metal/http_authentication.rb +45 -23
  26. data/lib/action_controller/metal/instrumentation.rb +9 -6
  27. data/lib/action_controller/metal/live.rb +173 -20
  28. data/lib/action_controller/metal/mime_responds.rb +127 -232
  29. data/lib/action_controller/metal/params_wrapper.rb +16 -9
  30. data/lib/action_controller/metal/rack_delegation.rb +1 -1
  31. data/lib/action_controller/metal/redirecting.rb +34 -26
  32. data/lib/action_controller/metal/renderers.rb +39 -12
  33. data/lib/action_controller/metal/rendering.rb +41 -14
  34. data/lib/action_controller/metal/request_forgery_protection.rb +147 -19
  35. data/lib/action_controller/metal/streaming.rb +19 -21
  36. data/lib/action_controller/metal/strong_parameters.rb +166 -22
  37. data/lib/action_controller/metal/testing.rb +0 -1
  38. data/lib/action_controller/metal/url_for.rb +11 -12
  39. data/lib/action_controller/metal.rb +14 -8
  40. data/lib/action_controller/model_naming.rb +1 -1
  41. data/lib/action_controller/railtie.rb +5 -1
  42. data/lib/action_controller/test_case.rb +160 -94
  43. data/lib/action_controller.rb +2 -18
  44. data/lib/action_dispatch/http/cache.rb +5 -4
  45. data/lib/action_dispatch/http/filter_parameters.rb +2 -2
  46. data/lib/action_dispatch/http/filter_redirect.rb +5 -4
  47. data/lib/action_dispatch/http/headers.rb +46 -10
  48. data/lib/action_dispatch/http/mime_negotiation.rb +31 -4
  49. data/lib/action_dispatch/http/mime_type.rb +25 -26
  50. data/lib/action_dispatch/http/mime_types.rb +1 -0
  51. data/lib/action_dispatch/http/parameter_filter.rb +1 -1
  52. data/lib/action_dispatch/http/parameters.rb +25 -41
  53. data/lib/action_dispatch/http/request.rb +49 -32
  54. data/lib/action_dispatch/http/response.rb +127 -25
  55. data/lib/action_dispatch/http/upload.rb +9 -21
  56. data/lib/action_dispatch/http/url.rb +97 -70
  57. data/lib/action_dispatch/journey/formatter.rb +35 -19
  58. data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
  59. data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
  60. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -33
  61. data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
  62. data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
  63. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
  64. data/lib/action_dispatch/journey/nodes/node.rb +4 -0
  65. data/lib/action_dispatch/journey/parser.rb +51 -59
  66. data/lib/action_dispatch/journey/parser.y +12 -10
  67. data/lib/action_dispatch/journey/path/pattern.rb +16 -19
  68. data/lib/action_dispatch/journey/route.rb +8 -19
  69. data/lib/action_dispatch/journey/router/strexp.rb +9 -6
  70. data/lib/action_dispatch/journey/router/utils.rb +54 -18
  71. data/lib/action_dispatch/journey/router.rb +53 -75
  72. data/lib/action_dispatch/journey/routes.rb +4 -0
  73. data/lib/action_dispatch/journey/scanner.rb +5 -5
  74. data/lib/action_dispatch/journey/visitors.rb +81 -60
  75. data/lib/action_dispatch/journey/visualizer/fsm.css +0 -4
  76. data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
  77. data/lib/action_dispatch/middleware/callbacks.rb +7 -7
  78. data/lib/action_dispatch/middleware/cookies.rb +119 -43
  79. data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -13
  80. data/lib/action_dispatch/middleware/exception_wrapper.rb +60 -20
  81. data/lib/action_dispatch/middleware/flash.rb +37 -24
  82. data/lib/action_dispatch/middleware/params_parser.rb +2 -2
  83. data/lib/action_dispatch/middleware/public_exceptions.rb +12 -3
  84. data/lib/action_dispatch/middleware/reloader.rb +11 -2
  85. data/lib/action_dispatch/middleware/remote_ip.rb +40 -54
  86. data/lib/action_dispatch/middleware/request_id.rb +1 -1
  87. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  88. data/lib/action_dispatch/middleware/session/cookie_store.rb +8 -7
  89. data/lib/action_dispatch/middleware/show_exceptions.rb +6 -2
  90. data/lib/action_dispatch/middleware/ssl.rb +10 -7
  91. data/lib/action_dispatch/middleware/static.rb +79 -23
  92. data/lib/action_dispatch/middleware/templates/rescues/{_request_and_response.erb → _request_and_response.html.erb} +0 -0
  93. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  94. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +21 -19
  95. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  96. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  97. data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +1 -1
  98. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  99. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
  100. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  101. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  102. data/lib/action_dispatch/middleware/templates/rescues/{routing_error.erb → routing_error.html.erb} +3 -1
  103. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  104. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  105. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  106. data/lib/action_dispatch/middleware/templates/rescues/{unknown_action.erb → unknown_action.html.erb} +1 -1
  107. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  108. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +120 -64
  109. data/lib/action_dispatch/railtie.rb +5 -2
  110. data/lib/action_dispatch/request/session.rb +12 -0
  111. data/lib/action_dispatch/request/utils.rb +35 -0
  112. data/lib/action_dispatch/routing/endpoint.rb +10 -0
  113. data/lib/action_dispatch/routing/inspector.rb +11 -17
  114. data/lib/action_dispatch/routing/mapper.rb +519 -312
  115. data/lib/action_dispatch/routing/polymorphic_routes.rb +204 -79
  116. data/lib/action_dispatch/routing/redirection.rb +51 -26
  117. data/lib/action_dispatch/routing/route_set.rb +331 -206
  118. data/lib/action_dispatch/routing/routes_proxy.rb +5 -4
  119. data/lib/action_dispatch/routing/url_for.rb +19 -5
  120. data/lib/action_dispatch/routing.rb +9 -6
  121. data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
  122. data/lib/action_dispatch/testing/assertions/response.rb +9 -15
  123. data/lib/action_dispatch/testing/assertions/routing.rb +22 -22
  124. data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
  125. data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
  126. data/lib/action_dispatch/testing/assertions.rb +11 -7
  127. data/lib/action_dispatch/testing/integration.rb +31 -29
  128. data/lib/action_dispatch/testing/test_request.rb +1 -1
  129. data/lib/action_dispatch/testing/test_response.rb +1 -5
  130. data/lib/action_dispatch.rb +5 -8
  131. data/lib/action_pack/gem_version.rb +15 -0
  132. data/lib/action_pack/version.rb +4 -7
  133. data/lib/action_pack.rb +1 -1
  134. metadata +77 -159
  135. data/lib/abstract_controller/layouts.rb +0 -423
  136. data/lib/abstract_controller/view_paths.rb +0 -96
  137. data/lib/action_controller/deprecated/integration_test.rb +0 -5
  138. data/lib/action_controller/deprecated.rb +0 -7
  139. data/lib/action_controller/metal/responder.rb +0 -287
  140. data/lib/action_controller/record_identifier.rb +0 -31
  141. data/lib/action_controller/vendor/html-scanner.rb +0 -5
  142. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -24
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -7
  144. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -43
  145. data/lib/action_view/base.rb +0 -201
  146. data/lib/action_view/buffers.rb +0 -49
  147. data/lib/action_view/context.rb +0 -36
  148. data/lib/action_view/dependency_tracker.rb +0 -93
  149. data/lib/action_view/digestor.rb +0 -113
  150. data/lib/action_view/flows.rb +0 -76
  151. data/lib/action_view/helpers/active_model_helper.rb +0 -49
  152. data/lib/action_view/helpers/asset_tag_helper.rb +0 -320
  153. data/lib/action_view/helpers/asset_url_helper.rb +0 -355
  154. data/lib/action_view/helpers/atom_feed_helper.rb +0 -203
  155. data/lib/action_view/helpers/cache_helper.rb +0 -196
  156. data/lib/action_view/helpers/capture_helper.rb +0 -216
  157. data/lib/action_view/helpers/controller_helper.rb +0 -25
  158. data/lib/action_view/helpers/csrf_helper.rb +0 -30
  159. data/lib/action_view/helpers/date_helper.rb +0 -1083
  160. data/lib/action_view/helpers/debug_helper.rb +0 -39
  161. data/lib/action_view/helpers/form_helper.rb +0 -1880
  162. data/lib/action_view/helpers/form_options_helper.rb +0 -838
  163. data/lib/action_view/helpers/form_tag_helper.rb +0 -785
  164. data/lib/action_view/helpers/javascript_helper.rb +0 -117
  165. data/lib/action_view/helpers/number_helper.rb +0 -441
  166. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  167. data/lib/action_view/helpers/record_tag_helper.rb +0 -106
  168. data/lib/action_view/helpers/rendering_helper.rb +0 -90
  169. data/lib/action_view/helpers/sanitize_helper.rb +0 -256
  170. data/lib/action_view/helpers/tag_helper.rb +0 -173
  171. data/lib/action_view/helpers/tags/base.rb +0 -148
  172. data/lib/action_view/helpers/tags/check_box.rb +0 -64
  173. data/lib/action_view/helpers/tags/checkable.rb +0 -16
  174. data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -44
  175. data/lib/action_view/helpers/tags/collection_helpers.rb +0 -84
  176. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -36
  177. data/lib/action_view/helpers/tags/collection_select.rb +0 -28
  178. data/lib/action_view/helpers/tags/color_field.rb +0 -25
  179. data/lib/action_view/helpers/tags/date_field.rb +0 -13
  180. data/lib/action_view/helpers/tags/date_select.rb +0 -72
  181. data/lib/action_view/helpers/tags/datetime_field.rb +0 -22
  182. data/lib/action_view/helpers/tags/datetime_local_field.rb +0 -19
  183. data/lib/action_view/helpers/tags/datetime_select.rb +0 -8
  184. data/lib/action_view/helpers/tags/email_field.rb +0 -8
  185. data/lib/action_view/helpers/tags/file_field.rb +0 -8
  186. data/lib/action_view/helpers/tags/grouped_collection_select.rb +0 -29
  187. data/lib/action_view/helpers/tags/hidden_field.rb +0 -8
  188. data/lib/action_view/helpers/tags/label.rb +0 -66
  189. data/lib/action_view/helpers/tags/month_field.rb +0 -13
  190. data/lib/action_view/helpers/tags/number_field.rb +0 -18
  191. data/lib/action_view/helpers/tags/password_field.rb +0 -12
  192. data/lib/action_view/helpers/tags/radio_button.rb +0 -31
  193. data/lib/action_view/helpers/tags/range_field.rb +0 -8
  194. data/lib/action_view/helpers/tags/search_field.rb +0 -24
  195. data/lib/action_view/helpers/tags/select.rb +0 -40
  196. data/lib/action_view/helpers/tags/tel_field.rb +0 -8
  197. data/lib/action_view/helpers/tags/text_area.rb +0 -18
  198. data/lib/action_view/helpers/tags/text_field.rb +0 -29
  199. data/lib/action_view/helpers/tags/time_field.rb +0 -13
  200. data/lib/action_view/helpers/tags/time_select.rb +0 -8
  201. data/lib/action_view/helpers/tags/time_zone_select.rb +0 -20
  202. data/lib/action_view/helpers/tags/url_field.rb +0 -8
  203. data/lib/action_view/helpers/tags/week_field.rb +0 -13
  204. data/lib/action_view/helpers/tags.rb +0 -39
  205. data/lib/action_view/helpers/text_helper.rb +0 -443
  206. data/lib/action_view/helpers/translation_helper.rb +0 -107
  207. data/lib/action_view/helpers/url_helper.rb +0 -635
  208. data/lib/action_view/helpers.rb +0 -58
  209. data/lib/action_view/locale/en.yml +0 -56
  210. data/lib/action_view/log_subscriber.rb +0 -30
  211. data/lib/action_view/lookup_context.rb +0 -241
  212. data/lib/action_view/model_naming.rb +0 -12
  213. data/lib/action_view/path_set.rb +0 -77
  214. data/lib/action_view/railtie.rb +0 -43
  215. data/lib/action_view/record_identifier.rb +0 -84
  216. data/lib/action_view/renderer/abstract_renderer.rb +0 -47
  217. data/lib/action_view/renderer/partial_renderer.rb +0 -492
  218. data/lib/action_view/renderer/renderer.rb +0 -50
  219. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -103
  220. data/lib/action_view/renderer/template_renderer.rb +0 -96
  221. data/lib/action_view/routing_url_for.rb +0 -107
  222. data/lib/action_view/tasks/dependencies.rake +0 -17
  223. data/lib/action_view/template/error.rb +0 -138
  224. data/lib/action_view/template/handlers/builder.rb +0 -26
  225. data/lib/action_view/template/handlers/erb.rb +0 -146
  226. data/lib/action_view/template/handlers/raw.rb +0 -11
  227. data/lib/action_view/template/handlers.rb +0 -53
  228. data/lib/action_view/template/resolver.rb +0 -326
  229. data/lib/action_view/template/text.rb +0 -34
  230. data/lib/action_view/template/types.rb +0 -57
  231. data/lib/action_view/template.rb +0 -339
  232. data/lib/action_view/test_case.rb +0 -270
  233. data/lib/action_view/testing/resolvers.rb +0 -50
  234. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  235. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  236. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  237. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  238. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  239. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
  240. data/lib/action_view/vendor/html-scanner.rb +0 -20
  241. data/lib/action_view.rb +0 -93
@@ -1,492 +0,0 @@
1
- require 'thread_safe'
2
-
3
- module ActionView
4
- # = Action View Partials
5
- #
6
- # There's also a convenience method for rendering sub templates within the current controller that depends on a
7
- # single object (we call this kind of sub templates for partials). It relies on the fact that partials should
8
- # follow the naming convention of being prefixed with an underscore -- as to separate them from regular
9
- # templates that could be rendered on their own.
10
- #
11
- # In a template for Advertiser#account:
12
- #
13
- # <%= render partial: "account" %>
14
- #
15
- # This would render "advertiser/_account.html.erb".
16
- #
17
- # In another template for Advertiser#buy, we could have:
18
- #
19
- # <%= render partial: "account", locals: { account: @buyer } %>
20
- #
21
- # <% @advertisements.each do |ad| %>
22
- # <%= render partial: "ad", locals: { ad: ad } %>
23
- # <% end %>
24
- #
25
- # This would first render "advertiser/_account.html.erb" with @buyer passed in as the local variable +account+, then
26
- # render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display.
27
- #
28
- # == The :as and :object options
29
- #
30
- # By default <tt>ActionView::PartialRenderer</tt> doesn't have any local variables.
31
- # The <tt>:object</tt> option can be used to pass an object to the partial. For instance:
32
- #
33
- # <%= render partial: "account", object: @buyer %>
34
- #
35
- # would provide the <tt>@buyer</tt> object to the partial, available under the local variable +account+ and is
36
- # equivalent to:
37
- #
38
- # <%= render partial: "account", locals: { account: @buyer } %>
39
- #
40
- # With the <tt>:as</tt> option we can specify a different name for said local variable. For example, if we
41
- # wanted it to be +user+ instead of +account+ we'd do:
42
- #
43
- # <%= render partial: "account", object: @buyer, as: 'user' %>
44
- #
45
- # This is equivalent to
46
- #
47
- # <%= render partial: "account", locals: { user: @buyer } %>
48
- #
49
- # == Rendering a collection of partials
50
- #
51
- # The example of partial use describes a familiar pattern where a template needs to iterate over an array and
52
- # render a sub template for each of the elements. This pattern has been implemented as a single method that
53
- # accepts an array and renders a partial by the same name as the elements contained within. So the three-lined
54
- # example in "Using partials" can be rewritten with a single line:
55
- #
56
- # <%= render partial: "ad", collection: @advertisements %>
57
- #
58
- # This will render "advertiser/_ad.html.erb" and pass the local variable +ad+ to the template for display. An
59
- # iteration counter will automatically be made available to the template with a name of the form
60
- # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+.
61
- #
62
- # The <tt>:as</tt> option may be used when rendering partials.
63
- #
64
- # You can specify a partial to be rendered between elements via the <tt>:spacer_template</tt> option.
65
- # The following example will render <tt>advertiser/_ad_divider.html.erb</tt> between each ad partial:
66
- #
67
- # <%= render partial: "ad", collection: @advertisements, spacer_template: "ad_divider" %>
68
- #
69
- # If the given <tt>:collection</tt> is nil or empty, <tt>render</tt> will return nil. This will allow you
70
- # to specify a text which will displayed instead by using this form:
71
- #
72
- # <%= render(partial: "ad", collection: @advertisements) || "There's no ad to be displayed" %>
73
- #
74
- # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
75
- # just keep domain objects, like Active Records, in there.
76
- #
77
- # == Rendering shared partials
78
- #
79
- # Two controllers can share a set of partials and render them like this:
80
- #
81
- # <%= render partial: "advertisement/ad", locals: { ad: @advertisement } %>
82
- #
83
- # This will render the partial "advertisement/_ad.html.erb" regardless of which controller this is being called from.
84
- #
85
- # == Rendering objects that respond to `to_partial_path`
86
- #
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_partial_path` method.
89
- #
90
- # # @account.to_partial_path returns 'accounts/account', so it can be used to replace:
91
- # # <%= render partial: "accounts/account", locals: { account: @account} %>
92
- # <%= render partial: @account %>
93
- #
94
- # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`,
95
- # # that's why we can replace:
96
- # # <%= render partial: "posts/post", collection: @posts %>
97
- # <%= render partial: @posts %>
98
- #
99
- # == Rendering the default case
100
- #
101
- # If you're not going to be using any of the options like collections or layouts, you can also use the short-hand
102
- # defaults of render to render partials. Examples:
103
- #
104
- # # Instead of <%= render partial: "account" %>
105
- # <%= render "account" %>
106
- #
107
- # # Instead of <%= render partial: "account", locals: { account: @buyer } %>
108
- # <%= render "account", account: @buyer %>
109
- #
110
- # # @account.to_partial_path returns 'accounts/account', so it can be used to replace:
111
- # # <%= render partial: "accounts/account", locals: { account: @account} %>
112
- # <%= render @account %>
113
- #
114
- # # @posts is an array of Post instances, so every post record returns 'posts/post' on `to_partial_path`,
115
- # # that's why we can replace:
116
- # # <%= render partial: "posts/post", collection: @posts %>
117
- # <%= render @posts %>
118
- #
119
- # == Rendering partials with layouts
120
- #
121
- # Partials can have their own layouts applied to them. These layouts are different than the ones that are
122
- # specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types
123
- # of users:
124
- #
125
- # <%# app/views/users/index.html.erb &>
126
- # Here's the administrator:
127
- # <%= render partial: "user", layout: "administrator", locals: { user: administrator } %>
128
- #
129
- # Here's the editor:
130
- # <%= render partial: "user", layout: "editor", locals: { user: editor } %>
131
- #
132
- # <%# app/views/users/_user.html.erb &>
133
- # Name: <%= user.name %>
134
- #
135
- # <%# app/views/users/_administrator.html.erb &>
136
- # <div id="administrator">
137
- # Budget: $<%= user.budget %>
138
- # <%= yield %>
139
- # </div>
140
- #
141
- # <%# app/views/users/_editor.html.erb &>
142
- # <div id="editor">
143
- # Deadline: <%= user.deadline %>
144
- # <%= yield %>
145
- # </div>
146
- #
147
- # ...this will return:
148
- #
149
- # Here's the administrator:
150
- # <div id="administrator">
151
- # Budget: $<%= user.budget %>
152
- # Name: <%= user.name %>
153
- # </div>
154
- #
155
- # Here's the editor:
156
- # <div id="editor">
157
- # Deadline: <%= user.deadline %>
158
- # Name: <%= user.name %>
159
- # </div>
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
- #
202
- # You can also apply a layout to a block within any template:
203
- #
204
- # <%# app/views/users/_chief.html.erb &>
205
- # <%= render(layout: "administrator", locals: { user: chief }) do %>
206
- # Title: <%= chief.title %>
207
- # <% end %>
208
- #
209
- # ...this will return:
210
- #
211
- # <div id="administrator">
212
- # Budget: $<%= user.budget %>
213
- # Title: <%= chief.name %>
214
- # </div>
215
- #
216
- # As you can see, the <tt>:locals</tt> hash is shared between both the partial and its layout.
217
- #
218
- # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass
219
- # an array to layout and treat it as an enumerable.
220
- #
221
- # <%# app/views/users/_user.html.erb &>
222
- # <div class="user">
223
- # Budget: $<%= user.budget %>
224
- # <%= yield user %>
225
- # </div>
226
- #
227
- # <%# app/views/users/index.html.erb &>
228
- # <%= render layout: @users do |user| %>
229
- # Title: <%= user.title %>
230
- # <% end %>
231
- #
232
- # This will render the layout for each user and yield to the block, passing the user, each time.
233
- #
234
- # You can also yield multiple times in one layout and use block arguments to differentiate the sections.
235
- #
236
- # <%# app/views/users/_user.html.erb &>
237
- # <div class="user">
238
- # <%= yield user, :header %>
239
- # Budget: $<%= user.budget %>
240
- # <%= yield user, :footer %>
241
- # </div>
242
- #
243
- # <%# app/views/users/index.html.erb &>
244
- # <%= render layout: @users do |user, section| %>
245
- # <%- case section when :header -%>
246
- # Title: <%= user.title %>
247
- # <%- when :footer -%>
248
- # Deadline: <%= user.deadline %>
249
- # <%- end -%>
250
- # <% end %>
251
- class PartialRenderer < AbstractRenderer
252
- PREFIXED_PARTIAL_NAMES = ThreadSafe::Cache.new do |h, k|
253
- h[k] = ThreadSafe::Cache.new
254
- end
255
-
256
- def initialize(*)
257
- super
258
- @context_prefix = @lookup_context.prefixes.first
259
- end
260
-
261
- def render(context, options, block)
262
- setup(context, options, block)
263
- identifier = (@template = find_partial) ? @template.identifier : @path
264
-
265
- @lookup_context.rendered_format ||= begin
266
- if @template && @template.formats.present?
267
- @template.formats.first
268
- else
269
- formats.first
270
- end
271
- end
272
-
273
- if @collection
274
- instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do
275
- render_collection
276
- end
277
- else
278
- instrument(:partial, :identifier => identifier) do
279
- render_partial
280
- end
281
- end
282
- end
283
-
284
- def render_collection
285
- return nil if @collection.blank?
286
-
287
- if @options.key?(:spacer_template)
288
- spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals)
289
- end
290
-
291
- result = @template ? collection_with_template : collection_without_template
292
- result.join(spacer).html_safe
293
- end
294
-
295
- def render_partial
296
- view, locals, block = @view, @locals, @block
297
- object, as = @object, @variable
298
-
299
- if !block && (layout = @options[:layout])
300
- layout = find_template(layout.to_s, @template_keys)
301
- end
302
-
303
- object ||= locals[as]
304
- locals[as] = object
305
-
306
- content = @template.render(view, locals) do |*name|
307
- view._layout_for(*name, &block)
308
- end
309
-
310
- content = layout.render(view, locals){ content } if layout
311
- content
312
- end
313
-
314
- private
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.
323
- def setup(context, options, block)
324
- @view = context
325
- partial = options[:partial]
326
-
327
- @options = options
328
- @locals = options[:locals] || {}
329
- @block = block
330
- @details = extract_details(options)
331
-
332
- prepend_formats(options[:formats])
333
-
334
- if String === partial
335
- @object = options[:object]
336
- @path = partial
337
- @collection = collection
338
- else
339
- @object = partial
340
-
341
- if @collection = collection_from_object || collection
342
- paths = @collection_data = @collection.map { |o| partial_path(o) }
343
- @path = paths.uniq.size == 1 ? paths.first : nil
344
- else
345
- @path = partial_path
346
- end
347
- end
348
-
349
- if as = options[:as]
350
- raise_invalid_identifier(as) unless as.to_s =~ /\A[a-z_]\w*\z/
351
- as = as.to_sym
352
- end
353
-
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) }
359
- end
360
-
361
- self
362
- end
363
-
364
- def collection
365
- if @options.key?(:collection)
366
- collection = @options[:collection]
367
- collection.respond_to?(:to_ary) ? collection.to_ary : []
368
- end
369
- end
370
-
371
- def collection_from_object
372
- @object.to_ary if @object.respond_to?(:to_ary)
373
- end
374
-
375
- def find_partial
376
- if path = @path
377
- find_template(path, @template_keys)
378
- end
379
- end
380
-
381
- def find_template(path, locals)
382
- prefixes = path.include?(?/) ? [] : @lookup_context.prefixes
383
- @lookup_context.find_template(path, prefixes, true, locals, @details)
384
- end
385
-
386
- def collection_with_template
387
- view, locals, template = @view, @locals, @template
388
- as, counter = @variable, @variable_counter
389
-
390
- if layout = @options[:layout]
391
- layout = find_template(layout, @template_keys)
392
- end
393
-
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
403
- end
404
-
405
- def collection_without_template
406
- view, locals, collection_data = @view, @locals, @collection_data
407
- cache = {}
408
- keys = @locals.keys
409
-
410
- index = -1
411
- @collection.map do |object|
412
- index += 1
413
- path, as, counter = collection_data[index]
414
-
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
421
- end
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.
430
- def partial_path(object = @object)
431
- object = object.to_model if object.respond_to?(:to_model)
432
-
433
- path = if object.respond_to?(:to_partial_path)
434
- object.to_partial_path
435
- else
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
443
- end
444
- end
445
-
446
- def prefixed_partial_names
447
- @prefixed_partial_names ||= PREFIXED_PARTIAL_NAMES[@context_prefix]
448
- end
449
-
450
- def merge_prefix_into_object_path(prefix, object_path)
451
- if prefix.include?(?/) && object_path.include?(?/)
452
- prefixes = []
453
- prefix_array = File.dirname(prefix).split('/')
454
- object_path_array = object_path.split('/')[0..-3] # skip model dir & partial
455
-
456
- prefix_array.each_with_index do |dir, index|
457
- break if dir == object_path_array[index]
458
- prefixes << dir
459
- end
460
-
461
- (prefixes << object_path).join("/")
462
- else
463
- object_path
464
- end
465
- end
466
-
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
480
- variable_counter = :"#{variable}_counter" if @collection
481
- [variable, variable_counter]
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
491
- end
492
- end
@@ -1,50 +0,0 @@
1
- module ActionView
2
- # This is the main entry point for rendering. It basically delegates
3
- # to other objects like TemplateRenderer and PartialRenderer which
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.
11
- class Renderer
12
- attr_accessor :lookup_context
13
-
14
- def initialize(lookup_context)
15
- @lookup_context = lookup_context
16
- end
17
-
18
- # Main render entry point shared by AV and AC.
19
- def render(context, options)
20
- if options.key?(:partial)
21
- render_partial(context, options)
22
- else
23
- render_template(context, options)
24
- end
25
- end
26
-
27
- # Render but returns a valid Rack body. If fibers are defined, we return
28
- # a streaming body that renders the template piece by piece.
29
- #
30
- # Note that partials are not supported to be rendered with streaming,
31
- # so in such cases, we just wrap them in an array.
32
- def render_body(context, options)
33
- if options.key?(:partial)
34
- [render_partial(context, options)]
35
- else
36
- StreamingTemplateRenderer.new(@lookup_context).render(context, options)
37
- end
38
- end
39
-
40
- # Direct accessor to template rendering.
41
- def render_template(context, options) #:nodoc:
42
- TemplateRenderer.new(@lookup_context).render(context, options)
43
- end
44
-
45
- # Direct access to partial rendering.
46
- def render_partial(context, options, &block) #:nodoc:
47
- PartialRenderer.new(@lookup_context).render(context, options, block)
48
- end
49
- end
50
- end
@@ -1,103 +0,0 @@
1
- require 'fiber'
2
-
3
- module ActionView
4
- # == TODO
5
- #
6
- # * Support streaming from child templates, partials and so on.
7
- # * Integrate exceptions with exceptron
8
- # * Rack::Cache needs to support streaming bodies
9
- class StreamingTemplateRenderer < TemplateRenderer #:nodoc:
10
- # A valid Rack::Body (i.e. it responds to each).
11
- # It is initialized with a block that, when called, starts
12
- # rendering the template.
13
- class Body #:nodoc:
14
- def initialize(&start)
15
- @start = start
16
- end
17
-
18
- def each(&block)
19
- begin
20
- @start.call(block)
21
- rescue Exception => exception
22
- log_error(exception)
23
- block.call ActionView::Base.streaming_completion_on_exception
24
- end
25
- self
26
- end
27
-
28
- private
29
-
30
- # This is the same logging logic as in ShowExceptions middleware.
31
- # TODO Once "exceptron" is in, refactor this piece to simply re-use exceptron.
32
- def log_error(exception) #:nodoc:
33
- logger = ActionView::Base.logger
34
- return unless logger
35
-
36
- message = "\n#{exception.class} (#{exception.message}):\n"
37
- message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
38
- message << " " << exception.backtrace.join("\n ")
39
- logger.fatal("#{message}\n\n")
40
- end
41
- end
42
-
43
- # For streaming, instead of rendering a given a template, we return a Body
44
- # object that responds to each. This object is initialized with a block
45
- # that knows how to render the template.
46
- def render_template(template, layout_name = nil, locals = {}) #:nodoc:
47
- return [super] unless layout_name && template.supports_streaming?
48
-
49
- locals ||= {}
50
- layout = layout_name && find_layout(layout_name, locals.keys)
51
-
52
- Body.new do |buffer|
53
- delayed_render(buffer, template, layout, @view, locals)
54
- end
55
- end
56
-
57
- private
58
-
59
- def delayed_render(buffer, template, layout, view, locals)
60
- # Wrap the given buffer in the StreamingBuffer and pass it to the
61
- # underlying template handler. Now, everytime something is concatenated
62
- # to the buffer, it is not appended to an array, but streamed straight
63
- # to the client.
64
- output = ActionView::StreamingBuffer.new(buffer)
65
- yielder = lambda { |*name| view._layout_for(*name) }
66
-
67
- instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
68
- fiber = Fiber.new do
69
- if layout
70
- layout.render(view, locals, output, &yielder)
71
- else
72
- # If you don't have a layout, just render the thing
73
- # and concatenate the final result. This is the same
74
- # as a layout with just <%= yield %>
75
- output.safe_concat view._layout_for
76
- end
77
- end
78
-
79
- # Set the view flow to support streaming. It will be aware
80
- # when to stop rendering the layout because it needs to search
81
- # something in the template and vice-versa.
82
- view.view_flow = StreamingFlow.new(view, fiber)
83
-
84
- # Yo! Start the fiber!
85
- fiber.resume
86
-
87
- # If the fiber is still alive, it means we need something
88
- # from the template, so start rendering it. If not, it means
89
- # the layout exited without requiring anything from the template.
90
- if fiber.alive?
91
- content = template.render(view, locals, &yielder)
92
-
93
- # Once rendering the template is done, sets its content in the :layout key.
94
- view.view_flow.set(:layout, content)
95
-
96
- # In case the layout continues yielding, we need to resume
97
- # the fiber until all yields are handled.
98
- fiber.resume while fiber.alive?
99
- end
100
- end
101
- end
102
- end
103
- end