actionview 4.2.11 → 5.0.7

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 (68) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +304 -184
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/action_view.rb +1 -1
  6. data/lib/action_view/base.rb +14 -2
  7. data/lib/action_view/dependency_tracker.rb +51 -18
  8. data/lib/action_view/digestor.rb +83 -81
  9. data/lib/action_view/flows.rb +4 -5
  10. data/lib/action_view/gem_version.rb +3 -3
  11. data/lib/action_view/helpers/asset_tag_helper.rb +15 -5
  12. data/lib/action_view/helpers/asset_url_helper.rb +51 -12
  13. data/lib/action_view/helpers/atom_feed_helper.rb +6 -5
  14. data/lib/action_view/helpers/cache_helper.rb +62 -21
  15. data/lib/action_view/helpers/capture_helper.rb +5 -4
  16. data/lib/action_view/helpers/controller_helper.rb +11 -2
  17. data/lib/action_view/helpers/date_helper.rb +59 -13
  18. data/lib/action_view/helpers/debug_helper.rb +1 -1
  19. data/lib/action_view/helpers/form_helper.rb +74 -72
  20. data/lib/action_view/helpers/form_options_helper.rb +79 -39
  21. data/lib/action_view/helpers/form_tag_helper.rb +74 -44
  22. data/lib/action_view/helpers/javascript_helper.rb +4 -4
  23. data/lib/action_view/helpers/number_helper.rb +28 -13
  24. data/lib/action_view/helpers/output_safety_helper.rb +32 -2
  25. data/lib/action_view/helpers/record_tag_helper.rb +12 -99
  26. data/lib/action_view/helpers/rendering_helper.rb +2 -2
  27. data/lib/action_view/helpers/sanitize_helper.rb +1 -2
  28. data/lib/action_view/helpers/tag_helper.rb +19 -11
  29. data/lib/action_view/helpers/tags/base.rb +45 -29
  30. data/lib/action_view/helpers/tags/collection_check_boxes.rb +4 -28
  31. data/lib/action_view/helpers/tags/collection_helpers.rb +32 -0
  32. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -9
  33. data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
  34. data/lib/action_view/helpers/tags/label.rb +1 -1
  35. data/lib/action_view/helpers/tags/placeholderable.rb +1 -1
  36. data/lib/action_view/helpers/tags/search_field.rb +12 -9
  37. data/lib/action_view/helpers/tags/text_field.rb +0 -1
  38. data/lib/action_view/helpers/tags/translator.rb +1 -1
  39. data/lib/action_view/helpers/text_helper.rb +27 -11
  40. data/lib/action_view/helpers/translation_helper.rb +56 -26
  41. data/lib/action_view/helpers/url_helper.rb +108 -79
  42. data/lib/action_view/layouts.rb +11 -10
  43. data/lib/action_view/log_subscriber.rb +35 -1
  44. data/lib/action_view/lookup_context.rb +69 -48
  45. data/lib/action_view/model_naming.rb +1 -1
  46. data/lib/action_view/path_set.rb +9 -0
  47. data/lib/action_view/railtie.rb +18 -3
  48. data/lib/action_view/record_identifier.rb +45 -19
  49. data/lib/action_view/renderer/abstract_renderer.rb +7 -3
  50. data/lib/action_view/renderer/partial_renderer.rb +38 -37
  51. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +49 -0
  52. data/lib/action_view/renderer/renderer.rb +2 -6
  53. data/lib/action_view/renderer/streaming_template_renderer.rb +1 -1
  54. data/lib/action_view/renderer/template_renderer.rb +11 -10
  55. data/lib/action_view/rendering.rb +15 -7
  56. data/lib/action_view/routing_url_for.rb +18 -6
  57. data/lib/action_view/tasks/{dependencies.rake → cache_digests.rake} +2 -2
  58. data/lib/action_view/template.rb +36 -12
  59. data/lib/action_view/template/error.rb +20 -9
  60. data/lib/action_view/template/handlers.rb +6 -4
  61. data/lib/action_view/template/handlers/html.rb +9 -0
  62. data/lib/action_view/template/handlers/raw.rb +1 -3
  63. data/lib/action_view/template/resolver.rb +49 -42
  64. data/lib/action_view/template/types.rb +14 -16
  65. data/lib/action_view/test_case.rb +15 -9
  66. data/lib/action_view/testing/resolvers.rb +1 -2
  67. data/lib/action_view/view_paths.rb +6 -24
  68. metadata +16 -20
