actionview 5.0.7.2 → 5.1.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +169 -345
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/action_view/base.rb +19 -19
  6. data/lib/action_view/buffers.rb +1 -1
  7. data/lib/action_view/context.rb +1 -1
  8. data/lib/action_view/dependency_tracker.rb +4 -5
  9. data/lib/action_view/digestor.rb +22 -13
  10. data/lib/action_view/flows.rb +5 -6
  11. data/lib/action_view/gem_version.rb +2 -2
  12. data/lib/action_view/helpers/active_model_helper.rb +8 -8
  13. data/lib/action_view/helpers/asset_tag_helper.rb +62 -36
  14. data/lib/action_view/helpers/asset_url_helper.rb +111 -49
  15. data/lib/action_view/helpers/atom_feed_helper.rb +12 -13
  16. data/lib/action_view/helpers/cache_helper.rb +32 -20
  17. data/lib/action_view/helpers/capture_helper.rb +2 -2
  18. data/lib/action_view/helpers/controller_helper.rb +2 -2
  19. data/lib/action_view/helpers/csrf_helper.rb +3 -3
  20. data/lib/action_view/helpers/date_helper.rb +119 -109
  21. data/lib/action_view/helpers/debug_helper.rb +2 -3
  22. data/lib/action_view/helpers/form_helper.rb +440 -31
  23. data/lib/action_view/helpers/form_options_helper.rb +12 -12
  24. data/lib/action_view/helpers/form_tag_helper.rb +20 -19
  25. data/lib/action_view/helpers/javascript_helper.rb +6 -6
  26. data/lib/action_view/helpers/number_helper.rb +48 -46
  27. data/lib/action_view/helpers/output_safety_helper.rb +8 -8
  28. data/lib/action_view/helpers/record_tag_helper.rb +2 -2
  29. data/lib/action_view/helpers/rendering_helper.rb +2 -3
  30. data/lib/action_view/helpers/sanitize_helper.rb +16 -12
  31. data/lib/action_view/helpers/tag_helper.rb +194 -77
  32. data/lib/action_view/helpers/tags/base.rb +121 -102
  33. data/lib/action_view/helpers/tags/check_box.rb +17 -17
  34. data/lib/action_view/helpers/tags/collection_check_boxes.rb +9 -8
  35. data/lib/action_view/helpers/tags/collection_helpers.rb +60 -60
  36. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +3 -2
  37. data/lib/action_view/helpers/tags/collection_select.rb +2 -2
  38. data/lib/action_view/helpers/tags/date_select.rb +36 -36
  39. data/lib/action_view/helpers/tags/grouped_collection_select.rb +2 -2
  40. data/lib/action_view/helpers/tags/label.rb +4 -0
  41. data/lib/action_view/helpers/tags/password_field.rb +1 -1
  42. data/lib/action_view/helpers/tags/radio_button.rb +4 -4
  43. data/lib/action_view/helpers/tags/select.rb +9 -9
  44. data/lib/action_view/helpers/tags/text_area.rb +1 -1
  45. data/lib/action_view/helpers/tags/text_field.rb +5 -5
  46. data/lib/action_view/helpers/tags/translator.rb +14 -12
  47. data/lib/action_view/helpers/text_helper.rb +20 -19
  48. data/lib/action_view/helpers/translation_helper.rb +6 -6
  49. data/lib/action_view/helpers/url_helper.rb +48 -46
  50. data/lib/action_view/helpers.rb +1 -1
  51. data/lib/action_view/layouts.rb +51 -47
  52. data/lib/action_view/log_subscriber.rb +25 -9
  53. data/lib/action_view/lookup_context.rb +19 -25
  54. data/lib/action_view/path_set.rb +19 -19
  55. data/lib/action_view/railtie.rb +13 -4
  56. data/lib/action_view/record_identifier.rb +6 -6
  57. data/lib/action_view/renderer/abstract_renderer.rb +17 -17
  58. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +7 -1
  59. data/lib/action_view/renderer/partial_renderer.rb +188 -187
  60. data/lib/action_view/renderer/renderer.rb +4 -0
  61. data/lib/action_view/renderer/streaming_template_renderer.rb +45 -47
  62. data/lib/action_view/renderer/template_renderer.rb +64 -66
  63. data/lib/action_view/rendering.rb +4 -5
  64. data/lib/action_view/routing_url_for.rb +9 -13
  65. data/lib/action_view/tasks/cache_digests.rake +7 -7
  66. data/lib/action_view/template/error.rb +5 -15
  67. data/lib/action_view/template/handlers/builder.rb +7 -7
  68. data/lib/action_view/template/handlers/erb/deprecated_erubis.rb +9 -0
  69. data/lib/action_view/template/handlers/erb/erubi.rb +81 -0
  70. data/lib/action_view/template/handlers/erb/erubis.rb +81 -0
  71. data/lib/action_view/template/handlers/erb.rb +9 -76
  72. data/lib/action_view/template/handlers.rb +4 -4
  73. data/lib/action_view/template/html.rb +2 -4
  74. data/lib/action_view/template/resolver.rb +107 -90
  75. data/lib/action_view/template/text.rb +5 -8
  76. data/lib/action_view/template/types.rb +1 -1
  77. data/lib/action_view/template.rb +26 -27
  78. data/lib/action_view/test_case.rb +20 -21
  79. data/lib/action_view/testing/resolvers.rb +29 -30
  80. data/lib/action_view/version.rb +1 -1
  81. data/lib/action_view/view_paths.rb +20 -8
  82. data/lib/action_view.rb +5 -5
  83. data/lib/assets/compiled/rails-ujs.js +683 -0
  84. metadata +18 -12
