actionview 5.0.0.beta2 → 5.0.0.beta3

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5ed6df64a978ea67512f762566f43c03720d81f6
4
- data.tar.gz: c434b674c12d95e5a19b5afc49013000db74122d
3
+ metadata.gz: 10ff10399f3daad3dd51ca76efc01f2ba03ac135
4
+ data.tar.gz: d9d94bc8fb7bdc057093ff1ad2a6d1df9e7bf544
5
5
  SHA512:
6
- metadata.gz: e1a742c7151ec8d4699923321e139ff293a19202e595f4748b465791dbacc032bc602cc572b36eb3fdebb01fa580ea673dd48ffb9144e8f341a349a9e9d99675
7
- data.tar.gz: 85487efbb9f882f2cf8f0bb7cca78fc95e4c1171ef502ecabd7c20ccf85c430c1457afeb8083a1dbbb9d12e489cc37eada7e004e5ecd148c674526bbb107c672
6
+ metadata.gz: 9d8fec2801766ff1fc3052e57be09a240280a1b28f3327c6844a312f828a8216e369c50c823fbf8d4608e5e521231cc4297650618c416047e02134c3dcf4257d
7
+ data.tar.gz: 749328a74e11735391283ca4741a6c1d1a35454726bc903fc29ffa354574ad84a61b13ea4ba079513e35cd3e7b3d30c190e408b08abf8c9f4b3c65d76bade99e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,34 @@
1
+ ## Rails 5.0.0.beta3 (February 24, 2016) ##
2
+
3
+ * Collection rendering can cache and fetch multiple partials at once.
4
+
5
+ Collections rendered as:
6
+
7
+ ```ruby
8
+ <%= render partial: 'notifications/notification', collection: @notifications, as: :notification, cached: true %>
9
+ ```
10
+
11
+ will read several partials from cache at once. The templates in the collection
12
+ that haven't been cached already will automatically be written to cache. Works
13
+ great alongside individual template fragment caching. For instance if the
14
+ template the collection renders is cached like:
15
+
16
+ ```ruby
17
+ # notifications/_notification.html.erb
18
+ <% cache notification do %>
19
+ <%# ... %>
20
+ <% end %>
21
+ ```
22
+
23
+ Then any collection renders shares that cache when attempting to read multiple
24
+ ones at once.
25
+
26
+ *Kasper Timm Hansen*
27
+
28
+ * Add support for nested hashes/arrays to `:params` option of `button_to` helper.
29
+
30
+ *James Coleman*
31
+
1
32
  ## Rails 5.0.0.beta2 (February 01, 2016) ##
2
33
 
3
34
  * Fix stripping the digest from the automatically generated img tag alt
@@ -68,7 +99,7 @@
68
99
 
69
100
  *Vasiliy Ermolovich*
70
101
 
71
- * Add a `hidden_field` on the `collection_radio_buttons` to avoid raising a error
102
+ * Add a `hidden_field` on the `collection_radio_buttons` to avoid raising an error
72
103
  when the only input on the form is the `collection_radio_buttons`.
73
104
 
74
105
  *Mauro George*
@@ -193,26 +224,6 @@
193
224
 
194
225
  *Ulisses Almeida*
195
226
 
196
- * Collection rendering automatically caches and fetches multiple partials.
197
-
198
- Collections rendered as:
199
-
200
- ```ruby
201
- <%= render @notifications %>
202
- <%= render partial: 'notifications/notification', collection: @notifications, as: :notification %>
203
- ```
204
-
205
- will now read several partials from cache at once, if the template starts with a cache call:
206
-
207
- ```ruby
208
- # notifications/_notification.html.erb
209
- <% cache notification do %>
210
- <%# ... %>
211
- <% end %>
212
- ```
213
-
214
- *Kasper Timm Hansen*
215
-
216
227
  * Fixed a dependency tracker bug that caused template dependencies not
217
228
  count layouts as dependencies for partials.
218
229
 
@@ -7,18 +7,20 @@ module ActionView
7
7
 
8
8
  def self.find_dependencies(name, template, view_paths = nil)
9
9
  tracker = @trackers[template.handler]
10
- return [] unless tracker.present?
10
+ return [] unless tracker
11
11
 
