actionview 5.0.0.beta3 → 5.0.0.beta4

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: 10ff10399f3daad3dd51ca76efc01f2ba03ac135
4
- data.tar.gz: d9d94bc8fb7bdc057093ff1ad2a6d1df9e7bf544
3
+ metadata.gz: 768e4964d149a5bc14eead72995ad8ef3536470d
4
+ data.tar.gz: de1dce7d080bfd7e4c1662dadafac393438dcede
5
5
  SHA512:
6
- metadata.gz: 9d8fec2801766ff1fc3052e57be09a240280a1b28f3327c6844a312f828a8216e369c50c823fbf8d4608e5e521231cc4297650618c416047e02134c3dcf4257d
7
- data.tar.gz: 749328a74e11735391283ca4741a6c1d1a35454726bc903fc29ffa354574ad84a61b13ea4ba079513e35cd3e7b3d30c190e408b08abf8c9f4b3c65d76bade99e
6
+ metadata.gz: 6554825ae4f6172d0dc2ed7392de0a2384230558c4bf3e9e16caceda7c9f11010ff3f5d3335761af6997201743345931417dc3b472a884ee348da3a44f63521c
7
+ data.tar.gz: a43c2274df7c6ef07f3d21f7e04fe1c3eb301beb890cf30382ba5bbccd020493655fe2d999dc04d336d15ebab3711f2f206f9d518b2a4e832e13a341d43aed51
data/CHANGELOG.md CHANGED
@@ -1,3 +1,37 @@
1
+ ## Rails 5.0.0.beta4 (April 27, 2016) ##
2
+
3
+ * `date_select` helper `:with_css_classes` option now accepts a hash of strings
4
+ for `:year`, `:month`, `:day`, `:hour`, `:minute`, `:second` that will extend
5
+ the select type with the given css class value.
6
+
7
+ ```erb
8
+ <%= f.date_select :birthday, with_css_classes: { month: "my-month", year: "my-year" } %>
9
+ ```
10
+
11
+ ```html
12
+ <select id="user_birthday_3i" name="user[birthday(3i)]">…</select>
13
+ <select id="user_birthday_2i" name="user[birthday(2i)]" class="my-month">…</select>
14
+ <select id="user_birthday_1i" name="user[birthday(1i)]" class="my-year">…</select>
15
+ ```
16
+
17
+ *Matthias Neumayr*
18
+
19
+ * Add `to_sentence` helper that is a HTML-safe aware version of `Array#to_sentence`.
20
+
21
+ *Neil Matatall*
22
+
23
+ * Deprecate `datetime_field` and `datetime_field_tag` helpers.
24
+ Datetime input type was removed from HTML specification.
25
+ One can use `datetime_local_field` and `datetime_local_field_tag` instead.
26
+
27
+ *Wojciech Wnętrzak*
28
+
29
+ * Added log "Rendering ...", when starting to render a template to log that
30
+ we have started rendering something. This helps to easily identify the origin
31
+ of queries in the log whether they came from controller or views.
32
+
33
+ *Vipul A M and Prem Sichanugrist*
34
+
1
35
  ## Rails 5.0.0.beta3 (February 24, 2016) ##
2
36
 
3
37
  * Collection rendering can cache and fetch multiple partials at once.
@@ -38,7 +72,7 @@
38
72
 
39
73
  * Create a new `ActiveSupport::SafeBuffer` instance when `content_for` is flushed.
40
74
 
41
- Fixes #19890
75
+ Fixes #19890.
42
76
 
43
77
  *Yoong Kang Lim*
44
78
 
@@ -4,7 +4,7 @@ require 'monitor'
4
4
 
5
5
  module ActionView
6
6
  class Digestor
7
- @@digest_monitor = Monitor.new
7
+ @@digest_mutex = Mutex.new
8
8
 
9
9
  class PerRequestDigestCacheExpiry < Struct.new(:app) # :nodoc:
10
10
  def call(env)
@@ -20,111 +20,104 @@ module ActionView
20
20
  # * <tt>finder</tt> - An instance of <tt>ActionView::LookupContext</tt>
21
21
  # * <tt>dependencies</tt> - An array of dependent views
22
22
  # * <tt>partial</tt> - Specifies whether the template is a partial