@@ -1,108 +1,21 @@
1
- require 'action_view/record_identifier'
2
-
3
1
  module ActionView
4
- # = Action View Record Tag Helpers
5
2
  module Helpers
6
3
  module RecordTagHelper
7
- include ActionView::RecordIdentifier
8
-
9
- # Produces a wrapper DIV element with id and class parameters that
10
- # relate to the specified Active Record object. Usage example:
11
- #
12
- # <%= div_for(@person, class: "foo") do %>
13
- # <%= @person.name %>
14
- # <% end %>
15
- #
16
- # produces:
17
- #
18
- # <div id="person_123" class="person foo"> Joe Bloggs </div>
19
- #
20
- # You can also pass an array of Active Record objects, which will then
21
- # get iterated over and yield each record as an argument for the block.
22
- # For example:
23
- #
24
- # <%= div_for(@people, class: "foo") do |person| %>
25
- # <%= person.name %>
26
- # <% end %>
27
- #
28
- # produces:
29
- #
30
- # <div id="person_123" class="person foo"> Joe Bloggs </div>
31
- # <div id="person_124" class="person foo"> Jane Bloggs </div>
32
- #
33
- def div_for(record, *args, &block)
34
- content_tag_for(:div, record, *args, &block)
4
+ def div_for(*)
5
+ raise NoMethodError, "The `div_for` method has been removed from " \
6
+ "Rails. To continue using it, add the `record_tag_helper` gem to " \
7
+ "your Gemfile:\n" \
8
+ " gem 'record_tag_helper', '~> 1.0'\n" \
9
+ "Consult the Rails upgrade guide for details."
35
10
  end
36
11
 
37
- # content_tag_for creates an HTML element with id and class parameters
38
- # that relate to the specified Active Record object. For example:
39
- #
40
- # <%= content_tag_for(:tr, @person) do %>
41
- # <td><%= @person.first_name %></td>
42
- # <td><%= @person.last_name %></td>
43
- # <% end %>
44
- #
45
- # would produce the following HTML (assuming @person is an instance of
46
- # a Person object, with an id value of 123):
47
- #
48
- # <tr id="person_123" class="person">....</tr>
49
- #
50
- # If you require the HTML id attribute to have a prefix, you can specify it:
51
- #
52
- # <%= content_tag_for(:tr, @person, :foo) do %> ...
53
- #
54
- # produces:
55
- #
56
- # <tr id="foo_person_123" class="person">...
57
- #
58
- # You can also pass an array of objects which this method will loop through
59
- # and yield the current object to the supplied block, reducing the need for
60
- # having to iterate through the object (using <tt>each</tt>) beforehand.
61
- # For example (assuming @people is an array of Person objects):
62
- #
63
- # <%= content_tag_for(:tr, @people) do |person| %>
64
- # <td><%= person.first_name %></td>
65
- # <td><%= person.last_name %></td>
66
- # <% end %>
67
- #
68
- # produces:
69
- #
70
- # <tr id="person_123" class="person">...</tr>
71
- # <tr id="person_124" class="person">...</tr>
72
- #
73
- # content_tag_for also accepts a hash of options, which will be converted to
74
- # additional HTML attributes. If you specify a <tt>:class</tt> value, it will be combined
75
- # with the default class name for your object. For example:
76
- #
77
- # <%= content_tag_for(:li, @person, class: "bar") %>...
78
- #
79
- # produces:
80
- #
81
- # <li id="person_123" class="person bar">...
82
- #
83
- def content_tag_for(tag_name, single_or_multiple_records, prefix = nil, options = nil, &block)
84
- options, prefix = prefix, nil if prefix.is_a?(Hash)
85
-
86
- Array(single_or_multiple_records).map do |single_record|
87
- content_tag_for_single_record(tag_name, single_record, prefix, options, &block)
88
- end.join("\n").html_safe
12
+ def content_tag_for(*)
13
+ raise NoMethodError, "The `content_tag_for` method has been removed from " \
14
+ "Rails. To continue using it, add the `record_tag_helper` gem to " \
15
+ "your Gemfile:\n" \
16
+ " gem 'record_tag_helper', '~> 1.0'\n" \
17
+ "Consult the Rails upgrade guide for details."
89
18
  end
