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
@@ -0,0 +1,354 @@
1
+ require 'zlib'
2
+
3
+ module ActionView
4
+ # = Action View Asset URL Helpers
5
+ module Helpers
6
+ # This module provides methods for generating asset paths and
7
+ # urls.
8
+ #
9
+ # image_path("rails.png")
10
+ # # => "/assets/rails.png"
11
+ #
12
+ # image_url("rails.png")
13
+ # # => "http://www.example.com/assets/rails.png"
14
+ #
15
+ # === Using asset hosts
16
+ #
17
+ # By default, Rails links to these assets on the current host in the public
18
+ # folder, but you can direct Rails to link to assets from a dedicated asset
19
+ # server by setting <tt>ActionController::Base.asset_host</tt> in the application
20
+ # configuration, typically in <tt>config/environments/production.rb</tt>.
21
+ # For example, you'd define <tt>assets.example.com</tt> to be your asset
22
+ # host this way, inside the <tt>configure</tt> block of your environment-specific
23
+ # configuration files or <tt>config/application.rb</tt>:
24
+ #
25
+ # config.action_controller.asset_host = "assets.example.com"
26
+ #
27
+ # Helpers take that into account:
28
+ #
29
+ # image_tag("rails.png")
30
+ # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
31
+ # stylesheet_link_tag("application")
32
+ # # => <link href="http://assets.example.com/assets/application.css" media="screen" rel="stylesheet" />
33
+ #
34
+ # Browsers typically open at most two simultaneous connections to a single
35
+ # host, which means your assets often have to wait for other assets to finish
36
+ # downloading. You can alleviate this by using a <tt>%d</tt> wildcard in the
37
+ # +asset_host+. For example, "assets%d.example.com". If that wildcard is
38
+ # present Rails distributes asset requests among the corresponding four hosts
39
+ # "assets0.example.com", ..., "assets3.example.com". With this trick browsers
40
+ # will open eight simultaneous connections rather than two.
41
+ #
42
+ # image_tag("rails.png")
43
+ # # => <img alt="Rails" src="http://assets0.example.com/assets/rails.png" />
44
+ # stylesheet_link_tag("application")
45
+ # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
46
+ #
47
+ # To do this, you can either setup four actual hosts, or you can use wildcard
48
+ # DNS to CNAME the wildcard to a single asset host. You can read more about
49
+ # setting up your DNS CNAME records from your ISP.
50
+ #
51
+ # Note: This is purely a browser performance optimization and is not meant
52
+ # for server load balancing. See http://www.die.net/musings/page_load_time/
53
+ # for background.
54
+ #
55
+ # Alternatively, you can exert more control over the asset host by setting
56
+ # +asset_host+ to a proc like this:
57
+ #
58
+ # ActionController::Base.asset_host = Proc.new { |source|
59
+ # "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com"
60
+ # }
61
+ # image_tag("rails.png")
62
+ # # => <img alt="Rails" src="http://assets1.example.com/assets/rails.png" />
63
+ # stylesheet_link_tag("application")
64
+ # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
65
+ #
66
+ # The example above generates "http://assets1.example.com" and
67
+ # "http://assets2.example.com". This option is useful for example if
68
+ # you need fewer/more than four hosts, custom host names, etc.
69
+ #
70
+ # As you see the proc takes a +source+ parameter. That's a string with the
71
+ # absolute path of the asset, for example "/assets/rails.png".
72
+ #
73
+ # ActionController::Base.asset_host = Proc.new { |source|
74
+ # if source.ends_with?('.css')
75
+ # "http://stylesheets.example.com"
76
+ # else
77
+ # "http://assets.example.com"
78
+ # end
79
+ # }
80
+ # image_tag("rails.png")
81
+ # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
82
+ # stylesheet_link_tag("application")
83
+ # # => <link href="http://stylesheets.example.com/assets/application.css" media="screen" rel="stylesheet" />
84
+ #
85
+ # Alternatively you may ask for a second parameter +request+. That one is
86
+ # particularly useful for serving assets from an SSL-protected page. The
87
+ # example proc below disables asset hosting for HTTPS connections, while
88
+ # still sending assets for plain HTTP requests from asset hosts. If you don't
89
+ # have SSL certificates for each of the asset hosts this technique allows you
90
+ # to avoid warnings in the client about mixed media.
91
+ #
92
+ # config.action_controller.asset_host = Proc.new { |source, request|
93
+ # if request.ssl?
94
+ # "#{request.protocol}#{request.host_with_port}"
95
+ # else
96
+ # "#{request.protocol}assets.example.com"
97
+ # end
98
+ # }
99
+ #
100
+ # You can also implement a custom asset host object that responds to +call+
101
+ # and takes either one or two parameters just like the proc.
102
+ #
103
+ # config.action_controller.asset_host = AssetHostingWithMinimumSsl.new(
104
+ # "http://asset%d.example.com", "https://asset1.example.com"
105
+ # )
106
+ #
107
+ module AssetUrlHelper
108
+ URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
109
+
110
+ # Computes the path to asset in public directory. If :type
111
+ # options is set, a file extension will be appended and scoped
112
+ # to the corresponding public directory.
113
+ #
114
+ # All other asset *_path helpers delegate through this method.
115
+ #
116
+ # asset_path "application.js" # => /application.js
117
+ # asset_path "application", type: :javascript # => /javascripts/application.js
118
+ # asset_path "application", type: :stylesheet # => /stylesheets/application.css
119
+ # asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
120
+ def asset_path(source, options = {})
121
+ source = source.to_s
122
+ return "" unless source.present?
123
+ return source if source =~ URI_REGEXP
124
+
125
+ tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, '')
126
+
127
+ if extname = compute_asset_extname(source, options)
128
+ source = "#{source}#{extname}"
129
+ end
130
+
131
+ if source[0] != ?/
132
+ source = compute_asset_path(source, options)
133
+ end
134
+
135
+ relative_url_root = defined?(config.relative_url_root) && config.relative_url_root
136
+ if relative_url_root
137
+ source = "#{relative_url_root}#{source}" unless source.starts_with?("#{relative_url_root}/")
138
+ end
139
+
140
+ if host = compute_asset_host(source, options)
141
+ source = "#{host}#{source}"
142
+ end
143
+
144
+ "#{source}#{tail}"
145
+ end
146
+ alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with a asset_path named route
147
+
148
+ # Computes the full URL to a asset in the public directory. This
149
+ # will use +asset_path+ internally, so most of their behaviors
150
+ # will be the same.
151
+ def asset_url(source, options = {})
152
+ path_to_asset(source, options.merge(:protocol => :request))
153
+ end
154
+ alias_method :url_to_asset, :asset_url # aliased to avoid conflicts with an asset_url named route
155
+
156
+ ASSET_EXTENSIONS = {
157
+ javascript: '.js',
158
+ stylesheet: '.css'
159
+ }
160
+
161
+ # Compute extname to append to asset path. Returns nil if
162
+ # nothing should be added.
163
+ def compute_asset_extname(source, options = {})
164
+ return if options[:extname] == false
165
+ extname = options[:extname] || ASSET_EXTENSIONS[options[:type]]
166
+ extname if extname && File.extname(source) != extname
167
+ end
168
+
169
+ # Maps asset types to public directory.
170
+ ASSET_PUBLIC_DIRECTORIES = {
171
+ audio: '/audios',
172
+ font: '/fonts',
173
+ image: '/images',
174
+ javascript: '/javascripts',
175
+ stylesheet: '/stylesheets',
176
+ video: '/videos'
177
+ }
178
+
179
+ # Computes asset path to public directory. Plugins and
180
+ # extensions can override this method to point to custom assets
181
+ # or generate digested paths or query strings.
182
+ def compute_asset_path(source, options = {})
183
+ dir = ASSET_PUBLIC_DIRECTORIES[options[:type]] || ""
184
+ File.join(dir, source)
185
+ end
186
+
187
+ # Pick an asset host for this source. Returns +nil+ if no host is set,
188
+ # the host if no wildcard is set, the host interpolated with the
189
+ # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
190
+ # or the value returned from invoking call on an object responding to call
191
+ # (proc or otherwise).
192
+ def compute_asset_host(source = "", options = {})
193
+ request = self.request if respond_to?(:request)
194
+ host = config.asset_host if defined? config.asset_host
195
+ host ||= request.base_url if request && options[:protocol] == :request
196
+ return unless host
197
+
198
+ if host.respond_to?(:call)
199
+ arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity
200
+ args = [source]
201
+ args << request if request && (arity > 1 || arity < 0)
202
+ host = host.call(*args)
203
+ elsif host =~ /%d/
204
+ host = host % (Zlib.crc32(source) % 4)
205
+ end
206
+
207
+ if host =~ URI_REGEXP
208
+ host
209
+ else
210
+ protocol = options[:protocol] || config.default_asset_host_protocol || (request ? :request : :relative)
211
+ case protocol
212
+ when :relative
213
+ "//#{host}"
214
+ when :request
215
+ "#{request.protocol}#{host}"
216
+ else
217
+ "#{protocol}://#{host}"
218
+ end
219
+ end
220
+ end
221
+
222
+ # Computes the path to a javascript asset in the public javascripts directory.
223
+ # If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
224
+ # Full paths from the document root will be passed through.
225
+ # Used internally by javascript_include_tag to build the script path.
226
+ #
227
+ # javascript_path "xmlhr" # => /javascripts/xmlhr.js
228
+ # javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
229
+ # javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
230
+ # javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr
231
+ # javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
232
+ def javascript_path(source, options = {})
233
+ path_to_asset(source, {type: :javascript}.merge!(options))
234
+ end
235
+ alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
236
+
237
+ # Computes the full URL to a javascript asset in the public javascripts directory.
238
+ # This will use +javascript_path+ internally, so most of their behaviors will be the same.
239
+ def javascript_url(source, options = {})
240
+ url_to_asset(source, {type: :javascript}.merge!(options))
241
+ end
242
+ alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
243
+
244
+ # Computes the path to a stylesheet asset in the public stylesheets directory.
245
+ # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
246
+ # Full paths from the document root will be passed through.
247
+ # Used internally by +stylesheet_link_tag+ to build the stylesheet path.
248
+ #
249
+ # stylesheet_path "style" # => /stylesheets/style.css
250
+ # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
251
+ # stylesheet_path "/dir/style.css" # => /dir/style.css
252
+ # stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style
253
+ # stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css
254
+ def stylesheet_path(source, options = {})
255
+ path_to_asset(source, {type: :stylesheet}.merge!(options))
256
+ end
257
+ alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
258
+
259
+ # Computes the full URL to a stylesheet asset in the public stylesheets directory.
260
+ # This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
261
+ def stylesheet_url(source, options = {})
262
+ url_to_asset(source, {type: :stylesheet}.merge!(options))
263
+ end
264
+ alias_method :url_to_stylesheet, :stylesheet_url # aliased to avoid conflicts with a stylesheet_url named route
265
+
266
+ # Computes the path to an image asset.
267
+ # Full paths from the document root will be passed through.
268
+ # Used internally by +image_tag+ to build the image path:
269
+ #
270
+ # image_path("edit") # => "/assets/edit"
271
+ # image_path("edit.png") # => "/assets/edit.png"
272
+ # image_path("icons/edit.png") # => "/assets/icons/edit.png"
273
+ # image_path("/icons/edit.png") # => "/icons/edit.png"
274
+ # image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
275
+ #
276
+ # If you have images as application resources this method may conflict with their named routes.
277
+ # The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
278
+ # plugin authors are encouraged to do so.
279
+ def image_path(source, options = {})
280
+ path_to_asset(source, {type: :image}.merge!(options))
281
+ end
282
+ alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
283
+
284
+ # Computes the full URL to an image asset.
285
+ # This will use +image_path+ internally, so most of their behaviors will be the same.
286
+ def image_url(source, options = {})
287
+ url_to_asset(source, {type: :image}.merge!(options))
288
+ end
289
+ alias_method :url_to_image, :image_url # aliased to avoid conflicts with an image_url named route
290
+
291
+ # Computes the path to a video asset in the public videos directory.
292
+ # Full paths from the document root will be passed through.
293
+ # Used internally by +video_tag+ to build the video path.
294
+ #
295
+ # video_path("hd") # => /videos/hd
296
+ # video_path("hd.avi") # => /videos/hd.avi
297
+ # video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
298
+ # video_path("/trailers/hd.avi") # => /trailers/hd.avi
299
+ # video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi
300
+ def video_path(source, options = {})
301
+ path_to_asset(source, {type: :video}.merge!(options))
302
+ end
303
+ alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
304
+
305
+ # Computes the full URL to a video asset in the public videos directory.
306
+ # This will use +video_path+ internally, so most of their behaviors will be the same.
307
+ def video_url(source, options = {})
308
+ url_to_asset(source, {type: :video}.merge!(options))
309
+ end
310
+ alias_method :url_to_video, :video_url # aliased to avoid conflicts with an video_url named route
311
+
312
+ # Computes the path to an audio asset in the public audios directory.
313
+ # Full paths from the document root will be passed through.
314
+ # Used internally by +audio_tag+ to build the audio path.
315
+ #
316
+ # audio_path("horse") # => /audios/horse
317
+ # audio_path("horse.wav") # => /audios/horse.wav
318
+ # audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav
319
+ # audio_path("/sounds/horse.wav") # => /sounds/horse.wav
320
+ # audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav
321
+ def audio_path(source, options = {})
322
+ path_to_asset(source, {type: :audio}.merge!(options))
323
+ end
324
+ alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
325
+
326
+ # Computes the full URL to an audio asset in the public audios directory.
327
+ # This will use +audio_path+ internally, so most of their behaviors will be the same.
328
+ def audio_url(source, options = {})
329
+ url_to_asset(source, {type: :audio}.merge!(options))
330
+ end
331
+ alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
332
+
333
+ # Computes the path to a font asset.
334
+ # Full paths from the document root will be passed through.
335
+ #
336
+ # font_path("font") # => /assets/font
337
+ # font_path("font.ttf") # => /assets/font.ttf
338
+ # font_path("dir/font.ttf") # => /assets/dir/font.ttf
339
+ # font_path("/dir/font.ttf") # => /dir/font.ttf
340
+ # font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
341
+ def font_path(source, options = {})
342
+ path_to_asset(source, {type: :font}.merge!(options))
343
+ end
344
+ alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
345
+
346
+ # Computes the full URL to a font asset.
347
+ # This will use +font_path+ internally, so most of their behaviors will be the same.
348
+ def font_url(source, options = {})
349
+ url_to_asset(source, {type: :font}.merge!(options))
350
+ end
351
+ alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route
352
+ end
353
+ end
354
+ end
@@ -2,7 +2,7 @@ require 'set'
2
2
 