23
- def digest(name:, finder:, **options)
24
- options.assert_valid_keys(:dependencies, :partial)
25
-
26
- cache_key = ([ name ].compact + Array.wrap(options[:dependencies])).join('.')
23
+ def digest(name:, finder:, dependencies: [])
24
+ dependencies ||= []
25
+ cache_key = ([ name ].compact + dependencies).join('.')
27
26
 
28
27
  # this is a correctly done double-checked locking idiom
29
28
  # (Concurrent::Map's lookups have volatile semantics)
30
- finder.digest_cache[cache_key] || @@digest_monitor.synchronize do
29
+ finder.digest_cache[cache_key] || @@digest_mutex.synchronize do
31
30
  finder.digest_cache.fetch(cache_key) do # re-check under lock
32
- compute_and_store_digest(cache_key, name, finder, options)
31
+ partial = name.include?("/_")
32
+ root = tree(name, finder, partial)
33
+ dependencies.each do |injected_dep|
34
+ root.children << Injected.new(injected_dep, nil, nil)
35
+ end
36
+ finder.digest_cache[cache_key] = root.digest(finder)
33
37
  end
34
38
  end
35
39
  end
36
40
 
37
- private
38
- def compute_and_store_digest(cache_key, name, finder, options) # called under @@digest_monitor lock
39
- klass = if options[:partial] || name.include?("/_")
40
- # Prevent re-entry or else recursive templates will blow the stack.
41
- # There is no need to worry about other threads seeing the +false+ value,
42
- # as they will then have to wait for this thread to let go of the @@digest_monitor lock.
43
- pre_stored = finder.digest_cache.put_if_absent(cache_key, false).nil? # put_if_absent returns nil on insertion
44
- PartialDigestor
45
- else
46
- Digestor
47
- end
48
-
49
- finder.digest_cache[cache_key] = stored_digest = klass.new(name, finder, options).digest
50
- ensure
51
- # something went wrong or ActionView::Resolver.caching? is false, make sure not to corrupt the @@cache
52
- finder.digest_cache.delete_pair(cache_key, false) if pre_stored && !stored_digest
53
- end
54
- end
55
-
56
- attr_reader :name, :finder, :options
41
+ def logger
42
+ ActionView::Base.logger || NullLogger
43
+ end
57
44
 
58
- def initialize(name, finder, options = {})
59
- @name, @finder = name, finder
60
- @options = options
61
- end
45
+ # Create a dependency tree for template named +name+.
46
+ def tree(name, finder, partial = false, seen = {})
47
+ logical_name = name.gsub(%r|/_|, "/")
62
48
 
63
- def digest
64
- Digest::MD5.hexdigest("#{source}-#{dependency_digest}").tap do |digest|
65
- logger.debug " Cache digest for #{template.inspect}: #{digest}"
66
- end
67
- rescue ActionView::MissingTemplate
68
- logger.error " Couldn't find template for digesting: #{name}"
69
- ''
70
- end
49
+ if finder.disable_cache { finder.exists?(logical_name, [], partial) }
50
+ template = finder.disable_cache { finder.find(logical_name, [], partial) }
71
51
 
72
- def dependencies
73
- DependencyTracker.find_dependencies(name, template, finder.view_paths)
74
- rescue ActionView::MissingTemplate
75
- logger.error " '#{name}' file doesn't exist, so no dependencies"
76
- []
77
- end
52
+ if node = seen[template.identifier] # handle cycles in the tree
53
+ node
54
+ else
55
+ node = seen[template.identifier] = Node.create(name, logical_name, template, partial)
78
56
 
79
- def nested_dependencies
80
- dependencies.collect do |dependency|
81
- dependencies = PartialDigestor.new(dependency, finder).nested_dependencies
82
- dependencies.any? ? { dependency => dependencies } : dependency
57
+ deps = DependencyTracker.find_dependencies(name, template, finder.view_paths)
58
+ deps.uniq { |n| n.gsub(%r|/_|, "/") }.each do |dep_file|
59
+ node.children << tree(dep_file, finder, true, seen)
60
+ end
61
+ node
62
+ end
63
+ else
64
+ logger.error " '#{name}' file doesn't exist, so no dependencies"
65
+ logger.error " Couldn't find template for digesting: #{name}"
66
+ seen[name] ||= Missing.new(name, logical_name, nil)
67
+ end
83
68
  end