90
-
91
- private
92
-
93
- # Called by <tt>content_tag_for</tt> internally to render a content tag
94
- # for each record.
95
- def content_tag_for_single_record(tag_name, record, prefix, options, &block)
96
- options = options ? options.dup : {}
97
- options[:class] = [ dom_class(record, prefix), options[:class] ].compact
98
- options[:id] = dom_id(record, prefix)
99
-
100
- if block_given?
101
- content_tag(tag_name, capture(record, &block), options)
102
- else
103
- content_tag(tag_name, "", options)
104
- end
105
- end
106
19
  end
107
20
  end
108
21
  end
@@ -18,7 +18,7 @@ module ActionView
18
18
  # performs HTML escape on the string first. Setting the content type as
19
19
  # <tt>text/html</tt>.
20
20
  # * <tt>:body</tt> - Renders the text passed in, and inherits the content
21
- # type of <tt>text/html</tt> from <tt>ActionDispatch::Response</tt>
21
+ # type of <tt>text/plain</tt> from <tt>ActionDispatch::Response</tt>
22
22
  # object.
23
23
  #
24
24
  # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
@@ -32,7 +32,7 @@ module ActionView
32
32
  view_renderer.render(self, options)
33
33
  end
34
34
  else
35
- view_renderer.render_partial(self, :partial => options, :locals => locals)
35
+ view_renderer.render_partial(self, :partial => options, :locals => locals, &block)
36
36
  end
37
37
  end
38
38
 
@@ -1,5 +1,4 @@
1
1
  require 'active_support/core_ext/object/try'
2
- require 'active_support/deprecation'
3
2
  require 'rails-html-sanitizer'
4
3
 
5
4
  module ActionView
@@ -121,7 +120,7 @@ module ActionView
121
120
  attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
122
121
 
123
122
  # Vendors the full, link and white list sanitizers.
124
- # Provided strictly for compabitility and can be removed in Rails 5.
123
+ # Provided strictly for compatibility and can be removed in Rails 5.1.
125
124
  def sanitizer_vendor
126
125
  Rails::Html::Sanitizer
127
126
  end
@@ -18,13 +18,14 @@ module ActionView
18
18
  itemscope allowfullscreen default inert sortable
19
19
  truespeed typemustmatch).to_set
20
20
 
21
- BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attribute| attribute.to_sym })
21
+ BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
22
22
 
23
23
  TAG_PREFIXES = ['aria', 'data', :aria, :data].to_set
24
24
 
25
- PRE_CONTENT_STRINGS = {
26
- :textarea => "\n"
27
- }
25
+ PRE_CONTENT_STRINGS = Hash.new { "".freeze }
26
+ PRE_CONTENT_STRINGS[:textarea] = "\n"
27
+ PRE_CONTENT_STRINGS["textarea"] = "\n"
28
+
28
29
 
29
30
  # Returns an empty HTML tag of type +name+ which by default is XHTML
30
31
  # compliant. Set +open+ to true to create an open tag compatible
@@ -143,24 +144,31 @@ module ActionView
143
144
  def content_tag_string(name, content, options, escape = true)
144
145
  tag_options = tag_options(options, escape) if options
145
146
  content = ERB::Util.unwrapped_html_escape(content) if escape
146
- "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe
147
+ "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name]}#{content}</#{name}>".html_safe
147
148
  end
148
149
 
149
150
  def tag_options(options, escape = true)
150
151
  return if options.blank?
151
- attrs = []
152
+ output = ""
153
+ sep = " ".freeze
152
154
  options.each_pair do |key, value|
153
155
  if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
154
156
  value.each_pair do |k, v|
155
- attrs << prefix_tag_option(key, k, v, escape)
157
+ next if v.nil?
158
+ output << sep
159
+ output << prefix_tag_option(key, k, v, escape)
156
160
  end
157
161
  elsif BOOLEAN_ATTRIBUTES.include?(key)
158
- attrs << boolean_tag_option(key) if value
162
+ if value
163
+ output << sep
164
+ output << boolean_tag_option(key)
165
+ end
159
166
  elsif !value.nil?
160
- attrs << tag_option(key, value, escape)
167
+ output << sep
168
+ output << tag_option(key, value, escape)
161
169
  end
162
170
  end
163
- " #{attrs * ' '}" unless attrs.empty?
171
+ output unless output.empty?
164
172
  end
165
173
 
166
174
  def prefix_tag_option(prefix, key, value, escape)
@@ -177,7 +185,7 @@ module ActionView
177
185
 
178
186
  def tag_option(key, value, escape)
179
187
  if value.is_a?(Array)
180
- value = escape ? safe_join(value, " ") : value.join(" ")
188
+ value = escape ? safe_join(value, " ".freeze) : value.join(" ".freeze)
181
189
  else
182
190
  value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
183
191
  end
@@ -14,7 +14,14 @@ module ActionView
14
14
  @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
15
15
  @object = retrieve_object(options.delete(:object))
16
16
  @options = options
17
- @auto_index = retrieve_autoindex(Regexp.last_match.pre_match) if Regexp.last_match
17
+
18
+ if Regexp.last_match
19
+ @generate_indexed_names = true
20
+ @auto_index = retrieve_autoindex(Regexp.last_match.pre_match)
21
+ else
22
+ @generate_indexed_names = false
23
+ @auto_index = nil
24
+ end
18
25
  end
19
26
 
20
27
  # This is what child classes implement.
@@ -79,35 +86,30 @@ module ActionView
79
86
  end
80
87
 
81
88
  def add_default_name_and_id(options)
82
- if options.has_key?("index")
83
- options["name"] ||= options.fetch("name"){ tag_name_with_index(options["index"], options["multiple"]) }
84
- options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) }
85
- options.delete("index")
86
- elsif defined?(@auto_index)
87
- options["name"] ||= options.fetch("name"){ tag_name_with_index(@auto_index, options["multiple"]) }
88
- options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) }
89
- else
90
- options["name"] ||= options.fetch("name"){ tag_name(options["multiple"]) }
91
- options["id"] = options.fetch("id"){ tag_id }
89
+ index = name_and_id_index(options)
90
+ options["name"] = options.fetch("name"){ tag_name(options["multiple"], index) }
91
+ options["id"] = options.fetch("id"){ tag_id(index) }
92
+ if namespace = options.delete("namespace")
93
+ options['id'] = options['id'] ? "#{namespace}_#{options['id']}" : namespace
92
94
  end
93
-
94
- options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence
95
95
  end
96
96
 
97
- def tag_name(multiple = false)
98
- "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
99
- end
100
-
101
- def tag_name_with_index(index, multiple = false)
102
- "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
103
- end
104
-
105
- def tag_id
106
- "#{sanitized_object_name}_#{sanitized_method_name}"
97
+ def tag_name(multiple = false, index = nil)
98
+ # a little duplication to construct less strings
99
+ if index
100
+ "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
101
+ else
102
+ "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
103
+ end
107
104
  end
108
105
 
109
- def tag_id_with_index(index)
110
- "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
106
+ def tag_id(index = nil)
107
+ # a little duplication to construct less strings
108
+ if index
109
+ "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
110
+ else
111
+ "#{sanitized_object_name}_#{sanitized_method_name}"
112
+ end
111
113
  end
112
114
 
113
115
  def sanitized_object_name
@@ -119,13 +121,18 @@ module ActionView
119
121
  end
120
122
 
121
123
  def sanitized_value(value)
122
- value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase
124
+ value.to_s.gsub(/\s/, "_").gsub(/[^-[[:word:]]]/, "").mb_chars.downcase.to_s
123
125
  end
124
126
 
125
127
  def select_content_tag(option_tags, options, html_options)
126
128
  html_options = html_options.stringify_keys
127
129
  add_default_name_and_id(html_options)
128
- options[:include_blank] ||= true unless options[:prompt] || select_not_required?(html_options)
130
+
131
+ if placeholder_required?(html_options)
132
+ raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
133
+ options[:include_blank] ||= true unless options[:prompt]
134
+ end
135
+
129
136
  value = options.fetch(:selected) { value(object) }
130
137
  select = content_tag("select", add_options(option_tags, options, value), html_options)
131
138
 
@@ -136,8 +143,9 @@ module ActionView
136
143
  end
137
144
  end
138
145
 
139
- def select_not_required?(html_options)
140
- !html_options["required"] || html_options["multiple"] || html_options["size"].to_i > 1
146
+ def placeholder_required?(html_options)
147
+ # See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
148
+ html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
141
149
  end
142
150
 
143
151
  def add_options(option_tags, options, value = nil)
@@ -149,6 +157,14 @@ module ActionView
149
157
  end
150
158
  option_tags
151
159
  end
160
+
161
+ def name_and_id_index(options)
162
+ if options.key?("index")
163
+ options.delete("index") || ""
164
+ elsif @generate_indexed_names
165
+ @auto_index || ""
166
+ end
167
+ end
152
168
  end
153
169
  end
154
170
  end
@@ -9,29 +9,13 @@ module ActionView
9
9
  class CheckBoxBuilder < Builder # :nodoc:
10
10
  def check_box(extra_html_options={})
11
11
  html_options = extra_html_options.merge(@input_html_options)
12
+ html_options[:multiple] = true
12
13
  @template_object.check_box(@object_name, @method_name, html_options, @value, nil)
13
14
  end
14
15
  end
15
16
 
16
17
  def render(&block)
17
- rendered_collection = render_collection do |item, value, text, default_html_options|
18
- default_html_options[:multiple] = true
19
- builder = instantiate_builder(CheckBoxBuilder, item, value, text, default_html_options)
20
-
21
- if block_given?
22
- @template_object.capture(builder, &block)
23
- else
24
- render_component(builder)
25
- end
26
- end
27
-
28
- # Append a hidden field to make sure something will be sent back to the
29
- # server if all check boxes are unchecked.
30
- if @options.fetch(:include_hidden, true)
31
- rendered_collection + hidden_field
32
- else
33
- rendered_collection
34
- end
18
+ render_collection_for(CheckBoxBuilder, &block)
35
19
  end
36
20
 
37
21
  private
@@ -40,16 +24,8 @@ module ActionView
40
24
  builder.check_box + builder.label
41
25
  end
42
26
 
43
- def hidden_field
44
- hidden_name = @html_options[:name]
45
-
46
- hidden_name ||= if @options.has_key?(:index)
47
- "#{tag_name_with_index(@options[:index])}[]"
48
- else
49
- "#{tag_name}[]"
50
- end
51
-
52
- @template_object.hidden_field_tag(hidden_name, "", id: nil)
27
+ def hidden_field_name #:nodoc:
28
+ "#{super}[]"
53
29
  end
54
30
  end
55
31
  end
@@ -19,6 +19,8 @@ module ActionView
19
19
 
20
20
  def label(label_html_options={}, &block)
21
21
  html_options = @input_html_options.slice(:index, :namespace).merge(label_html_options)
22
+ html_options[:for] ||= @input_html_options[:id] if @input_html_options[:id]
23
+
22
24
  @template_object.label(@object_name, @sanitized_attribute_name, @text, html_options, &block)
23
25
  end
24
26
  end
@@ -79,6 +81,36 @@ module ActionView
79
81
  yield item, value, text, default_html_options.merge(additional_html_options)
80
82
  end.join.html_safe
81
83
  end
84
+
85
+ def render_collection_for(builder_class, &block) #:nodoc:
86
+ options = @options.stringify_keys
87
+ rendered_collection = render_collection do |item, value, text, default_html_options|
88
+ builder = instantiate_builder(builder_class, item, value, text, default_html_options)
89
+
90
+ if block_given?
91
+ @template_object.capture(builder, &block)
92
+ else
93
+ render_component(builder)
94
+ end
95
+ end
96
+
97
+ # Prepend a hidden field to make sure something will be sent back to the
98
+ # server if all radio buttons are unchecked.
99
+ if options.fetch('include_hidden', true)
100
+ hidden_field + rendered_collection
101
+ else
102
+ rendered_collection
103
+ end
104
+ end
105
+
106
+ def hidden_field #:nodoc:
107
+ hidden_name = @html_options[:name] || hidden_field_name
108
+ @template_object.hidden_field_tag(hidden_name, "", id: nil)
109
+ end
110
+
111
+ def hidden_field_name #:nodoc:
112
+ "#{tag_name(false, @options[:index])}"
113
+ end
82
114
  end
83
115
  end
84
116
  end
@@ -14,15 +14,7 @@ module ActionView
14
14
  end
15
15
 
16
16
  def render(&block)
17
- render_collection do |item, value, text, default_html_options|
18
- builder = instantiate_builder(RadioButtonBuilder, item, value, text, default_html_options)
19
-
20
- if block_given?
21
- @template_object.capture(builder, &block)
22
- else
23
- render_component(builder)
24
- end
25
- end
17
+ render_collection_for(RadioButtonBuilder, &block)
26
18
  end
27
19
 
28
20
  private