12
- if tracker.respond_to?(:supports_view_paths?) && tracker.supports_view_paths?
13
- tracker.call(name, template, view_paths)
14
- else
15
- tracker.call(name, template)
16
- end
12
+ tracker.call(name, template, view_paths)
17
13
  end
18
14
 
19
15
  def self.register_tracker(extension, tracker)
20
16
  handler = Template.handler_for_extension(extension)
21
- @trackers[handler] = tracker
17
+ if tracker.respond_to?(:supports_view_paths?)
18
+ @trackers[handler] = tracker
19
+ else
20
+ @trackers[handler] = lambda { |name, template, _|
21
+ tracker.call(name, template)
22
+ }
23
+ end
22
24
  end
23
25
 
24
26
  def self.remove_tracker(handler)
@@ -151,11 +153,11 @@ module ActionView
151
153
  def resolve_directories(wildcard_dependencies)
152
154
  return [] unless @view_paths
153
155
 
154
- wildcard_dependencies.each_with_object([]) do |query, templates|
155
- @view_paths.find_all_with_query(query).each do |template|
156
- templates << "#{File.dirname(query)}/#{File.basename(template).split('.').first}"
156
+ wildcard_dependencies.flat_map { |query, templates|
157
+ @view_paths.find_all_with_query(query).map do |template|
158
+ "#{File.dirname(query)}/#{File.basename(template).split('.').first}"
157
159
  end
158
- end
160
+ }.sort
159
161
  end
160
162
 
161
163
  def explicit_dependencies
@@ -4,13 +4,11 @@ require 'monitor'
4
4
 
5
5
  module ActionView
6
6
  class Digestor
7
- cattr_reader(:cache)
8
- @@cache = Concurrent::Map.new
9
7
  @@digest_monitor = Monitor.new
10
8
 
11
9
  class PerRequestDigestCacheExpiry < Struct.new(:app) # :nodoc:
12
10
  def call(env)
13
- ActionView::Digestor.cache.clear
11
+ ActionView::LookupContext::DetailsKey.clear
14
12
  app.call(env)
15
13
  end
16
14
  end
@@ -22,72 +20,77 @@ module ActionView
22
20
  # * <tt>finder</tt> - An instance of <tt>ActionView::LookupContext</tt>
23
21
  # * <tt>dependencies</tt> - An array of dependent views
24
22
  # * <tt>partial</tt> - Specifies whether the template is a partial
25
- def digest(options)
26
- options.assert_valid_keys(:name, :finder, :dependencies, :partial)
23
+ def digest(name:, finder:, **options)
24
+ options.assert_valid_keys(:dependencies, :partial)
27
25
 
28
- cache_key = ([ options[:name], options[:finder].details_key.hash ].compact + Array.wrap(options[:dependencies])).join('.')
26
+ cache_key = ([ name ].compact + Array.wrap(options[:dependencies])).join('.')
29
27
 
30
28
  # this is a correctly done double-checked locking idiom
31
29
  # (Concurrent::Map's lookups have volatile semantics)
32
- @@cache[cache_key] || @@digest_monitor.synchronize do
33
- @@cache.fetch(cache_key) do # re-check under lock
34
- compute_and_store_digest(cache_key, options)
30
+ finder.digest_cache[cache_key] || @@digest_monitor.synchronize do
31
+ finder.digest_cache.fetch(cache_key) do # re-check under lock
32
+ compute_and_store_digest(cache_key, name, finder, options)
35
33
  end
36
34
  end
37
35
  end
38
36
 
39
37
  private
40
- def compute_and_store_digest(cache_key, options) # called under @@digest_monitor lock
41
- klass = if options[:partial] || options[:name].include?("/_")
38
+ def compute_and_store_digest(cache_key, name, finder, options) # called under @@digest_monitor lock
39
+ klass = if options[:partial] || name.include?("/_")
42
40
  # Prevent re-entry or else recursive templates will blow the stack.
43
41
  # There is no need to worry about other threads seeing the +false+ value,
44
42
  # as they will then have to wait for this thread to let go of the @@digest_monitor lock.
45
- pre_stored = @@cache.put_if_absent(cache_key, false).nil? # put_if_absent returns nil on insertion
43
+ pre_stored = finder.digest_cache.put_if_absent(cache_key, false).nil? # put_if_absent returns nil on insertion
46
44
  PartialDigestor