84
69
  end
85
70
 
86
- private
87
- class NullLogger
88
- def self.debug(_); end
89
- def self.error(_); end
90
- end
71
+ class Node
72
+ attr_reader :name, :logical_name, :template, :children
91
73
 
92
- def logger
93
- ActionView::Base.logger || NullLogger
74
+ def self.create(name, logical_name, template, partial)
75
+ klass = partial ? Partial : Node
76
+ klass.new(name, logical_name, template, [])
94
77
  end
95
78
 
96
- def logical_name
97
- name.gsub(%r|/_|, "/")
79
+ def initialize(name, logical_name, template, children = [])
80
+ @name = name
81
+ @logical_name = logical_name
82
+ @template = template
83
+ @children = children
98
84
  end
99
85
 
100
- def partial?
101
- false
86
+ def digest(finder, stack = [])
87
+ Digest::MD5.hexdigest("#{template.source}-#{dependency_digest(finder, stack)}")
102
88
  end
103
89
 
104
- def template
105
- @template ||= finder.disable_cache { finder.find(logical_name, [], partial?) }
90
+ def dependency_digest(finder, stack)
91
+ children.map do |node|
92
+ if stack.include?(node)
93
+ false
94
+ else
95
+ finder.digest_cache[node.name] ||= begin
96
+ stack.push node
97
+ node.digest(finder, stack).tap { stack.pop }
98
+ end
99
+ end
100
+ end.join("-")
106
101
  end
107
102
 
108
- def source
109
- template.source
103
+ def to_dep_map
104
+ children.any? ? { name => children.map(&:to_dep_map) } : name
110
105
  end
106
+ end
111
107
 
112
- def dependency_digest
113
- template_digests = dependencies.collect do |template_name|
114
- Digestor.digest(name: template_name, finder: finder, partial: true)
115
- end
108
+ class Partial < Node; end
116
109
 
117
- (template_digests + injected_dependencies).join("-")
118
- end
110
+ class Missing < Node
111
+ def digest(finder, _ = []) '' end
112
+ end
119
113
 
120
- def injected_dependencies
121
- Array.wrap(options[:dependencies])
122
- end
123
- end
114
+ class Injected < Node
115
+ def digest(finder, _ = []) name end
116
+ end
124
117
 
125
- class PartialDigestor < Digestor # :nodoc:
126
- def partial?
127
- true
118
+ class NullLogger
119
+ def self.debug(_); end
120
+ def self.error(_); end
128
121
  end
129
122
  end
130
123
  end
@@ -37,9 +37,8 @@ module ActionView
37
37
  end
38
38
 
39
39
  # Try to get stored content. If the content
40
- # is not available and we are inside the layout
41
- # fiber, we set that we are waiting for the given
42
- # key and yield.
40
+ # is not available and we're inside the layout fiber,
41
+ # then it will begin waiting for the given key and yield.
43
42
  def get(key)
44
43
  return super if @content.key?(key)
45
44
 
@@ -60,8 +59,8 @@ module ActionView
60
59
  end
61
60
 
62
61
  # Appends the contents for the given key. This is called
63
- # by provides and resumes back to the fiber if it is
64
- # the key it is waiting for.
62
+ # by providing and resuming back to the fiber,
63
+ # if that's the key it's waiting for.
65
64
  def append!(key, value)
66
65
  super
67
66
  @fiber.resume if @waiting_for == key
@@ -8,7 +8,7 @@ module ActionView
8
8
  MAJOR = 5
9
9
  MINOR = 0
10
10
  TINY = 0
11
- PRE = "beta3"
11
+ PRE = "beta4"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -132,7 +132,7 @@ module ActionView
132
132
  end
133
133
 
134
134
  private
135
- # Delegate to xml builder, first wrapping the element in a xhtml
135
+ # Delegate to xml builder, first wrapping the element in an xhtml
136
136
  # namespaced div element if the method and arguments indicate
137
137
  # that an xhtml_block? is desired.
138
138
  def method_missing(method, *arguments, &block)