@@ -1,39 +1,207 @@
1
- require 'active_support/core_ext/string/output_safety'
2
- require 'set'
1
+ # frozen-string-literal: true
2
+
3
+ require "active_support/core_ext/string/output_safety"
4
+ require "set"
3
5
 
4
6
  module ActionView
5
7
  # = Action View Tag Helpers
6
8
  module Helpers #:nodoc:
7
- # Provides methods to generate HTML tags programmatically when you can't use
8
- # a Builder. By default, they output XHTML compliant tags.
9
+ # Provides methods to generate HTML tags programmatically both as a modern
10
+ # HTML5 compliant builder style and legacy XHTML compliant tags.
9
11
  module TagHelper
10
12
  extend ActiveSupport::Concern
11
13
  include CaptureHelper
12
14
  include OutputSafetyHelper
13
15
 
14
- BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
15
- autoplay controls loop selected hidden scoped async
16
- defer reversed ismap seamless muted required
17
- autofocus novalidate formnovalidate open pubdate
18
- itemscope allowfullscreen default inert sortable
19
- truespeed typemustmatch).to_set
16
+ BOOLEAN_ATTRIBUTES = %w(allowfullscreen async autofocus autoplay checked
17
+ compact controls declare default defaultchecked
18
+ defaultmuted defaultselected defer disabled
19
+ enabled formnovalidate hidden indeterminate inert
20
+ ismap itemscope loop multiple muted nohref
21
+ noresize noshade novalidate nowrap open
22
+ pauseonexit readonly required reversed scoped
23
+ seamless selected sortable truespeed typemustmatch
24
+ visible).to_set
20
25
 
21
26
  BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map(&:to_sym))
22
27
 
23
- TAG_PREFIXES = ['aria', 'data', :aria, :data].to_set
28
+ TAG_PREFIXES = ["aria", "data", :aria, :data].to_set
24
29
 
25
- PRE_CONTENT_STRINGS = Hash.new { "".freeze }
30
+ PRE_CONTENT_STRINGS = Hash.new { "" }
26
31
  PRE_CONTENT_STRINGS[:textarea] = "\n"
27
32
  PRE_CONTENT_STRINGS["textarea"] = "\n"
28
33
 