47
45
  else
48
46
  Digestor
49
47
  end
50
48
 
51
- @@cache[cache_key] = stored_digest = klass.new(options).digest
49
+ finder.digest_cache[cache_key] = stored_digest = klass.new(name, finder, options).digest
52
50
  ensure
53
51
  # something went wrong or ActionView::Resolver.caching? is false, make sure not to corrupt the @@cache
54
- @@cache.delete_pair(cache_key, false) if pre_stored && !stored_digest
52
+ finder.digest_cache.delete_pair(cache_key, false) if pre_stored && !stored_digest
55
53
  end
56
54
  end
57
55
 
58
56
  attr_reader :name, :finder, :options
59
57
 
60
- def initialize(options)
61
- @name, @finder = options.values_at(:name, :finder)
62
- @options = options.except(:name, :finder)
58
+ def initialize(name, finder, options = {})
59
+ @name, @finder = name, finder
60
+ @options = options
63
61
  end
64
62
 
65
63
  def digest
66
64
  Digest::MD5.hexdigest("#{source}-#{dependency_digest}").tap do |digest|
67
- logger.try :debug, " Cache digest for #{template.inspect}: #{digest}"
65
+ logger.debug " Cache digest for #{template.inspect}: #{digest}"
68
66
  end
69
67
  rescue ActionView::MissingTemplate
70
- logger.try :error, " Couldn't find template for digesting: #{name}"
68
+ logger.error " Couldn't find template for digesting: #{name}"
71
69
  ''
72
70
  end
73
71
 
74
72
  def dependencies
75
73
  DependencyTracker.find_dependencies(name, template, finder.view_paths)
76
74
  rescue ActionView::MissingTemplate
77
- logger.try :error, " '#{name}' file doesn't exist, so no dependencies"
75
+ logger.error " '#{name}' file doesn't exist, so no dependencies"
78
76
  []
79
77
  end
80
78
 
81
79
  def nested_dependencies
82
80
  dependencies.collect do |dependency|
83
- dependencies = PartialDigestor.new(name: dependency, finder: finder).nested_dependencies
81
+ dependencies = PartialDigestor.new(dependency, finder).nested_dependencies
84
82
  dependencies.any? ? { dependency => dependencies } : dependency
85
83
  end
86
84
  end
87
85
 
88
86
  private
87
+ class NullLogger
88
+ def self.debug(_); end
89
+ def self.error(_); end
90
+ end
91
+
89
92
  def logger
90
- ActionView::Base.logger
93
+ ActionView::Base.logger || NullLogger
91
94
  end
92
95
 
93
96
  def logical_name
@@ -8,7 +8,7 @@ module ActionView
8
8
  MAJOR = 5
9
9
  MINOR = 0
10
10
  TINY = 0
11
- PRE = "beta2"
11
+ PRE = "beta3"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -126,44 +126,29 @@ module ActionView
126
126
  #
127
127
  # Now all you have to do is change that timestamp when the helper method changes.
128
128
  #
129
- # === Automatic Collection Caching
129
+ # === Collection Caching
130
130
  #
131
- # When rendering collections such as:
131
+ # When rendering a collection of objects that each use the same partial, a `cached`
132
+ # option can be passed.
133
+ # For collections rendered such:
132
134
  #
133
- # <%= render @notifications %>
134
- # <%= render partial: 'notifications/notification', collection: @notifications %>
135
+ # <%= render partial: 'notifications/notification', collection: @notifications, cached: true %>
135
136
  #
136
- # If the notifications/_notification partial starts with a cache call as:
137
+ # The `cached: true` will make Action View's rendering read several templates
138
+ # from cache at once instead of one call per template.
137
139
  #
138
- # <% cache notification do %>
139
- # <%= notification.name %>
140
- # <% end %>
141
- #
142
- # The collection can then automatically use any cached renders for that
143
- # template by reading them at once instead of one by one.
144
- #
145
- # See ActionView::Template::Handlers::ERB.resource_cache_call_pattern for
146
- # more information on what cache calls make a template eligible for this
147
- # collection caching.
148
- #
149
- # The automatic cache multi read can be turned off like so:
140
+ # Templates in the collection not already cached are written to cache.
150
141
  #