@@ -226,8 +226,10 @@ module ActionView
226
226
  # for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
227
227
  # Setting this option prepends a select option with a generic prompt (Day, Month, Year, Hour, Minute, Seconds)
228
228
  # or the given prompt string.
229
- # * <tt>:with_css_classes</tt> - Set to true if you want assign different styles for 'select' tags. This option
230
- # automatically set classes 'year', 'month', 'day', 'hour', 'minute' and 'second' for your 'select' tags.
229
+ # * <tt>:with_css_classes</tt> - Set to true or a hash of strings. Use true if you want to assign generic styles for
230
+ # select tags. This automatically set classes 'year', 'month', 'day', 'hour', 'minute' and 'second'. A hash of
231
+ # strings for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt>, <tt>:second</tt>
232
+ # will extend the select type with the given value. Use +html_options+ to modify every select tag in the set.
231
233
  # * <tt>:use_hidden</tt> - Set to true if you only want to generate hidden input tags.
232
234
  #
233
235
  # If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
@@ -994,7 +996,7 @@ module ActionView
994
996
  :name => input_name_from_type(type)
995
997
  }.merge!(@html_options)
996
998
  select_options[:disabled] = 'disabled' if @options[:disabled]
997
- select_options[:class] = [select_options[:class], type].compact.join(' ') if @options[:with_css_classes]
999
+ select_options[:class] = css_class_attribute(type, select_options[:class], @options[:with_css_classes]) if @options[:with_css_classes]
998
1000
 
999
1001
  select_html = "\n"
1000
1002
  select_html << content_tag("option".freeze, '', :value => '') + "\n" if @options[:include_blank]
@@ -1004,6 +1006,20 @@ module ActionView
1004
1006
  (content_tag("select".freeze, select_html.html_safe, select_options) + "\n").html_safe
1005
1007
  end
1006
1008
 
1009
+ # Builds the css class value for the select element
1010
+ # css_class_attribute(:year, 'date optional', { year: 'my-year' })
1011
+ # => "date optional my-year"
1012
+ def css_class_attribute(type, html_options_class, options) # :nodoc:
1013
+ css_class = case options
1014
+ when Hash
1015
+ options[type.to_sym]
1016
+ else
1017
+ type
1018
+ end
1019
+
1020
+ [html_options_class, css_class].compact.join(' ')
1021
+ end
1022
+
1007
1023
  # Builds a prompt option tag with supplied options or from default options.
1008
1024
  # prompt_option_tag(:month, prompt: 'Select month')
1009
1025
  # => "<option value="">Select month</option>"
@@ -1118,6 +1118,10 @@ module ActionView
1118
1118
  # # => <input id="user_born_on" name="user[born_on]" type="datetime" min="2014-05-20T00:00:00.000+0000" />
1119
1119
  #
1120
1120
  def datetime_field(object_name, method, options = {})
1121
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
1122
+ datetime_field is deprecated and will be removed in Rails 5.1.
1123
+ Use datetime_local_field instead.
1124
+ MESSAGE
1121
1125
  Tags::DatetimeField.new(object_name, method, self, options).render
1122
1126
  end
1123
1127
 
@@ -1922,8 +1926,6 @@ module ActionView
1922
1926
  @object_name.to_s.humanize
1923
1927
  end
1924
1928
 
1925
- model = model.downcase
1926
-
1927
1929
  defaults = []
1928
1930
  defaults << :"helpers.submit.#{object_name}.#{key}"
1929
1931
  defaults << :"helpers.submit.#{key}"
@@ -268,10 +268,11 @@ module ActionView
268
268
  # for more information.)
269
269
  #
270
270
  # You can also supply an array of ActiveSupport::TimeZone objects
271
- # as +priority_zones+, so that they will be listed above the rest of the
272
- # (long) list. (You can use ActiveSupport::TimeZone.us_zones as a convenience
273
- # for obtaining a list of the US time zones, or a Regexp to select the zones
274
- # of your choice)
271
+ # as +priority_zones+ so that they will be listed above the rest of the
272
+ # (long) list. You can use ActiveSupport::TimeZone.us_zones for a list
273
+ # of US time zones, ActiveSupport::TimeZone.country_zones(country_code)
274
+ # for another country's time zones, or a Regexp to select the zones of
275
+ # your choice.
275
276
  #