3
3
  module ActionView
4
4
  # = Action View Atom Feed Helpers
5
- module Helpers #:nodoc:
5
+ module Helpers
6
6
  module AtomFeedHelper
7
7
  # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other
8
8
  # template languages).
@@ -12,7 +12,7 @@ module ActionView
12
12
  # config/routes.rb:
13
13
  # Basecamp::Application.routes.draw do
14
14
  # resources :posts
15
- # root :to => "posts#index"
15
+ # root to: "posts#index"
16
16
  # end
17
17
  #
18
18
  # app/controllers/posts_controller.rb:
@@ -37,7 +37,7 @@ module ActionView
37
37
  # @posts.each do |post|
38
38
  # feed.entry(post) do |entry|
39
39
  # entry.title(post.title)
40
- # entry.content(post.body, :type => 'html')
40
+ # entry.content(post.body, type: 'html')
41
41
  #
42
42
  # entry.author do |author|
43
43
  # author.name("DHH")
@@ -69,7 +69,7 @@ module ActionView
69
69
  # @posts.each do |post|
70
70
  # feed.entry(post) do |entry|
71
71
  # entry.title(post.title)
72
- # entry.content(post.body, :type => 'html')
72
+ # entry.content(post.body, type: 'html')
73
73
  # entry.tag!('app:edited', Time.now)