34
+ class TagBuilder #:nodoc:
35
+ include CaptureHelper
36
+ include OutputSafetyHelper
37
+
38
+ VOID_ELEMENTS = %i(area base br col embed hr img input keygen link meta param source track wbr).to_set
39
+
40
+ def initialize(view_context)
41
+ @view_context = view_context
42
+ end
43
+
44
+ def tag_string(name, content = nil, escape_attributes: true, **options, &block)
45
+ content = @view_context.capture(self, &block) if block_given?
46
+ if VOID_ELEMENTS.include?(name) && content.nil?
47
+ "<#{name.to_s.dasherize}#{tag_options(options, escape_attributes)}>".html_safe
48
+ else
49
+ content_tag_string(name.to_s.dasherize, content || "", options, escape_attributes)
50
+ end
51
+ end
52
+
53
+ def content_tag_string(name, content, options, escape = true)
54
+ tag_options = tag_options(options, escape) if options
55
+ content = ERB::Util.unwrapped_html_escape(content) if escape
56
+ "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name]}#{content}</#{name}>".html_safe
57
+ end
58
+
59
+ def tag_options(options, escape = true)
60
+ return if options.blank?
61
+ output = "".dup
62
+ sep = " "
63
+ options.each_pair do |key, value|
64
+ if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
65
+ value.each_pair do |k, v|
66
+ next if v.nil?
67
+ output << sep
68
+ output << prefix_tag_option(key, k, v, escape)
69
+ end
70
+ elsif BOOLEAN_ATTRIBUTES.include?(key)
71
+ if value
72
+ output << sep
73
+ output << boolean_tag_option(key)
74
+ end
75
+ elsif !value.nil?
76
+ output << sep
77
+ output << tag_option(key, value, escape)
78
+ end
79
+ end
80
+ output unless output.empty?
81
+ end
82
+
83
+ def boolean_tag_option(key)
84
+ %(#{key}="#{key}")
85
+ end
29
86
 
30
- # Returns an empty HTML tag of type +name+ which by default is XHTML
87
+ def tag_option(key, value, escape)
88
+ if value.is_a?(Array)
89
+ value = escape ? safe_join(value, " ".freeze) : value.join(" ".freeze)
90
+ else
91
+ value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
92
+ end
93
+ %(#{key}="#{value.gsub('"'.freeze, '&quot;'.freeze)}")
94
+ end
95
+
96
+ private
97
+ def prefix_tag_option(prefix, key, value, escape)
98
+ key = "#{prefix}-#{key.to_s.dasherize}"
99
+ unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
100
+ value = value.to_json
101
+ end
102
+ tag_option(key, value, escape)
103
+ end
104
+
105
+ def respond_to_missing?(*args)
106
+ true
107
+ end
108
+
109
+ def method_missing(called, *args, &block)
110
+ tag_string(called, *args, &block)
111
+ end
112
+ end
113
+
114
+ # Returns an HTML tag.
115
+ #
116
+ # === Building HTML tags
117
+ #
118
+ # Builds HTML5 compliant tags with a tag proxy. Every tag can be built with:
119
+ #
120
+ # tag.<tag name>(optional content, options)
121
+ #
122
+ # where tag name can be e.g. br, div, section, article, or any tag really.
123
+ #
124
+ # ==== Passing content
125
+ #
126
+ # Tags can pass content to embed within it:
127
+ #
128
+ # tag.h1 'All titles fit to print' # => <h1>All titles fit to print</h1>
129
+ #
130
+ # tag.div tag.p('Hello world!') # => <div><p>Hello world!</p></div>
131
+ #
132
+ # Content can also be captured with a block, which is useful in templates:
133
+ #
134
+ # <%= tag.p do %>
135
+ # The next great American novel starts here.
136
+ # <% end %>
137
+ # # => <p>The next great American novel starts here.</p>
138
+ #
139
+ # ==== Options
140
+ #
141
+ # Use symbol keyed options to add attributes to the generated tag.
142
+ #
143
+ # tag.section class: %w( kitties puppies )
144
+ # # => <section class="kitties puppies"></section>
145
+ #
146
+ # tag.section id: dom_id(@post)
147
+ # # => <section id="<generated dom id>"></section>
148
+ #
149
+ # Pass +true+ for any attributes that can render with no values, like +disabled+ and +readonly+.
150
+ #
151
+ # tag.input type: 'text', disabled: true
152
+ # # => <input type="text" disabled="disabled">
153
+ #
154
+ # HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
155
+ # pointing to a hash of sub-attributes.
156
+ #
157
+ # To play nicely with JavaScript conventions, sub-attributes are dasherized.
158
+ #
159
+ # tag.article data: { user_id: 123 }
160
+ # # => <article data-user-id="123"></article>
161
+ #
162
+ # Thus <tt>data-user-id</tt> can be accessed as <tt>dataset.userId</tt>.
163
+ #
164
+ # Data attribute values are encoded to JSON, with the exception of strings, symbols and
165
+ # BigDecimals.
166
+ # This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
167
+ # from 1.4.3.
168
+ #
169
+ # tag.div data: { city_state: %w( Chigaco IL ) }
170
+ # # => <div data-city-state="[&quot;Chicago&quot;,&quot;IL&quot;]"></div>
171
+ #
172
+ # The generated attributes are escaped by default. This can be disabled using
173
+ # +escape_attributes+.
174
+ #
175
+ # tag.img src: 'open & shut.png'
176
+ # # => <img src="open &amp; shut.png">
177
+ #
178
+ # tag.img src: 'open & shut.png', escape_attributes: false
179
+ # # => <img src="open & shut.png">
180
+ #
181
+ # The tag builder respects
182
+ # {HTML5 void elements}[https://www.w3.org/TR/html5/syntax.html#void-elements]
183
+ # if no content is passed, and omits closing tags for those elements.
184
+ #
185
+ # # A standard element:
186
+ # tag.div # => <div></div>
187
+ #
188
+ # # A void element:
189
+ # tag.br # => <br>
190
+ #
191
+ # === Legacy syntax
192
+ #
193
+ # The following format is for legacy syntax support. It will be deprecated in future versions of Rails.
194
+ #
195
+ # tag(name, options = nil, open = false, escape = true)
196
+ #
197
+ # It returns an empty HTML tag of type +name+ which by default is XHTML
31
198
  # compliant. Set +open+ to true to create an open tag compatible
32
199
  # with HTML 4.0 and below. Add HTML attributes by passing an attributes
33
200
  # hash to +options+. Set +escape+ to false to disable attribute value
34
201
  # escaping.
35
202
  #
36
203
  # ==== Options
204
+ #
37
205
  # You can use symbols or strings for the attribute names.
38
206
  #
39
207
  # Use +true+ with boolean attributes that can render with no value, like
@@ -42,16 +210,8 @@ module ActionView
42
210
  # HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
43
211
  # pointing to a hash of sub-attributes.
44
212
  #
45
- # To play nicely with JavaScript conventions sub-attributes are dasherized.
46
- # For example, a key +user_id+ would render as <tt>data-user-id</tt> and
47
- # thus accessed as <tt>dataset.userId</tt>.
48
- #
49
- # Values are encoded to JSON, with the exception of strings, symbols and
50
- # BigDecimals.
51
- # This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
52
- # from 1.4.3.
53
- #
54
213
  # ==== Examples
214
+ #
55
215
  # tag("br")
56
216
  # # => <br />
57
217
  #
@@ -72,8 +232,12 @@ module ActionView
72
232
  #
73
233
  # tag("div", data: {name: 'Stephen', city_state: %w(Chicago IL)})
74
234
  # # => <div data-name="Stephen" data-city-state="[&quot;Chicago&quot;,&quot;IL&quot;]" />
75
- def tag(name, options = nil, open = false, escape = true)
76
- "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
235
+ def tag(name = nil, options = nil, open = false, escape = true)
236
+ if name.nil?
237
+ tag_builder
238
+ else
239
+ "<#{name}#{tag_builder.tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
240
+ end
77
241
  end
78
242
 
79
243
  # Returns an HTML block tag of type +name+ surrounding the +content+. Add
@@ -81,6 +245,7 @@ module ActionView
81
245
  # Instead of passing the content as an argument, you can also use a block
82
246
  # in which case, you pass your +options+ as the second parameter.
83
247
  # Set escape to false to disable attribute value escaping.
248
+ # Note: this is legacy syntax, see +tag+ method description for details.
84
249
  #
85
250
  # ==== Options
86
251
  # The +options+ hash can be used with attributes with no value like (<tt>disabled</tt> and
@@ -104,9 +269,9 @@ module ActionView
104
269
  def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
105
270
  if block_given?
106
271
  options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
107
- content_tag_string(name, capture(&block), options, escape)
272
+ tag_builder.content_tag_string(name, capture(&block), options, escape)
108
273
  else
109
- content_tag_string(name, content_or_options_with_block, options, escape)
274
+ tag_builder.content_tag_string(name, content_or_options_with_block, options, escape)
110
275
  end
111
276
  end
112
277
 
@@ -124,7 +289,7 @@ module ActionView
124
289
  # cdata_section("hello]]>world")
125
290
  # # => <![CDATA[hello]]]]><![CDATA[>world]]>
126
291
  def cdata_section(content)
127
- splitted = content.to_s.gsub(/\]\]\>/, ']]]]><![CDATA[>')
292
+ splitted = content.to_s.gsub(/\]\]\>/, "]]]]><![CDATA[>")
128
293
  "<![CDATA[#{splitted}]]>".html_safe
129
294
  end
130
295
 
@@ -140,56 +305,8 @@ module ActionView
140
305
  end
141
306
 
142
307
  private
143
-
144
- def content_tag_string(name, content, options, escape = true)
145
- tag_options = tag_options(options, escape) if options
146
- content = ERB::Util.unwrapped_html_escape(content) if escape
147
- "<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name]}#{content}</#{name}>".html_safe
148
- end
149
-
150
- def tag_options(options, escape = true)
151
- return if options.blank?
152
- output = ""
153
- sep = " ".freeze
154
- options.each_pair do |key, value|
155
- if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
156
- value.each_pair do |k, v|
157
- next if v.nil?
158
- output << sep
159
- output << prefix_tag_option(key, k, v, escape)
160
- end
161
- elsif BOOLEAN_ATTRIBUTES.include?(key)
162
- if value
163
- output << sep
164
- output << boolean_tag_option(key)
165
- end
166
- elsif !value.nil?
167
- output << sep
168
- output << tag_option(key, value, escape)
169
- end
170
- end
171
- output unless output.empty?
172
- end
173
-
174
- def prefix_tag_option(prefix, key, value, escape)
175
- key = "#{prefix}-#{key.to_s.dasherize}"
176
- unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
177
- value = value.to_json
178
- end
179
- tag_option(key, value, escape)
180
- end
181
-
182
- def boolean_tag_option(key)
183
- %(#{key}="#{key}")
184
- end
185
-
186
- def tag_option(key, value, escape)
187
- if value.is_a?(Array)
188
- value = escape ? safe_join(value, " ".freeze) : value.join(" ".freeze)
189
- else
190
- value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
191
- end
192
- %(#{key}="#{value.gsub('"'.freeze, '&quot;'.freeze)}")
308
+ def tag_builder
309
+ @tag_builder ||= TagBuilder.new(self)
193
310
  end
194
311
  end
195
312
  end
@@ -11,8 +11,10 @@ module ActionView
11
11
  @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
12
12
  @template_object = template_object
13
13
 
14
- @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
14
+ @object_name.sub!(/\[\]$/, "") || @object_name.sub!(/\[\]\]$/, "]")
15
15
  @object = retrieve_object(options.delete(:object))
16
+ @skip_default_ids = options.delete(:skip_default_ids)
17
+ @allow_method_names_outside_object = options.delete(:allow_method_names_outside_object)
16
18
  @options = options
17
19
 
18
20
  if Regexp.last_match
@@ -31,140 +33,157 @@ module ActionView
31
33
 
32
34
  private
33
35
 
34
- def value(object)
35
- object.public_send @method_name if object
36
- end
36
+ def value(object)
37
+ if @allow_method_names_outside_object
38
+ object.public_send @method_name if object && object.respond_to?(@method_name)
39
+ else
40
+ object.public_send @method_name if object
41
+ end
42
+ end
37
43
 
38
- def value_before_type_cast(object)
39
- unless object.nil?
40
- method_before_type_cast = @method_name + "_before_type_cast"
44
+ def value_before_type_cast(object)
45
+ unless object.nil?
46
+ method_before_type_cast = @method_name + "_before_type_cast"
41
47
 
42
- if value_came_from_user?(object) && object.respond_to?(method_before_type_cast)
43
- object.public_send(method_before_type_cast)
44
- else
45
- value(object)
48
+ if value_came_from_user?(object) && object.respond_to?(method_before_type_cast)
49
+ object.public_send(method_before_type_cast)
50
+ else
51
+ value(object)
52
+ end
46
53
  end
47
54
  end
48
- end
49
55
 
50
- def value_came_from_user?(object)
51
- method_name = "#{@method_name}_came_from_user?"
52
- !object.respond_to?(method_name) || object.public_send(method_name)
53
- end
56
+ def value_came_from_user?(object)
57
+ method_name = "#{@method_name}_came_from_user?"
58
+ !object.respond_to?(method_name) || object.public_send(method_name)
59
+ end
54
60
 
55
- def retrieve_object(object)
56
- if object
57
- object
58
- elsif @template_object.instance_variable_defined?("@#{@object_name}")
59
- @template_object.instance_variable_get("@#{@object_name}")
61
+ def retrieve_object(object)
62
+ if object
63
+ object
64
+ elsif @template_object.instance_variable_defined?("@#{@object_name}")
65
+ @template_object.instance_variable_get("@#{@object_name}")
66
+ end
67
+ rescue NameError
68
+ # As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
69
+ nil
60
70
  end
61
- rescue NameError
62
- # As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
63
- nil
64
- end
65
71
 
66
- def retrieve_autoindex(pre_match)
67
- object = self.object || @template_object.instance_variable_get("@#{pre_match}")
68
- if object && object.respond_to?(:to_param)
69
- object.to_param
70
- else
71
- raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
72
+ def retrieve_autoindex(pre_match)
73
+ object = self.object || @template_object.instance_variable_get("@#{pre_match}")
74
+ if object && object.respond_to?(:to_param)
75
+ object.to_param
76
+ else
77
+ raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
78
+ end
72
79
  end
73
- end
74
80
 
75
- def add_default_name_and_id_for_value(tag_value, options)
76
- if tag_value.nil?
77
- add_default_name_and_id(options)
78
- else
79
- specified_id = options["id"]
80
- add_default_name_and_id(options)
81
+ def add_default_name_and_id_for_value(tag_value, options)
82
+ if tag_value.nil?
83
+ add_default_name_and_id(options)
84
+ else
85
+ specified_id = options["id"]
86
+ add_default_name_and_id(options)
81
87
 
82
- if specified_id.blank? && options["id"].present?
83
- options["id"] += "_#{sanitized_value(tag_value)}"
88
+ if specified_id.blank? && options["id"].present?
89
+ options["id"] += "_#{sanitized_value(tag_value)}"
90
+ end
84
91
  end
85
92
  end
86
- end
87
93
 
88
- def add_default_name_and_id(options)
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
94
+ def add_default_name_and_id(options)
95
+ index = name_and_id_index(options)
96
+ options["name"] = options.fetch("name") { tag_name(options["multiple"], index) }
97
+
98
+ unless skip_default_ids?
99
+ options["id"] = options.fetch("id") { tag_id(index) }
100
+ if namespace = options.delete("namespace")
101
+ options["id"] = options["id"] ? "#{namespace}_#{options['id']}" : namespace
102
+ end
103
+ end
94
104
  end
95
- end
96
105
 
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}"
106
+ def tag_name(multiple = false, index = nil)
107
+ # a little duplication to construct less strings
108
+ case
109
+ when @object_name.empty?
110
+ "#{sanitized_method_name}#{"[]" if multiple}"
111
+ when index
112
+ "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
113
+ else
114
+ "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
115
+ end
103
116
  end
104
- end
105
117
 
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}"
118
+ def tag_id(index = nil)
119
+ # a little duplication to construct less strings
120
+ case
121
+ when @object_name.empty?
122
+ sanitized_method_name.dup
123
+ when index
124
+ "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
125
+ else
126
+ "#{sanitized_object_name}_#{sanitized_method_name}"
127
+ end
112
128
  end
113
- end
114
129
 
115
- def sanitized_object_name
116
- @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
117
- end
130
+ def sanitized_object_name
131
+ @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
132
+ end
118
133
 
119
- def sanitized_method_name
120
- @sanitized_method_name ||= @method_name.sub(/\?$/,"")
121
- end
134
+ def sanitized_method_name
135
+ @sanitized_method_name ||= @method_name.sub(/\?$/, "")
136
+ end
122
137
 
123
- def sanitized_value(value)
124
- value.to_s.gsub(/\s/, "_").gsub(/[^-[[:word:]]]/, "").mb_chars.downcase.to_s
125
- end
138
+ def sanitized_value(value)
139
+ value.to_s.gsub(/\s/, "_").gsub(/[^-[[:word:]]]/, "").mb_chars.downcase.to_s
140
+ end
126
141
 
127
- def select_content_tag(option_tags, options, html_options)
128
- html_options = html_options.stringify_keys
129
- add_default_name_and_id(html_options)
142
+ def select_content_tag(option_tags, options, html_options)
143
+ html_options = html_options.stringify_keys
144
+ add_default_name_and_id(html_options)
130
145
 
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
146
+ if placeholder_required?(html_options)
147
+ raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
148
+ options[:include_blank] ||= true unless options[:prompt]
149
+ end
135
150
 
136
- value = options.fetch(:selected) { value(object) }
137
- select = content_tag("select", add_options(option_tags, options, value), html_options)
151
+ value = options.fetch(:selected) { value(object) }
152
+ select = content_tag("select", add_options(option_tags, options, value), html_options.except!("skip_default_ids", "allow_method_names_outside_object"))
138
153
 
139
- if html_options["multiple"] && options.fetch(:include_hidden, true)
140
- tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
141
- else
142
- select
154
+ if html_options["multiple"] && options.fetch(:include_hidden, true)
155
+ tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "") + select
156
+ else
157
+ select
158
+ end
143
159
  end
144
- end
145
160
 
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
149
- end
161
+ def placeholder_required?(html_options)
162
+ # See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
163
+ html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
164
+ end
150
165
 
151
- def add_options(option_tags, options, value = nil)
152
- if options[:include_blank]
153
- option_tags = content_tag_string('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags
166
+ def add_options(option_tags, options, value = nil)
167
+ if options[:include_blank]
168
+ option_tags = tag_builder.content_tag_string("option", options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, value: "") + "\n" + option_tags
169
+ end
170
+ if value.blank? && options[:prompt]
171
+ option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), value: "") + "\n" + option_tags
172
+ end
173
+ option_tags
154
174
  end