151
- # <%= render @notifications, cache: false %>
142
+ # Works great alongside individual template fragment caching.
143
+ # For instance if the template the collection renders is cached like:
152
144
  #
153
- # === Explicit Collection Caching
154
- #
155
- # If the partial template doesn't start with a clean cache call as
156
- # mentioned above, you can still benefit from collection caching by
157
- # adding a special comment format anywhere in the template, like:
158
- #
159
- # <%# Template Collection: notification %>
160
- # <% my_helper_that_calls_cache(some_arg, notification) do %>
161
- # <%= notification.name %>
145
+ # # notifications/_notification.html.erb
146
+ # <% cache notification do %>
147
+ # <%# ... %>
162
148
  # <% end %>
163
149
  #
164
- # The pattern used to match these is <tt>/# Template Collection: (\S+)/</tt>,
165
- # so it's important that you type it out just so.
166
- # You can only declare one collection in a partial template file.
150
+ # Any collection renders will find those cached templates when attempting
151
+ # to read multiple templates at once.
167
152
  def cache(name = {}, options = {}, &block)
168
153
  if controller.respond_to?(:perform_caching) && controller.perform_caching
169
154
  name_options = options.slice(:skip_digest, :virtual_path)
@@ -862,13 +862,13 @@ module ActionView
862
862
 
863
863
  def extra_tags_for_form(html_options)
864
864
  authenticity_token = html_options.delete("authenticity_token")
865
- method = html_options.delete("method").to_s
865
+ method = html_options.delete("method").to_s.downcase
866
866
 
867
867
  method_tag = case method
868
- when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
868
+ when 'get'
869
869
  html_options["method"] = "get"
870
870
  ''
871
- when /^post$/i, "", nil
871
+ when 'post', ''
872
872
  html_options["method"] = "post"