74
74
  #
75
75
  # entry.author do |author|
@@ -80,11 +80,11 @@ module ActionView
80
80
  # end
81
81
  #
82
82
  # The Atom spec defines five elements (content rights title subtitle
83
- # summary) which may directly contain xhtml content if :type => 'xhtml'
83
+ # summary) which may directly contain xhtml content if type: 'xhtml'
84
84
  # is specified as an attribute. If so, this helper will take care of
85
85
  # the enclosing div and xhtml namespace declaration. Example usage:
86
86
  #
87
- # entry.summary :type => 'xhtml' do |xhtml|
87
+ # entry.summary type: 'xhtml' do |xhtml|
88
88
  # xhtml.p pluralize(order.line_items.count, "line item")
89
89
  # xhtml.p "Shipped to #{order.address}"
90
90
  # xhtml.p "Paid by #{order.pay_type}"
@@ -124,7 +124,7 @@ module ActionView
124
124
  end
125
125
  end
126
126
 
127
- class AtomBuilder
127
+ class AtomBuilder #:nodoc:
128
128
  XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set
129
129
 
130
130
  def initialize(xml)
@@ -149,7 +149,7 @@ module ActionView
149
149
 
150
150
  # True if the method name matches one of the five elements defined
151
151
  # in the Atom spec as potentially containing XHTML content and
152
- # if :type => 'xhtml' is, in fact, specified.
152
+ # if type: 'xhtml' is, in fact, specified.
153
153
  def xhtml_block?(method, arguments)
154
154
  if XHTML_TAG_NAMES.include?(method.to_s)
155
155
  last = arguments.last
@@ -158,7 +158,7 @@ module ActionView
158
158
  end
159
159
  end
160
160
 
161
- class AtomFeedBuilder < AtomBuilder
161
+ class AtomFeedBuilder < AtomBuilder #:nodoc:
162
162
  def initialize(xml, view, feed_options = {})
163
163
  @xml, @view, @feed_options = xml, view, feed_options
164
164
  end
@@ -176,6 +176,7 @@ module ActionView
176
176
  # * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
177
177
  # * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
178
178
  # * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
179
+ # * <tt>:type</tt>: The TYPE for this entry. Defaults to "text/html".
179
180
  def entry(record, options = {})
180
181
  @xml.entry do
181
182
  @xml.id(options[:id] || "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
@@ -188,7 +189,9 @@ module ActionView
188
189
  @xml.updated((options[:updated] || record.updated_at).xmlschema)
189
190
  end
190
191
 
191
- @xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:url] || @view.polymorphic_url(record))
192
+ type = options.fetch(:type, 'text/html')
193
+
194
+ @xml.link(:rel => 'alternate', :type => type, :href => options[:url] || @view.polymorphic_url(record))
192
195
 
193
196
  yield AtomBuilder.new(@xml)
194
197
  end
@@ -2,38 +2,117 @@ module ActionView
2
2
  # = Action View Cache Helper
3
3
  module Helpers
4
4
  module CacheHelper
5
- # This helper exposes a method for caching fragments of a view
5
+ # This helper exposes a method for caching fragments of a view
6
6
  # rather than an entire action or page. This technique is useful
7
7
  # caching pieces like menus, lists of newstopics, static HTML
8
8
  # fragments, and so on. This method takes a block that contains
9
- # the content you wish to cache.
9
+ # the content you wish to cache.
10
10
  #
11
- # See ActionController::Caching::Fragments for usage instructions.
11
+ # The best way to use this is by doing key-based cache expiration
12
+ # on top of a cache store like Memcached that'll automatically
13
+ # kick out old entries. For more on key-based expiration, see:
14
+ # http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works
12
15
  #
13
- # ==== Examples
14
- # If you want to cache a navigation menu, you can do following:
16
+ # When using this method, you list the cache dependency as the name of the cache, like so:
15
17
  #
16
- # <% cache do %>
17
- # <%= render :partial => "menu" %>
18
+ # <% cache project do %>
19
+ # <b>All the topics on this project</b>
20
+ # <%= render project.topics %>
18
21
  # <% end %>
19
22
  #
20
- # You can also cache static content:
23
+ # This approach will assume that when a new topic is added, you'll touch
24
+ # the project. The cache key generated from this call will be something like:
21
25
  #
22
- # <% cache do %>
23
- # <p>Hello users! Welcome to our website!</p>
26
+ # views/projects/123-20120806214154/7a1156131a6928cb0026877f8b749ac9
27
+ # ^class ^id ^updated_at ^template tree digest
28
+ #
29
+ # The cache is thus automatically bumped whenever the project updated_at is touched.
30
+ #
31
+ # If your template cache depends on multiple sources (try to avoid this to keep things simple),
32
+ # you can name all these dependencies as part of an array:
33
+ #
34
+ # <% cache [ project, current_user ] do %>
35
+ # <b>All the topics on this project</b>
36
+ # <%= render project.topics %>
24
37
  # <% end %>
25
38
  #
26
- # Static content with embedded ruby content can be cached as
27
- # well:
39
+ # This will include both records as part of the cache key and updating either of them will
40
+ # expire the cache.
41
+ #
42
+ # ==== Template digest
43
+ #
44
+ # The template digest that's added to the cache key is computed by taking an md5 of the
45
+ # contents of the entire template file. This ensures that your caches will automatically
46
+ # expire when you change the template file.
47
+ #
48
+ # Note that the md5 is taken of the entire template file, not just what's within the
49
+ # cache do/end call. So it's possible that changing something outside of that call will
50
+ # still expire the cache.
51
+ #
52
+ # Additionally, the digestor will automatically look through your template file for
53
+ # explicit and implicit dependencies, and include those as part of the digest.
54
+ #
55
+ # The digestor can be bypassed by passing skip_digest: true as an option to the cache call:
56
+ #
57
+ # <% cache project, skip_digest: true do %>
58
+ # <b>All the topics on this project</b>
59
+ # <%= render project.topics %>
60
+ # <% end %>
61
+ #
62
+ # ==== Implicit dependencies
63
+ #
64
+ # Most template dependencies can be derived from calls to render in the template itself.
65
+ # Here are some examples of render calls that Cache Digests knows how to decode:
66
+ #
67
+ # render partial: "comments/comment", collection: commentable.comments
68
+ # render "comments/comments"
69
+ # render 'comments/comments'
70
+ # render('comments/comments')
71
+ #
72
+ # render "header" => render("comments/header")
73
+ #
74
+ # render(@topic) => render("topics/topic")
75
+ # render(topics) => render("topics/topic")
76
+ # render(message.topics) => render("topics/topic")
77
+ #
78
+ # It's not possible to derive all render calls like that, though. Here are a few examples of things that can't be derived:
79
+ #
80
+ # render group_of_attachments
81
+ # render @project.documents.where(published: true).order('created_at')
82
+ #
83
+ # You will have to rewrite those to the explicit form:
84
+ #
85
+ # render partial: 'attachments/attachment', collection: group_of_attachments
86
+ # render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')
87
+ #
88
+ # === Explicit dependencies
89
+ #
90
+ # Some times you'll have template dependencies that can't be derived at all. This is typically
91
+ # the case when you have template rendering that happens in helpers. Here's an example:
92
+ #
93
+ # <%= render_sortable_todolists @project.todolists %>
94
+ #
95
+ # You'll need to use a special comment format to call those out:
96
+ #
97
+ # <%# Template Dependency: todolists/todolist %>
98
+ # <%= render_sortable_todolists @project.todolists %>
99
+ #
100
+ # The pattern used to match these is /# Template Dependency: ([^ ]+)/, so it's important that you type it out just so.
101
+ # You can only declare one template dependency per line.
102
+ #
103
+ # === External dependencies
28
104
  #
29
- # <% cache do %>
30
- # Topics:
31
- # <%= render :partial => "topics", :collection => @topic_list %>
32
- # <i>Topics listed alphabetically</i>
33
- # <% end %>
105
+ # If you use a helper method, for example, inside of a cached block and you then update that helper,
106
+ # you'll have to bump the cache as well. It doesn't really matter how you do it, but the md5 of the template file
107
+ # must change. One recommendation is to simply be explicit in a comment, like:
108
+ #
109
+ # <%# Helper Dependency Updated: May 6, 2012 at 6pm %>
110
+ # <%= some_helper_method(person) %>
111
+ #
112
+ # Now all you'll have to do is change that timestamp when the helper method changes.
34
113
  def cache(name = {}, options = nil, &block)
35
114
  if controller.perform_caching
36
- safe_concat(fragment_for(name, options, &block))
115
+ safe_concat(fragment_for(cache_fragment_name(name, options), options, &block))
37
116
  else
38
117
  yield
39
118
  end
@@ -41,7 +120,60 @@ module ActionView
41
120
  nil
42
121
  end
43
122
 
123
+ # Cache fragments of a view if +condition+ is true
124
+ #
125
+ # <%= cache_if admin?, project do %>
126
+ # <b>All the topics on this project</b>
127
+ # <%= render project.topics %>
128
+ # <% end %>
129
+ def cache_if(condition, name = {}, options = nil, &block)
130
+ if condition
131
+ cache(name, options, &block)
132
+ else
133
+ yield
134
+ end
135
+
136
+ nil
137
+ end
138
+
139
+ # Cache fragments of a view unless +condition+ is true
140
+ #
141
+ # <%= cache_unless admin?, project do %>
142
+ # <b>All the topics on this project</b>
143
+ # <%= render project.topics %>
144
+ # <% end %>
145
+ def cache_unless(condition, name = {}, options = nil, &block)
146
+ cache_if !condition, name, options, &block
147
+ end
148
+
149
+ # This helper returns the name of a cache key for a given fragment cache
150
+ # call. By supplying skip_digest: true to cache, the digestion of cache
151
+ # fragments can be manually bypassed. This is useful when cache fragments
152
+ # cannot be manually expired unless you know the exact key which is the
153
+ # case when using memcached.
154
+ def cache_fragment_name(name = {}, options = nil)
155
+ skip_digest = options && options[:skip_digest]
156
+
157
+ if skip_digest
158
+ name
159
+ else
160
+ fragment_name_with_digest(name)
161
+ end
162
+ end
163
+
44
164
  private
165
+
166
+ def fragment_name_with_digest(name) #:nodoc:
167
+ if @virtual_path
168
+ [
169
+ *Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name),
170
+ Digestor.digest(@virtual_path, formats.last.to_sym, lookup_context, dependencies: view_cache_dependencies)
171
+ ]
172
+ else
173
+ name
174
+ end
175
+ end
176
+
45
177
  # TODO: Create an object that has caching read/write on it
46
178
  def fragment_for(name = {}, options = nil, &block) #:nodoc:
47
179
  if fragment = controller.read_fragment(name, options)