155
- if value.blank? && options[:prompt]
156
- option_tags = content_tag_string('option', prompt_text(options[:prompt]), :value => '') + "\n" + option_tags
175
+
176
+ def name_and_id_index(options)
177
+ if options.key?("index")
178
+ options.delete("index") || ""
179
+ elsif @generate_indexed_names
180
+ @auto_index || ""
181
+ end
157
182
  end
158
- option_tags
159
- end
160
183
 
161
- def name_and_id_index(options)
162
- if options.key?("index")
163
- options.delete("index") || ""
164
- elsif @generate_indexed_names
165
- @auto_index || ""
184
+ def skip_default_ids?
185
+ @skip_default_ids
166
186
  end
167
- end
168
187
  end
169
188
  end
170
189
  end
@@ -1,4 +1,4 @@
1
- require 'action_view/helpers/tags/checkable'
1
+ require "action_view/helpers/tags/checkable"
2
2
 
3
3
  module ActionView
4
4
  module Helpers
@@ -38,26 +38,26 @@ module ActionView
38
38
 
39
39
  private
40
40
 
41
- def checked?(value)
42
- case value
43
- when TrueClass, FalseClass
44
- value == !!@checked_value
45
- when NilClass
46
- false
47
- when String
48
- value == @checked_value
49
- else
50
- if value.respond_to?(:include?)
51
- value.include?(@checked_value)
41
+ def checked?(value)
42
+ case value
43
+ when TrueClass, FalseClass
44
+ value == !!@checked_value
45
+ when NilClass
46
+ false
47
+ when String
48
+ value == @checked_value
52
49
  else
53
- value.to_i == @checked_value.to_i
50
+ if value.respond_to?(:include?)
51
+ value.include?(@checked_value)
52
+ else
53
+ value.to_i == @checked_value.to_i
54
+ end
54
55
  end
55
56
  end
56
- end
57
57
 
58
- def hidden_field_for_checkbox(options)
59
- @unchecked_value ? tag("input", options.slice("name", "disabled", "form").merge!("type" => "hidden", "value" => @unchecked_value)) : "".html_safe
60
- end
58
+ def hidden_field_for_checkbox(options)
59
+ @unchecked_value ? tag("input", options.slice("name", "disabled", "form").merge!("type" => "hidden", "value" => @unchecked_value)) : "".html_safe
60
+ end
61
61
  end
62
62
  end
63
63
  end