276
277
  # Finally, this method supports a <tt>:default</tt> option, which selects
277
278
  # a default ActiveSupport::TimeZone if the object's time zone is +nil+.
@@ -691,6 +691,10 @@ module ActionView
691
691
  # * <tt>:step</tt> - The acceptable value granularity.
692
692
  # * Otherwise accepts the same options as text_field_tag.
693
693
  def datetime_field_tag(name, value = nil, options = {})
694
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
695
+ datetime_field_tag is deprecated and will be removed in Rails 5.1.
696
+ Use datetime_local_field_tag instead.
697
+ MESSAGE
694
698
  text_field_tag(name, value, options.merge(type: :datetime))
695
699
  end
696
700
 
@@ -23,7 +23,7 @@ module ActionView
23
23
  end
24
24
  end
25
25
 
26
- # Formats a +number+ into a US phone number (e.g., (555)
26
+ # Formats a +number+ into a phone number (US by default e.g., (555)
27
27
  # 123-9876). You can customize the format in the +options+ hash.
28
28
  #
29
29
  # ==== Options
@@ -35,6 +35,8 @@ module ActionView
35
35
  # end of the generated number.
36
36
  # * <tt>:country_code</tt> - Sets the country code for the phone
37
37
  # number.
38
+ # * <tt>:pattern</tt> - Specifies how the number is divided into three
39
+ # groups with the custom regexp to override the default format.
38
40
  # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
39
41
  # the argument is invalid.
40
42
  #
@@ -52,6 +54,11 @@ module ActionView
52
54
  #
53
55
  # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
54
56
  # # => +1.123.555.1234 x 1343
57
+ #
58
+ # number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
59
+ # # => "(755) 6123-4567"
60
+ # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/))
61
+ # # => "133-1234-5678"
55
62
  def number_to_phone(number, options = {})
56
63
  return unless number
57
64
  options = options.symbolize_keys
@@ -33,6 +33,36 @@ module ActionView #:nodoc:
33
33
 
34
34
  array.flatten.map! { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
35
35
  end
36
+
37
+ # Converts the array to a comma-separated sentence where the last element is
38
+ # joined by the connector word. This is the html_safe-aware version of
39
+ # ActiveSupport's {Array#to_sentence}[http://api.rubyonrails.org/classes/Array.html#method-i-to_sentence].
40
+ #
41
+ def to_sentence(array, options = {})
42
+ options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
43
+
44
+ default_connectors = {
45
+ :words_connector => ', ',
46
+ :two_words_connector => ' and ',
47
+ :last_word_connector => ', and '
48
+ }
49
+ if defined?(I18n)
50
+ i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
51
+ default_connectors.merge!(i18n_connectors)
52
+ end
53
+ options = default_connectors.merge!(options)
54
+
55
+ case array.length
56
+ when 0
57
+ ''.html_safe
58
+ when 1
59
+ ERB::Util.html_escape(array[0])
60
+ when 2
61
+ safe_join([array[0], array[1]], options[:two_words_connector])
62
+ else
63
+ safe_join([safe_join(array[0...-1], options[:words_connector]), options[:last_word_connector], array[-1]])
64
+ end
65
+ end
36
66
  end
37
67
  end
38
68
  end
@@ -120,7 +120,7 @@ module ActionView
120
120
  attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
121
121
 
122
122
  # Vendors the full, link and white list sanitizers.
123
- # Provided strictly for compatibility and can be removed in Rails 5.
123
+ # Provided strictly for compatibility and can be removed in Rails 5.1.
124
124
  def sanitizer_vendor
125
125
  Rails::Html::Sanitizer
126
126
  end
@@ -302,7 +302,7 @@ module ActionView
302
302
  params = html_options.delete('params')
303
303
 
304
304
  method = html_options.delete('method').to_s
305
- method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : ''.html_safe
305
+ method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : ''.freeze.html_safe
306
306
 
307
307
  form_method = method == 'get' ? 'get' : 'post'
308
308
  form_options = html_options.delete('form') || {}
@@ -315,7 +315,7 @@ module ActionView
315
315
  request_method = method.empty? ? 'post' : method
316
316
  token_tag(nil, form_options: { action: url, method: request_method })
317
317
  else
318
- ''
318
+ ''.freeze
319
319
  end
320
320
 
321
321
  html_options = convert_options_to_data_attributes(options, html_options)
@@ -481,7 +481,7 @@ module ActionView
481
481
  option = html_options.delete(item).presence || next
482
482
  "#{item.dasherize}=#{ERB::Util.url_encode(option)}"
483
483
  }.compact
484
- extras = extras.empty? ? '' : '?' + extras.join('&')
484
+ extras = extras.empty? ? ''.freeze : '?' + extras.join('&')
485
485
 
486
486
  encoded_email_address = ERB::Util.url_encode(email_address).gsub("%40", "@")
487
487
  html_options["href"] = "mailto:#{encoded_email_address}#{extras}"
@@ -559,29 +559,29 @@ module ActionView
559
559
  def convert_options_to_data_attributes(options, html_options)
560
560
  if html_options
561
561
  html_options = html_options.stringify_keys
562
- html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
562
+ html_options['data-remote'] = 'true'.freeze if link_to_remote_options?(options) || link_to_remote_options?(html_options)
563
563
 
564
- method = html_options.delete('method')
564
+ method = html_options.delete('method'.freeze)
565
565
 
566
566
  add_method_to_attributes!(html_options, method) if method
567
567
 
568
568
  html_options
569
569
  else
570
- link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
570
+ link_to_remote_options?(options) ? {'data-remote' => 'true'.freeze} : {}
571
571
  end
572
572
  end
573
573
 
574
574
  def link_to_remote_options?(options)
575
575
  if options.is_a?(Hash)
576
- options.delete('remote') || options.delete(:remote)
576
+ options.delete('remote'.freeze) || options.delete(:remote)
577
577
  end
578
578
  end
579
579
 
580
580
  def add_method_to_attributes!(html_options, method)
581
- if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/
582
- html_options["rel"] = "#{html_options["rel"]} nofollow".lstrip
581
+ if method && method.to_s.downcase != "get".freeze && html_options["rel".freeze] !~ /nofollow/
582
+ html_options["rel".freeze] = "#{html_options["rel".freeze]} nofollow".lstrip
583
583
  end
584
- html_options["data-method"] = method
584
+ html_options["data-method".freeze] = method
585
585
  end
586
586
 
587
587
  def token_tag(token=nil, form_options: {})
@@ -589,7 +589,7 @@ module ActionView
589
589
  token ||= form_authenticity_token(form_options: form_options)
590
590
  tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
591
591
  else
592
- ''
592
+ ''.freeze
593
593
  end
594
594
  end
595
595
 
@@ -30,6 +30,14 @@ module ActionView
30
30
  end
31
31
  end
32
32
 
33
+ def start(name, id, payload)
34
+ if name == "render_template.action_view"
35
+ log_rendering_start(payload)
36
+ end
37
+
38
+ super
39
+ end
40
+
33
41
  def logger
34
42
  ActionView::Base.logger
35
43
  end
@@ -54,6 +62,16 @@ module ActionView
54
62
  "[#{payload[:count]} times]"
55
63
  end
56
64
  end
65
+
66
+ private
67
+
68
+ def log_rendering_start(payload)
69
+ info do
70
+ message = " Rendering #{from_rails_root(payload[:identifier])}"
71
+ message << " within #{from_rails_root(payload[:layout])}" if payload[:layout]
72
+ message
73
+ end
74
+ end
57
75
  end
58
76
  end
59
77
 
@@ -70,8 +70,6 @@ module ActionView
70
70
  @details_keys.clear
71
71
  end
72
72
 
73
- def self.empty?; @details_keys.empty?; end
74
-
75
73
  def self.digest_caches
76
74
  @details_keys.values.map(&:digest_cache)
77
75
  end
@@ -138,6 +136,11 @@ module ActionView
138
136
  end
139
137
  alias :template_exists? :exists?
140
138
 
139
+ def any?(name, prefixes = [], partial = false)
140
+ @view_paths.exists?(*args_for_any(name, prefixes, partial))
141
+ end
142
+ alias :any_templates? :any?
143
+
141
144
  # Adds fallbacks to the view paths. Useful in cases when you are rendering
142
145
  # a :file.
143
146
  def with_fallbacks
@@ -174,6 +177,32 @@ module ActionView
174
177
  [user_details, details_key]
175
178
  end
176
179
 
180
+ def args_for_any(name, prefixes, partial) # :nodoc:
181
+ name, prefixes = normalize_name(name, prefixes)
182
+ details, details_key = detail_args_for_any
183
+ [name, prefixes, partial || false, details, details_key]
184
+ end
185
+
186
+ def detail_args_for_any # :nodoc:
187
+ @detail_args_for_any ||= begin
188
+ details = {}
189
+
190
+ registered_details.each do |k|
191
+ if k == :variants
192
+ details[k] = :any
193
+ else
194
+ details[k] = Accessors::DEFAULT_PROCS[k].call
195
+ end
196
+ end
197
+
198
+ if @cache
199
+ [details, DetailsKey.get(details)]
200
+ else
201
+ [details, nil]
202
+ end
203
+ end
204
+ end
205
+
177
206
  # Support legacy foo.erb names even though we now ignore .erb
178
207
  # as well as incorrectly putting part of the path in the template
179
208
  # name instead of the prefix.
@@ -37,10 +37,8 @@ module ActionView
37
37
  end
38
38
  end
39
39
 
40
- initializer "action_view.collection_caching" do |app|
41
- ActiveSupport.on_load(:action_controller) do
42
- PartialRenderer.collection_cache = app.config.action_controller.cache_store
43
- end
40
+ initializer "action_view.collection_caching", after: "action_controller.set_configs" do |app|
41
+ PartialRenderer.collection_cache = app.config.action_controller.cache_store
44
42
  end
45
43
 
46
44
  initializer "action_view.per_request_digest_cache" do |app|
@@ -59,7 +57,7 @@ module ActionView
59
57
 
60
58
  rake_tasks do |app|
61
59
  unless app.config.api_only
62
- load "action_view/tasks/dependencies.rake"
60
+ load "action_view/tasks/cache_digests.rake"
63
61
  end
64
62
  end
65
63
  end
@@ -15,7 +15,7 @@ module ActionView
15
15
  # that new object is called in turn. This abstracts the setup and rendering
16
16
  # into a separate classes for partials and templates.
17
17
  class AbstractRenderer #:nodoc:
18
- delegate :find_template, :find_file, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
18
+ delegate :find_template, :find_file, :template_exists?, :any_templates?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
19
19
 
20
20
  def initialize(lookup_context)
21
21
  @lookup_context = lookup_context
@@ -521,7 +521,7 @@ module ActionView
521
521
  def retrieve_variable(path, as)
522
522
  variable = as || begin
523
523
  base = path[-1] == "/".freeze ? "".freeze : File.basename(path)
524
- raise_invalid_identifier(path) unless base =~ /\A_?(.*)(?:\.\w+)*\z/
524
+ raise_invalid_identifier(path) unless base =~ /\A_?(.*?)(?:\.\w+)*\z/
525
525
  $1.to_sym
526
526
  end
527
527
  if @collection
@@ -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(CacheDigests.template_name, CacheDigests.finder).nested_dependencies
5
+ puts JSON.pretty_generate ActionView::Digestor.tree(CacheDigests.template_name, CacheDigests.finder).children.map(&:to_dep_map)
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(CacheDigests.template_name, CacheDigests.finder).dependencies
11
+ puts JSON.pretty_generate ActionView::Digestor.tree(CacheDigests.template_name, CacheDigests.finder).children.map(&:name)
12
12
  end
13
13
 
14
14
  class CacheDigests
@@ -55,6 +55,10 @@ module ActionView
55
55
  @query_cache = SmallCache.new
56
56
  end
57
57
 
58
+ def inspect
59
+ "#<#{self.class.name}:0x#{(object_id << 1).to_s(16)} keys=#{@data.size} queries=#{@query_cache.size}>"
60
+ end
61
+
58
62
  # Cache the templates returned by the block
59
63
  def cache(key, name, prefix, partial, locals)
60
64
  if Resolver.caching?
@@ -222,7 +226,7 @@ module ActionView
222
226
  end
223
227
 
224
228
  def find_template_paths(query)
225
- Dir[query].reject do |filename|
229
+ Dir[query].uniq.reject do |filename|
226
230
  File.directory?(filename) ||
227
231
  # deals with case-insensitive file systems.
228
232
  !File.fnmatch(query, filename, File::FNM_EXTGLOB)
@@ -245,8 +249,12 @@ module ActionView
245
249
  partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
246
250
  query.gsub!(/:action/, partial)
247
251
 
248
- details.each do |ext, variants|
249
- query.gsub!(/:#{ext}/, "{#{variants.compact.uniq.join(',')}}")
252
+ details.each do |ext, candidates|
253
+ if ext == :variants && candidates == :any
254
+ query.gsub!(/:#{ext}/, "*")
255
+ else
256
+ query.gsub!(/:#{ext}/, "{#{candidates.compact.uniq.join(',')}}")
257
+ end
250
258
  end
251
259
 
252
260
  File.expand_path(query, @path)
@@ -340,7 +348,11 @@ module ActionView
340
348
  query = escape_entry(File.join(@path, path))
341
349
 
342
350
  exts = EXTENSIONS.map do |ext, prefix|
343
- "{#{details[ext].compact.uniq.map { |e| "#{prefix}#{e}," }.join}}"
351
+ if ext == :variants && details[ext] == :any
352
+ "{#{prefix}*,}"
353
+ else
354
+ "{#{details[ext].compact.uniq.map { |e| "#{prefix}#{e}," }.join}}"
355
+ end
344
356
  end.join
345
357
 
346
358
  query + exts
@@ -1,4 +1,3 @@
1
- require 'set'
2
1
  require 'active_support/core_ext/module/attribute_accessors'
3
2
 
4
3
  module ActionView
@@ -1,5 +1,3 @@
1
- require 'action_view/base'
2
-
3
1
  module ActionView
4
2
  module ViewPaths
5
3
  extend ActiveSupport::Concern
@@ -10,7 +8,7 @@ module ActionView
10
8
  self._view_paths.freeze
11
9
  end
12
10
 
13
- delegate :template_exists?, :view_paths, :formats, :formats=,
11
+ delegate :template_exists?, :any_templates?, :view_paths, :formats, :formats=,
14
12
  :locale, :locale=, :to => :lookup_context
15
13
 
16
14
  module ClassMethods
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.beta3
4
+ version: 5.0.0.beta4
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-24 00:00:00.000000000 Z
11
+ date: 2016-04-27 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.beta3
19
+ version: 5.0.0.beta4
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.beta3
26
+ version: 5.0.0.beta4
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.beta3
101
+ version: 5.0.0.beta4
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.beta3
108
+ version: 5.0.0.beta4
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.beta3
115
+ version: 5.0.0.beta4
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.beta3
122
+ version: 5.0.0.beta4
123
123
  description: Simple, battle-tested conventions and helpers for building web pages.
124
124
  email: david@loudthinking.com
125
125
  executables: []
@@ -213,7 +213,7 @@ files:
213
213
  - lib/action_view/renderer/template_renderer.rb
214
214
  - lib/action_view/rendering.rb
215
215
  - lib/action_view/routing_url_for.rb
216
- - lib/action_view/tasks/dependencies.rake
216
+ - lib/action_view/tasks/cache_digests.rake
217
217
  - lib/action_view/template.rb
218
218
  - lib/action_view/template/error.rb
219
219
  - lib/action_view/template/handlers.rb
@@ -229,7 +229,7 @@ files:
229
229
  - lib/action_view/testing/resolvers.rb
230
230
  - lib/action_view/version.rb
231
231
  - lib/action_view/view_paths.rb
232
- homepage: http://www.rubyonrails.org
232
+ homepage: http://rubyonrails.org
233
233
  licenses:
234
234
  - MIT
235
235
  metadata: {}
@@ -250,7 +250,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
250
250
  requirements:
251
251
  - none
252
252
  rubyforge_project:
253
- rubygems_version: 2.5.1
253
+ rubygems_version: 2.6.4
254
254
  signing_key:
255
255
  specification_version: 4
256
256
  summary: Rendering framework putting the V in MVC (part of Rails).