873
873
  token_tag(authenticity_token, form_options: {
874
874
  action: html_options["action"],
@@ -154,6 +154,7 @@ module ActionView
154
154
  options.each_pair do |key, value|
155
155
  if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
156
156
  value.each_pair do |k, v|
157
+ next if v.nil?
157
158
  output << sep
158
159
  output << prefix_tag_option(key, k, v, escape)
159
160
  end
@@ -312,7 +312,8 @@ module ActionView
312
312
  form_options[:'data-remote'] = true if remote
313
313
 
314
314
  request_token_tag = if form_method == 'post'
315
- token_tag(nil, form_options: form_options)
315
+ request_method = method.empty? ? 'post' : method
316
+ token_tag(nil, form_options: { action: url, method: request_method })
316
317
  else
317
318
  ''
318
319
  end
@@ -329,8 +330,8 @@ module ActionView
329
330
 
330
331
  inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
331
332
  if params
332
- params.each do |param_name, value|
333
- inner_tags.safe_concat tag(:input, type: "hidden", name: param_name, value: value.to_param)
333
+ to_form_params(params).each do |param|
334
+ inner_tags.safe_concat tag(:input, type: "hidden", name: param[:name], value: param[:value])
334
335
  end
335
336
  end
336
337
  content_tag('form', inner_tags, form_options)
@@ -595,6 +596,42 @@ module ActionView
595
596
  def method_tag(method)
596
597
  tag('input', type: 'hidden', name: '_method', value: method.to_s)
597
598
  end
599
+
600
+ # Returns an array of hashes each containing :name and :value keys
601
+ # suitable for use as the names and values of form input fields:
602
+ #
603
+ # to_form_params(name: 'David', nationality: 'Danish')
604
+ # # => [{name: :name, value: 'David'}, {name: 'nationality', value: 'Danish'}]
605
+ #
606
+ # to_form_params(country: {name: 'Denmark'})
607
+ # # => [{name: 'country[name]', value: 'Denmark'}]
608
+ #
609
+ # to_form_params(countries: ['Denmark', 'Sweden']})
610
+ # # => [{name: 'countries[]', value: 'Denmark'}, {name: 'countries[]', value: 'Sweden'}]
611
+ #
612
+ # An optional namespace can be passed to enclose key names:
613
+ #
614
+ # to_form_params({ name: 'Denmark' }, 'country')
615
+ # # => [{name: 'country[name]', value: 'Denmark'}]
616
+ def to_form_params(attribute, namespace = nil) # :nodoc:
617
+ params = []
618
+ case attribute
619
+ when Hash
620
+ attribute.each do |key, value|
621
+ prefix = namespace ? "#{namespace}[#{key}]" : key
622
+ params.push(*to_form_params(value, prefix))
623
+ end
624
+ when Array
625
+ array_prefix = "#{namespace}[]"
626
+ attribute.each do |value|
627
+ params.push(*to_form_params(value, array_prefix))
628
+ end
629
+ else
630
+ params << { name: namespace, value: attribute.to_param }
631
+ end
632
+
633
+ params.sort_by { |pair| pair[:name] }
634
+ end
598
635
  end
599
636
  end
600
637
  end
@@ -20,7 +20,15 @@ module ActionView
20
20
  end
21
21
  end
22
22
  alias :render_partial :render_template
23
- alias :render_collection :render_template
23
+
24
+ def render_collection(event)
25
+ identifier = event.payload[:identifier] || 'templates'
26
+
27
+ info do
28
+ " Rendered collection of #{from_rails_root(identifier)}" \
29
+ " #{render_count(event.payload)} (#{event.duration.round(1)}ms)"
30
+ end
31
+ end
24
32
 
25
33
  def logger
26
34
  ActionView::Base.logger
@@ -38,6 +46,14 @@ module ActionView
38
46
  def rails_root
39
47
  @root ||= "#{Rails.root}/"
40
48
  end
49
+
50
+ def render_count(payload)
51
+ if payload[:cache_hits]
52
+ "[#{payload[:cache_hits]} / #{payload[:count]} cache hits]"
53
+ else
54
+ "[#{payload[:count]} times]"
55
+ end
56
+ end
41
57
  end
42
58
  end
43
59
 
@@ -22,7 +22,7 @@ module ActionView
22
22
 
23
23
  def self.register_detail(name, &block)
24
24
  self.registered_details << name
25
- initialize = registered_details.map { |n| "@details[:#{n}] = details[:#{n}] || default_#{n}" }
25
+ Accessors::DEFAULT_PROCS[name] = block
26
26
 
27
27
  Accessors.send :define_method, :"default_#{name}", &block
28
28
  Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
@@ -34,16 +34,12 @@ module ActionView
34
34
  value = value.present? ? Array(value) : default_#{name}
35
35
  _set_detail(:#{name}, value) if value != @details[:#{name}]
36
36
  end
37
-
38
- remove_possible_method :initialize_details
39
- def initialize_details(details)
40
- #{initialize.join("\n")}
41
- end
42
37
  METHOD
43
38
  end
44
39
 
45
40
  # Holds accessors for the registered details.
46
41
  module Accessors #:nodoc:
42
+ DEFAULT_PROCS = {}
47
43
  end
48
44
 
49
45
  register_detail(:locale) do
@@ -59,9 +55,7 @@ module ActionView
59
55
 
60
56
  class DetailsKey #:nodoc:
61
57
  alias :eql? :equal?
62
- alias :object_hash :hash
63
58
 
64
- attr_reader :hash
65
59
  @details_keys = Concurrent::Map.new
66
60
 
67
61
  def self.get(details)
@@ -76,8 +70,16 @@ module ActionView
76
70
  @details_keys.clear
77
71
  end
78
72
 
73
+ def self.empty?; @details_keys.empty?; end
74
+
75
+ def self.digest_caches
76
+ @details_keys.values.map(&:digest_cache)
77
+ end
78
+
79
+ attr_reader :digest_cache
80
+
79
81
  def initialize
80
- @hash = object_hash
82
+ @digest_cache = Concurrent::Map.new
81
83
  end
82
84
  end
83
85
 
@@ -195,15 +197,27 @@ module ActionView
195
197
  include ViewPaths
196
198
 
197
199
  def initialize(view_paths, details = {}, prefixes = [])
198
- @details, @details_key = {}, nil
200
+ @details_key = nil
199
201
  @cache = true
200
202
  @prefixes = prefixes
201
203
  @rendered_format = nil
202
204
 
205
+ @details = initialize_details({}, details)
203
206
  self.view_paths = view_paths
204
- initialize_details(details)
205
207
  end
206
208
 
209
+ def digest_cache
210
+ details_key.digest_cache
211
+ end
212
+
213
+ def initialize_details(target, details)
214
+ registered_details.each do |k|
215
+ target[k] = details[k] || Accessors::DEFAULT_PROCS[k].call
216
+ end
217
+ target
218
+ end
219
+ private :initialize_details
220
+
207
221
  # Override formats= to expand ["*/*"] values and automatically
208
222
  # add :html as fallback to :js.
209
223
  def formats=(values)
@@ -35,8 +35,12 @@ module ActionView
35
35
  end
36
36
  end
37
37
 
38
- def instrument(name, options={})
39
- ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
38
+ def instrument(name, **options)
39
+ options[:identifier] ||= (@template && @template.identifier) || @path
40
+
41
+ ActiveSupport::Notifications.instrument("render_#{name}.action_view", options) do |payload|
42
+ yield payload
43
+ end
40
44
  end
41
45
 
42
46
  def prepend_formats(formats)
@@ -294,7 +294,7 @@ module ActionView
294
294
 
295
295
  def render(context, options, block)
296
296
  setup(context, options, block)
297
- identifier = (@template = find_partial) ? @template.identifier : @path
297
+ @template = find_partial
298
298
 
299
299
  @lookup_context.rendered_format ||= begin
300
300
  if @template && @template.formats.present?
@@ -305,11 +305,9 @@ module ActionView
305
305
  end
306
306
 
307
307
  if @collection
308
- instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do
309
- render_collection
310
- end
308
+ render_collection
311
309
  else
312
- instrument(:partial, :identifier => identifier) do
310
+ instrument(:partial) do
313
311
  render_partial
314
312
  end
315
313
  end
@@ -318,15 +316,17 @@ module ActionView
318
316
  private
319
317
 
320
318
  def render_collection
321
- return nil if @collection.blank?
319
+ instrument(:collection, count: @collection.size) do |payload|
320
+ return nil if @collection.blank?
322
321
 
323
- if @options.key?(:spacer_template)
324
- spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals)
325
- end
322
+ if @options.key?(:spacer_template)
323
+ spacer = find_template(@options[:spacer_template], @locals.keys).render(@view, @locals)
324
+ end
326
325
 
327
- cache_collection_render do
328
- @template ? collection_with_template : collection_without_template
329
- end.join(spacer).html_safe
326
+ cache_collection_render(payload) do
327
+ @template ? collection_with_template : collection_without_template
328
+ end.join(spacer).html_safe
329
+ end
330
330
  end
331
331
 
332
332
  def render_partial
@@ -1,5 +1,3 @@
1
- require 'active_support/core_ext/object/try'
2
-
3
1
  module ActionView
4
2
  module CollectionCaching # :nodoc:
5
3
  extend ActiveSupport::Concern
@@ -11,42 +9,25 @@ module ActionView
11
9
  end
12
10
 
13
11
  private
14
- def cache_collection_render
15
- return yield unless cache_collection?
12
+ def cache_collection_render(instrumentation_payload)
13
+ return yield unless @options[:cached]
16
14
 
17
15
  keyed_collection = collection_by_cache_keys
18
- partial_cache = collection_cache.read_multi(*keyed_collection.keys)
16
+ cached_partials = collection_cache.read_multi(*keyed_collection.keys)
17
+ instrumentation_payload[:cache_hits] = cached_partials.size
19
18
 
20
- @collection = keyed_collection.reject { |key, _| partial_cache.key?(key) }.values
21
- rendered_partials = @collection.any? ? yield.dup : []
19
+ @collection = keyed_collection.reject { |key, _| cached_partials.key?(key) }.values
20
+ rendered_partials = @collection.empty? ? [] : yield
22
21
 
23
- fetch_or_cache_partial(partial_cache, order_by: keyed_collection.each_key) do
24
- rendered_partials.shift
22
+ index = 0
23
+ fetch_or_cache_partial(cached_partials, order_by: keyed_collection.each_key) do
24
+ rendered_partials[index].tap { index += 1 }
25
25
  end
26
26
  end
27
27
 
28
- def cache_collection?
29
- @options.fetch(:cache, automatic_cache_eligible?)
30
- end
31
-
32
- def automatic_cache_eligible?
33
- single_template_render? && !callable_cache_key? &&
34
- @template.eligible_for_collection_caching?(as: @options[:as])
35
- end
36
-
37
- def single_template_render?
38
- @template # Template is only set when a collection renders one template.
39
- end
40
-
41
- def callable_cache_key?
42
- @options[:cache].respond_to?(:call)
43
- end
44
-
45
28
  def collection_by_cache_keys
46
- seed = callable_cache_key? ? @options[:cache] : ->(i) { i }
47
-
48
29
  @collection.each_with_object({}) do |item, hash|
49
- hash[expanded_cache_key(seed.call(item))] = item
30
+ hash[expanded_cache_key(item)] = item
50
31
  end
51
32
  end
52
33
 
@@ -56,12 +37,10 @@ module ActionView
56
37
  end
57
38
 
58
39
  def fetch_or_cache_partial(cached_partials, order_by:)
59
- cache_options = @options[:cache_options] || @locals[:cache_options] || {}
60
-
61
- order_by.map do |key|
62
- cached_partials.fetch(key) do
40
+ order_by.map do |cache_key|
41
+ cached_partials.fetch(cache_key) do
63
42
  yield.tap do |rendered_partial|
64
- collection_cache.write(key, rendered_partial, cache_options)
43
+ collection_cache.write(cache_key, rendered_partial)
65
44
  end
66
45
  end
67
46
  end
@@ -2,13 +2,13 @@ namespace :cache_digests do
2
2
  desc 'Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)'
3
3
  task :nested_dependencies => :environment do
4
4
  abort 'You must provide TEMPLATE for the task to run' unless ENV['TEMPLATE'].present?
5
- puts JSON.pretty_generate ActionView::Digestor.new(name: CacheDigests.template_name, finder: CacheDigests.finder).nested_dependencies
5
+ puts JSON.pretty_generate ActionView::Digestor.new(CacheDigests.template_name, CacheDigests.finder).nested_dependencies
6
6
  end
7
7
 
8
8
  desc 'Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_comment.html)'
9
9
  task :dependencies => :environment do
10
10
  abort 'You must provide TEMPLATE for the task to run' unless ENV['TEMPLATE'].present?
11
- puts JSON.pretty_generate ActionView::Digestor.new(name: CacheDigests.template_name, finder: CacheDigests.finder).dependencies
11
+ puts JSON.pretty_generate ActionView::Digestor.new(CacheDigests.template_name, CacheDigests.finder).dependencies
12
12
  end
13
13
 
14
14
  class CacheDigests
@@ -130,7 +130,6 @@ module ActionView
130
130
  @source = source
131
131
  @identifier = identifier
132
132
  @handler = handler
133
- @cache_name = extract_resource_cache_name
134
133
  @compiled = false
135
134
  @original_encoding = nil
136
135
  @locals = details[:locals] || []
@@ -166,10 +165,6 @@ module ActionView
166
165
  @type ||= Types[@formats.first] if @formats.first
167
166
  end
168
167
 
169
- def eligible_for_collection_caching?(as: nil)
170
- @cache_name == (as || inferred_cache_name).to_s
171
- end
172
-
173
168
  # Receives a view object and return a template similar to self by using @virtual_path.
174
169
  #
175
170
  # This method is useful if you have a template object but it does not contain its source
@@ -355,23 +350,5 @@ module ActionView
355
350
  ActiveSupport::Notifications.instrument("#{action}.action_view".freeze, payload, &block)
356
351
  end
357
352
  end
358
-
359
- EXPLICIT_COLLECTION = /# Template Collection: (?<resource_name>\w+)/
360
-
361
- def extract_resource_cache_name
362
- if match = @source.match(EXPLICIT_COLLECTION) || resource_cache_call_match
363
- match[:resource_name]
364
- end
365
- end
366
-
367
- def resource_cache_call_match
368
- if @handler.respond_to?(:resource_cache_call_pattern)
369
- @source.match(@handler.resource_cache_call_pattern)
370
- end
371
- end
372
-
373
- def inferred_cache_name
374
- @inferred_cache_name ||= @virtual_path.split('/'.freeze).last.sub('_'.freeze, ''.freeze)
375
- end
376
353
  end
377
354
  end
@@ -59,6 +59,9 @@ module ActionView
59
59
  class Error < ActionViewError #:nodoc:
60
60
  SOURCE_CODE_RADIUS = 3
61
61
 
62
+ # Override to prevent #cause resetting during re-raise.
63
+ attr_reader :cause
64
+
62
65
  def initialize(template, original_exception = nil)
63
66
  if original_exception
64
67
  ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \
@@ -67,6 +70,7 @@ module ActionView
67
70
 
68
71
  super($!.message)
69
72
  set_backtrace($!.backtrace)
73
+ @cause = $!
70
74
  @template, @sub_templates = template, nil
71
75
  end
72
76
 
@@ -131,13 +135,13 @@ module ActionView
131
135
  end
132
136
 
133
137
  def formatted_code_for(source_code, line_counter, indent, output)
134
- start_value = (output == :html) ? {} : ""
138
+ start_value = (output == :html) ? {} : []
135
139
  source_code.inject(start_value) do |result, line|
136
140
  line_counter += 1
137
141
  if output == :html
138
142
  result.update(line_counter.to_s => "%#{indent}s %s\n" % ["", line])
139
143
  else
140
- result << "%#{indent}s: %s\n" % [line_counter, line]
144
+ result << "%#{indent}s: %s" % [line_counter, line]
141
145
  end
142
146
  end
143
147
  end
@@ -123,31 +123,6 @@ module ActionView
123
123
  ).src
124
124
  end
125
125
 
126
- # Returns Regexp to extract a cached resource's name from a cache call at the
127
- # first line of a template.
128
- # The extracted cache name is captured as :resource_name.
129
- #
130
- # <% cache notification do %> # => notification
131
- #
132
- # The pattern should support templates with a beginning comment:
133
- #
134
- # <%# Still extractable even though there's a comment %>
135
- # <% cache notification do %> # => notification
136
- #
137
- # But fail to extract a name if a resource association is cached.
138
- #
139
- # <% cache notification.event do %> # => nil
140
- def resource_cache_call_pattern
141
- /\A
142
- (?:<%\#.*%>)* # optional initial comment
143
- \s* # followed by optional spaces or newlines
144
- <%\s*cache[\(\s] # followed by an ERB call to cache
145
- \s* # followed by optional spaces or newlines
146
- (?<resource_name>\w+) # capture the cache call argument as :resource_name
147
- [\s\)] # followed by a space or close paren
148
- /xm
149
- end
150
-
151
126
  private
152
127
 
153
128
  def valid_encoding(string, encoding)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionview
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0.beta2
4
+ version: 5.0.0.beta3
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-01 00:00:00.000000000 Z
11
+ date: 2016-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 5.0.0.beta2
19
+ version: 5.0.0.beta3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 5.0.0.beta2
26
+ version: 5.0.0.beta3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: builder
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -98,28 +98,28 @@ dependencies:
98
98
  requirements:
99
99
  - - '='
100
100
  - !ruby/object:Gem::Version
101
- version: 5.0.0.beta2
101
+ version: 5.0.0.beta3
102
102
  type: :development
103
103
  prerelease: false
104
104
  version_requirements: !ruby/object:Gem::Requirement
105
105
  requirements:
106
106
  - - '='
107
107
  - !ruby/object:Gem::Version
108
- version: 5.0.0.beta2
108
+ version: 5.0.0.beta3
109
109
  - !ruby/object:Gem::Dependency
110
110
  name: activemodel
111
111
  requirement: !ruby/object:Gem::Requirement
112
112
  requirements:
113
113
  - - '='
114
114
  - !ruby/object:Gem::Version
115
- version: 5.0.0.beta2
115
+ version: 5.0.0.beta3
116
116
  type: :development
117
117
  prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - '='
121
121
  - !ruby/object:Gem::Version
122
- version: 5.0.0.beta2
122
+ version: 5.0.0.beta3
123
123
  description: Simple, battle-tested conventions and helpers for building web pages.
124
124
  email: david@loudthinking.com
125
125
  executables: []
@@ -250,9 +250,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
250
250
  requirements:
251
251
  - none
252
252
  rubyforge_project:
253
- rubygems_version: 2.5.2
253
+ rubygems_version: 2.5.1
254
254
  signing_key:
255
255
  specification_version: 4
256
256
  summary: Rendering framework putting the V in MVC (part of Rails).
257
257
  test_files: []
258
- has_rdoc: