actionview 4.1.13 → 6.1.3.1

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

Potentially problematic release.


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

Files changed (124) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +181 -359
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +12 -6
  5. data/lib/action_view/base.rb +115 -43
  6. data/lib/action_view/buffers.rb +22 -4
  7. data/lib/action_view/cache_expiry.rb +52 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker.rb +61 -21
  10. data/lib/action_view/digestor.rb +89 -84
  11. data/lib/action_view/flows.rb +12 -13
  12. data/lib/action_view/gem_version.rb +6 -4
  13. data/lib/action_view/helpers/active_model_helper.rb +16 -11
  14. data/lib/action_view/helpers/asset_tag_helper.rb +311 -105
  15. data/lib/action_view/helpers/asset_url_helper.rb +197 -80
  16. data/lib/action_view/helpers/atom_feed_helper.rb +20 -17
  17. data/lib/action_view/helpers/cache_helper.rb +109 -45
  18. data/lib/action_view/helpers/capture_helper.rb +20 -22
  19. data/lib/action_view/helpers/controller_helper.rb +15 -4
  20. data/lib/action_view/helpers/csp_helper.rb +26 -0
  21. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  22. data/lib/action_view/helpers/date_helper.rb +245 -140
  23. data/lib/action_view/helpers/debug_helper.rb +14 -17
  24. data/lib/action_view/helpers/form_helper.rb +875 -148
  25. data/lib/action_view/helpers/form_options_helper.rb +128 -82
  26. data/lib/action_view/helpers/form_tag_helper.rb +253 -91
  27. data/lib/action_view/helpers/javascript_helper.rb +37 -15
  28. data/lib/action_view/helpers/number_helper.rb +100 -77
  29. data/lib/action_view/helpers/output_safety_helper.rb +42 -10
  30. data/lib/action_view/helpers/rendering_helper.rb +26 -15
  31. data/lib/action_view/helpers/sanitize_helper.rb +79 -164
  32. data/lib/action_view/helpers/tag_helper.rb +277 -64
  33. data/lib/action_view/helpers/tags/base.rb +143 -92
  34. data/lib/action_view/helpers/tags/check_box.rb +20 -19
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -30
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  39. data/lib/action_view/helpers/tags/collection_select.rb +4 -2
  40. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  41. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  42. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  43. data/lib/action_view/helpers/tags/datetime_field.rb +14 -5
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  49. data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
  50. data/lib/action_view/helpers/tags/label.rb +41 -22
  51. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  54. data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
  55. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +3 -0
  58. data/lib/action_view/helpers/tags/select.rb +11 -10
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +7 -1
  61. data/lib/action_view/helpers/tags/text_field.rb +11 -7
  62. data/lib/action_view/helpers/tags/time_field.rb +3 -2
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +39 -0
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  68. data/lib/action_view/helpers/tags.rb +4 -1
  69. data/lib/action_view/helpers/text_helper.rb +80 -45
  70. data/lib/action_view/helpers/translation_helper.rb +148 -67
  71. data/lib/action_view/helpers/url_helper.rb +289 -147
  72. data/lib/action_view/helpers.rb +5 -3
  73. data/lib/action_view/layouts.rb +68 -63
  74. data/lib/action_view/log_subscriber.rb +80 -13
  75. data/lib/action_view/lookup_context.rb +137 -92
  76. data/lib/action_view/model_naming.rb +4 -2
  77. data/lib/action_view/path_set.rb +30 -16
  78. data/lib/action_view/railtie.rb +62 -13
  79. data/lib/action_view/record_identifier.rb +53 -26
  80. data/lib/action_view/renderer/abstract_renderer.rb +152 -13
  81. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  82. data/lib/action_view/renderer/object_renderer.rb +34 -0
  83. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  84. data/lib/action_view/renderer/partial_renderer.rb +61 -261
  85. data/lib/action_view/renderer/renderer.rb +67 -6
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +58 -54
  87. data/lib/action_view/renderer/template_renderer.rb +83 -75
  88. data/lib/action_view/rendering.rb +73 -46
  89. data/lib/action_view/routing_url_for.rb +54 -17
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template/error.rb +44 -29
  92. data/lib/action_view/template/handlers/builder.rb +12 -13
  93. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  94. data/lib/action_view/template/handlers/erb.rb +23 -89
  95. data/lib/action_view/template/handlers/html.rb +11 -0
  96. data/lib/action_view/template/handlers/raw.rb +4 -4
  97. data/lib/action_view/template/handlers.rb +22 -9
  98. data/lib/action_view/template/html.rb +10 -11
  99. data/lib/action_view/template/inline.rb +22 -0
  100. data/lib/action_view/template/raw_file.rb +25 -0
  101. data/lib/action_view/template/renderable.rb +24 -0
  102. data/lib/action_view/template/resolver.rb +267 -181
  103. data/lib/action_view/template/sources/file.rb +17 -0
  104. data/lib/action_view/template/sources.rb +13 -0
  105. data/lib/action_view/template/text.rb +8 -10
  106. data/lib/action_view/template/types.rb +18 -18
  107. data/lib/action_view/template.rb +109 -99
  108. data/lib/action_view/test_case.rb +73 -53
  109. data/lib/action_view/testing/resolvers.rb +24 -33
  110. data/lib/action_view/unbound_template.rb +31 -0
  111. data/lib/action_view/version.rb +3 -1
  112. data/lib/action_view/view_paths.rb +74 -44
  113. data/lib/action_view.rb +14 -9
  114. data/lib/assets/compiled/rails-ujs.js +746 -0
  115. metadata +71 -26
  116. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  117. data/lib/action_view/tasks/dependencies.rake +0 -23
  118. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  119. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  120. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  121. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  122. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  123. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
  124. data/lib/action_view/vendor/html-scanner.rb +0 -20
@@ -1,8 +1,11 @@
1
- require 'set'
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "active_support/core_ext/symbol/starts_ends_with"
2
5
 
3
6
  module ActionView
4
7
  # = Action View Atom Feed Helpers
5
- module Helpers
8
+ module Helpers #:nodoc:
6
9
  module AtomFeedHelper
7
10
  # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other
8
11
  # template languages).
@@ -16,7 +19,7 @@ module ActionView
16
19
  # end
17
20
  #
18
21
  # app/controllers/posts_controller.rb:
19
- # class PostsController < ApplicationController::Base
22
+ # class PostsController < ApplicationController
20
23
  # # GET /posts.html
21
24
  # # GET /posts.atom
22
25
  # def index
@@ -51,7 +54,7 @@ module ActionView
51
54
  # * <tt>:language</tt>: Defaults to "en-US".
52
55
  # * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
53
56
  # * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
54
- # * <tt>:id</tt>: The id for this feed. Defaults to "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}"
57
+ # * <tt>:id</tt>: The id for this feed. Defaults to "tag:localhost,2005:/posts", in this case.
55
58
  # * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
56
59
  # created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
57
60
  # 2005 is used (as an "I don't care" value).
@@ -103,7 +106,7 @@ module ActionView
103
106
  xml = options.delete(:xml) || eval("xml", block.binding)
104
107
  xml.instruct!
105
108
  if options[:instruct]
106
- options[:instruct].each do |target,attrs|
109
+ options[:instruct].each do |target, attrs|
107
110
  if attrs.respond_to?(:keys)
108
111
  xml.instruct!(target, attrs)
109
112
  elsif attrs.respond_to?(:each)
@@ -112,13 +115,13 @@ module ActionView
112
115
  end
113
116
  end
114
117
 
115
- feed_opts = {"xml:lang" => options[:language] || "en-US", "xmlns" => 'http://www.w3.org/2005/Atom'}
116
- feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
118
+ feed_opts = { "xml:lang" => options[:language] || "en-US", "xmlns" => "http://www.w3.org/2005/Atom" }
119
+ feed_opts.merge!(options).select! { |k, _| k.start_with?("xml") }
117
120
 
118
121
  xml.feed(feed_opts) do
119
122
  xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
120
- xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
121
- xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
123
+ xml.link(rel: "alternate", type: "text/html", href: options[:root_url] || (request.protocol + request.host_with_port))
124
+ xml.link(rel: "self", type: "application/atom+xml", href: options[:url] || request.url)
122
125
 
123
126
  yield AtomFeedBuilder.new(xml, self, options)
124
127
  end
@@ -132,13 +135,13 @@ module ActionView
132
135
  end
133
136
 
134
137
  private
135
- # Delegate to xml builder, first wrapping the element in a xhtml
138
+ # Delegate to xml builder, first wrapping the element in an xhtml
136
139
  # namespaced div element if the method and arguments indicate
137
140
  # that an xhtml_block? is desired.
138
141
  def method_missing(method, *arguments, &block)
139
142
  if xhtml_block?(method, arguments)
140
143
  @xml.__send__(method, *arguments) do
141
- @xml.div(:xmlns => 'http://www.w3.org/1999/xhtml') do |xhtml|
144
+ @xml.div(xmlns: "http://www.w3.org/1999/xhtml") do |xhtml|
142
145
  block.call(xhtml)
143
146
  end
144
147
  end
@@ -153,7 +156,7 @@ module ActionView
153
156
  def xhtml_block?(method, arguments)
154
157
  if XHTML_TAG_NAMES.include?(method.to_s)
155
158
  last = arguments.last
156
- last.is_a?(Hash) && last[:type].to_s == 'xhtml'
159
+ last.is_a?(Hash) && last[:type].to_s == "xhtml"
157
160
  end
158
161
  end
159
162
  end
@@ -163,7 +166,7 @@ module ActionView
163
166
  @xml, @view, @feed_options = xml, view, feed_options
164
167
  end
165
168
 
166
- # Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
169
+ # Accepts a Date or Time object and inserts it in the proper format. If +nil+ is passed, current time in UTC is used.
167
170
  def updated(date_or_time = nil)
168
171
  @xml.updated((date_or_time || Time.now.utc).xmlschema)
169
172
  end
@@ -174,7 +177,7 @@ module ActionView
174
177
  #
175
178
  # * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
176
179
  # * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
177
- # * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
180
+ # * <tt>:url</tt>: The URL for this entry or +false+ or +nil+ for not having a link tag. Defaults to the +polymorphic_url+ for the record.
178
181
  # * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
179
182
  # * <tt>:type</tt>: The TYPE for this entry. Defaults to "text/html".
180
183
  def entry(record, options = {})
@@ -189,15 +192,15 @@ module ActionView
189
192
  @xml.updated((options[:updated] || record.updated_at).xmlschema)
190
193
  end
191
194
 
192
- type = options.fetch(:type, 'text/html')
195
+ type = options.fetch(:type, "text/html")
193
196
 
194
- @xml.link(:rel => 'alternate', :type => type, :href => options[:url] || @view.polymorphic_url(record))
197
+ url = options.fetch(:url) { @view.polymorphic_url(record) }
198
+ @xml.link(rel: "alternate", type: type, href: url) if url
195
199
 
196
200
  yield AtomBuilder.new(@xml)
197
201
  end
198
202
  end
199
203
  end
200
-
201
204
  end
202
205
  end
203
206
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  # = Action View Cache Helper
3
- module Helpers
5
+ module Helpers #:nodoc:
4
6
  module CacheHelper
5
7
  # This helper exposes a method for caching fragments of a view
6
8
  # rather than an entire action or page. This technique is useful
@@ -8,10 +10,9 @@ module ActionView
8
10
  # fragments, and so on. This method takes a block that contains
9
11
  # the content you wish to cache.
10
12
  #
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
13
+ # The best way to use this is by doing recyclable key-based cache expiration
14
+ # on top of a cache store like Memcached or Redis that'll automatically
15
+ # kick out old entries.
15
16
  #
16
17
  # When using this method, you list the cache dependency as the name of the cache, like so:
17
18
  #
@@ -23,10 +24,14 @@ module ActionView
23
24
  # This approach will assume that when a new topic is added, you'll touch
24
25
  # the project. The cache key generated from this call will be something like:
25
26
  #
26
- # views/projects/123-20120806214154/7a1156131a6928cb0026877f8b749ac9
27
- # ^class ^id ^updated_at ^template tree digest
27
+ # views/template/action:7a1156131a6928cb0026877f8b749ac9/projects/123
28
+ # ^template path ^template tree digest ^class ^id
28
29
  #
29
- # The cache is thus automatically bumped whenever the project updated_at is touched.
30
+ # This cache key is stable, but it's combined with a cache version derived from the project
31
+ # record. When the project updated_at is touched, the #cache_version changes, even
32
+ # if the key stays stable. This means that unlike a traditional key-based cache expiration
33
+ # approach, you won't be generating cache trash, unused keys, simply because the dependent
34
+ # record is updated.
30
35
  #
31
36
  # If your template cache depends on multiple sources (try to avoid this to keep things simple),
32
37
  # you can name all these dependencies as part of an array:
@@ -39,13 +44,13 @@ module ActionView
39
44
  # This will include both records as part of the cache key and updating either of them will
40
45
  # expire the cache.
41
46
  #
42
- # ==== Template digest
47
+ # ==== \Template digest
43
48
  #
44
- # The template digest that's added to the cache key is computed by taking an md5 of the
49
+ # The template digest that's added to the cache key is computed by taking an MD5 of the
45
50
  # contents of the entire template file. This ensures that your caches will automatically
46
51
  # expire when you change the template file.
47
52
  #
48
- # Note that the md5 is taken of the entire template file, not just what's within the
53
+ # Note that the MD5 is taken of the entire template file, not just what's within the
49
54
  # cache do/end call. So it's possible that changing something outside of that call will
50
55
  # still expire the cache.
51
56
  #
@@ -69,13 +74,14 @@ module ActionView
69
74
  # render 'comments/comments'
70
75
  # render('comments/comments')
71
76
  #
72
- # render "header" => render("comments/header")
77
+ # render "header" translates to render("comments/header")
73
78
  #
74
- # render(@topic) => render("topics/topic")
75
- # render(topics) => render("topics/topic")
76
- # render(message.topics) => render("topics/topic")
79
+ # render(@topic) translates to render("topics/topic")
80
+ # render(topics) translates to render("topics/topic")
81
+ # render(message.topics) translates to render("topics/topic")
77
82
  #
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:
83
+ # It's not possible to derive all render calls like that, though.
84
+ # Here are a few examples of things that can't be derived:
79
85
  #
80
86
  # render group_of_attachments
81
87
  # render @project.documents.where(published: true).order('created_at')
@@ -87,7 +93,7 @@ module ActionView
87
93
  #
88
94
  # === Explicit dependencies
89
95
  #
90
- # Some times you'll have template dependencies that can't be derived at all. This is typically
96
+ # Sometimes you'll have template dependencies that can't be derived at all. This is typically
91
97
  # the case when you have template rendering that happens in helpers. Here's an example:
92
98
  #
93
99
  # <%= render_sortable_todolists @project.todolists %>
@@ -97,22 +103,70 @@ module ActionView
97
103
  # <%# Template Dependency: todolists/todolist %>
98
104
  # <%= render_sortable_todolists @project.todolists %>
99
105
  #
100
- # The pattern used to match these is /# Template Dependency: ([^ ]+)/, so it's important that you type it out just so.
106
+ # In some cases, like a single table inheritance setup, you might have
107
+ # a bunch of explicit dependencies. Instead of writing every template out,
108
+ # you can use a wildcard to match any template in a directory:
109
+ #
110
+ # <%# Template Dependency: events/* %>
111
+ # <%= render_categorizable_events @person.events %>
112
+ #
113
+ # This marks every template in the directory as a dependency. To find those
114
+ # templates, the wildcard path must be absolutely defined from <tt>app/views</tt> or paths
115
+ # otherwise added with +prepend_view_path+ or +append_view_path+.
116
+ # This way the wildcard for <tt>app/views/recordings/events</tt> would be <tt>recordings/events/*</tt> etc.
117
+ #
118
+ # The pattern used to match explicit dependencies is <tt>/# Template Dependency: (\S+)/</tt>,
119
+ # so it's important that you type it out just so.
101
120
  # You can only declare one template dependency per line.
102
121
  #
103
122
  # === External dependencies
104
123
  #
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
124
+ # If you use a helper method, for example, inside a cached block and
125
+ # you then update that helper, you'll have to bump the cache as well.
126
+ # It doesn't really matter how you do it, but the MD5 of the template file
107
127
  # must change. One recommendation is to simply be explicit in a comment, like:
108
128
  #
109
129
  # <%# Helper Dependency Updated: May 6, 2012 at 6pm %>
110
130
  # <%= some_helper_method(person) %>
111
131
  #
112
- # Now all you'll have to do is change that timestamp when the helper method changes.
113
- def cache(name = {}, options = nil, &block)
132
+ # Now all you have to do is change that timestamp when the helper method changes.
133
+ #
134
+ # === Collection Caching
135
+ #
136
+ # When rendering a collection of objects that each use the same partial, a <tt>:cached</tt>
137
+ # option can be passed.
138
+ #
139
+ # For collections rendered such:
140
+ #
141
+ # <%= render partial: 'projects/project', collection: @projects, cached: true %>
142
+ #
143
+ # The <tt>cached: true</tt> will make Action View's rendering read several templates
144
+ # from cache at once instead of one call per template.
145
+ #
146
+ # Templates in the collection not already cached are written to cache.
147
+ #
148
+ # Works great alongside individual template fragment caching.
149
+ # For instance if the template the collection renders is cached like:
150
+ #
151
+ # # projects/_project.html.erb
152
+ # <% cache project do %>
153
+ # <%# ... %>
154
+ # <% end %>
155
+ #
156
+ # Any collection renders will find those cached templates when attempting
157
+ # to read multiple templates at once.
158
+ #
159
+ # If your collection cache depends on multiple sources (try to avoid this to keep things simple),
160
+ # you can name all these dependencies as part of a block that returns an array:
161
+ #
162
+ # <%= render partial: 'projects/project', collection: @projects, cached: -> project { [ project, current_user ] } %>
163
+ #
164
+ # This will include both records as part of the cache key and updating either of them will
165
+ # expire the cache.
166
+ def cache(name = {}, options = {}, &block)
114
167
  if controller.respond_to?(:perform_caching) && controller.perform_caching
115
- safe_concat(fragment_for(cache_fragment_name(name, options), options, &block))
168
+ name_options = options.slice(:skip_digest)
169
+ safe_concat(fragment_for(cache_fragment_name(name, **name_options), options, &block))
116
170
  else
117
171
  yield
118
172
  end
@@ -122,11 +176,11 @@ module ActionView
122
176
 
123
177
  # Cache fragments of a view if +condition+ is true
124
178
  #
125
- # <%= cache_if admin?, project do %>
179
+ # <% cache_if admin?, project do %>
126
180
  # <b>All the topics on this project</b>
127
181
  # <%= render project.topics %>
128
182
  # <% end %>
129
- def cache_if(condition, name = {}, options = nil, &block)
183
+ def cache_if(condition, name = {}, options = {}, &block)
130
184
  if condition
131
185
  cache(name, options, &block)
132
186
  else
@@ -138,54 +192,64 @@ module ActionView
138
192
 
139
193
  # Cache fragments of a view unless +condition+ is true
140
194
  #
141
- # <%= cache_unless admin?, project do %>
195
+ # <% cache_unless admin?, project do %>
142
196
  # <b>All the topics on this project</b>
143
197
  # <%= render project.topics %>
144
198
  # <% end %>
145
- def cache_unless(condition, name = {}, options = nil, &block)
199
+ def cache_unless(condition, name = {}, options = {}, &block)
146
200
  cache_if !condition, name, options, &block
147
201
  end
148
202
 
149
203
  # 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
204
+ # call. By supplying <tt>skip_digest: true</tt> to cache, the digestion of cache
151
205
  # fragments can be manually bypassed. This is useful when cache fragments
152
206
  # cannot be manually expired unless you know the exact key which is the
153
207
  # case when using memcached.
154
- def cache_fragment_name(name = {}, options = nil)
155
- skip_digest = options && options[:skip_digest]
156
-
208
+ def cache_fragment_name(name = {}, skip_digest: nil, digest_path: nil)
157
209
  if skip_digest
158
210
  name
159
211
  else
160
- fragment_name_with_digest(name)
212
+ fragment_name_with_digest(name, digest_path)
161
213
  end
162
214
  end
163
215
 
164
- private
216
+ def digest_path_from_template(template) # :nodoc:
217
+ digest = Digestor.digest(name: template.virtual_path, format: template.format, finder: lookup_context, dependencies: view_cache_dependencies)
165
218
 
166
- def fragment_name_with_digest(name) #:nodoc:
167
- if @virtual_path
168
- names = Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name)
169
- digest = Digestor.digest name: @virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
219
+ if digest.present?
220
+ "#{template.virtual_path}:#{digest}"
221
+ else
222
+ template.virtual_path
223
+ end
224
+ end
170
225
 
171
- [ *names, digest ]
226
+ private
227
+ def fragment_name_with_digest(name, digest_path)
228
+ name = controller.url_for(name).split("://").last if name.is_a?(Hash)
229
+
230
+ if @current_template&.virtual_path || digest_path
231
+ digest_path ||= digest_path_from_template(@current_template)
232
+ [ digest_path, name ]
172
233
  else
173
234
  name
174
235
  end
175
236
  end
176
237
 
177
- # TODO: Create an object that has caching read/write on it
178
- def fragment_for(name = {}, options = nil, &block) #:nodoc:
179
- read_fragment_for(name, options) || write_fragment_for(name, options, &block)
238
+ def fragment_for(name = {}, options = nil, &block)
239
+ if content = read_fragment_for(name, options)
240
+ @view_renderer.cache_hits[@current_template&.virtual_path] = :hit if defined?(@view_renderer)
241
+ content
242
+ else
243
+ @view_renderer.cache_hits[@current_template&.virtual_path] = :miss if defined?(@view_renderer)
244
+ write_fragment_for(name, options, &block)
245
+ end
180
246
  end
181
247
 
182
- def read_fragment_for(name, options) #:nodoc:
248
+ def read_fragment_for(name, options)
183
249
  controller.read_fragment(name, options)
184
250
  end
185
251
 
186
- def write_fragment_for(name, options) #:nodoc:
187
- # VIEW TODO: Make #capture usable outside of ERB
188
- # This dance is needed because Builder can't use capture
252
+ def write_fragment_for(name, options)
189
253
  pos = output_buffer.length
190
254
  yield
191
255
  output_safe = output_buffer.html_safe?
@@ -1,16 +1,18 @@
1
- require 'active_support/core_ext/string/output_safety'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/output_safety"
2
4
 
3
5
  module ActionView
4
6
  # = Action View Capture Helper
5
- module Helpers
7
+ module Helpers #:nodoc:
6
8
  # CaptureHelper exposes methods to let you extract generated markup which
7
9
  # can be used in other parts of a template or layout file.
8
10
  #
9
11
  # It provides a method to capture blocks into variables through capture and
10
- # a way to capture a block of markup for use in a layout through content_for.
12
+ # a way to capture a block of markup for use in a layout through {content_for}[rdoc-ref:ActionView::Helpers::CaptureHelper#content_for].
11
13
  module CaptureHelper
12
- # The capture method allows you to extract part of a template into a
13
- # variable. You can then use this variable anywhere in your templates or layout.
14
+ # The capture method extracts part of a template as a String object.
15
+ # You can then use this object anywhere in your templates, layout, or helpers.
14
16
  #
15
17
  # The capture method can be used in ERB templates...
16
18
  #
@@ -31,17 +33,22 @@ module ActionView
31
33
  # <head><title><%= @greeting %></title></head>
32
34
  # <body>
33
35
  # <b><%= @greeting %></b>
34
- # </body></html>
36
+ # </body>
37
+ # </html>
38
+ #
39
+ # The return of capture is the string generated by the block. For Example:
40
+ #
41
+ # @greeting # => "Welcome to my shiny new web page! The date and time is 2018-09-06 11:09:16 -0500"
35
42
  #
36
43
  def capture(*args)
37
44
  value = nil
38
45
  buffer = with_output_buffer { value = yield(*args) }
39
- if string = buffer.presence || value and string.is_a?(String)
46
+ if (string = buffer.presence || value) && string.is_a?(String)
40
47
  ERB::Util.html_escape string
41
48
  end
42
49
  end
43
50
 
44
- # Calling content_for stores a block of markup in an identifier for later use.
51
+ # Calling <tt>content_for</tt> stores a block of markup in an identifier for later use.
45
52
  # In order to access this stored content in other templates, helper modules
46
53
  # or the layout, you would pass the identifier as an argument to <tt>content_for</tt>.
47
54
  #
@@ -107,14 +114,14 @@ module ActionView
107
114
  # That will place +script+ tags for your default set of JavaScript files on the page;
108
115
  # this technique is useful if you'll only be using these scripts in a few views.
109
116
  #
110
- # Note that content_for concatenates (default) the blocks it is given for a particular
117
+ # Note that <tt>content_for</tt> concatenates (default) the blocks it is given for a particular
111
118
  # identifier in order. For example:
112
119
  #
113
120
  # <% content_for :navigation do %>
114
121
  # <li><%= link_to 'Home', action: 'index' %></li>
115
122
  # <% end %>
116
123
  #
117
- # And in other place:
124
+ # And in another place:
118
125
  #
119
126
  # <% content_for :navigation do %>
120
127
  # <li><%= link_to 'Login', action: 'login' %></li>
@@ -124,7 +131,7 @@ module ActionView
124
131
  #
125
132
  # <ul><%= content_for :navigation %></ul>
126
133
  #
127
- # If the flush parameter is true content_for replaces the blocks it is given. For example:
134
+ # If the flush parameter is +true+ <tt>content_for</tt> replaces the blocks it is given. For example:
128
135
  #
129
136
  # <% content_for :navigation do %>
130
137
  # <li><%= link_to 'Home', action: 'index' %></li>
@@ -144,7 +151,7 @@ module ActionView
144
151
  #
145
152
  # <% content_for :script, javascript_include_tag(:defaults) %>
146
153
  #
147
- # WARNING: content_for is ignored in caches. So you shouldn't use it for elements that will be fragment cached.
154
+ # WARNING: <tt>content_for</tt> is ignored in caches. So you shouldn't use it for elements that will be fragment cached.
148
155
  def content_for(name, content = nil, options = {}, &block)
149
156
  if content || block_given?
150
157
  if block_given?
@@ -171,7 +178,7 @@ module ActionView
171
178
  result unless content
172
179
  end
173
180
 
174
- # content_for? checks whether any content has been captured yet using `content_for`.
181
+ # <tt>content_for?</tt> checks whether any content has been captured yet using <tt>content_for</tt>.
175
182
  # Useful to render parts of your layout differently based on what is in your views.
176
183
  #
177
184
  # <%# This is the layout %>
@@ -204,15 +211,6 @@ module ActionView
204
211
  ensure
205
212
  self.output_buffer = old_buffer
206
213
  end
207
-
208
- # Add the output buffer to the response body and start a new one.
209
- def flush_output_buffer #:nodoc:
210
- if output_buffer && !output_buffer.empty?
211
- response.stream.write output_buffer
212
- self.output_buffer = output_buffer.respond_to?(:clone_empty) ? output_buffer.clone_empty : output_buffer[0, 0]
213
- nil
214
- end
215
- end
216
214
  end
217
215
  end
218
216
  end