actionview 4.1.13 → 6.1.3.1

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

Potentially problematic release.


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

Files changed (124) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +181 -359
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +12 -6
  5. data/lib/action_view/base.rb +115 -43
  6. data/lib/action_view/buffers.rb +22 -4
  7. data/lib/action_view/cache_expiry.rb +52 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker.rb +61 -21
  10. data/lib/action_view/digestor.rb +89 -84
  11. data/lib/action_view/flows.rb +12 -13
  12. data/lib/action_view/gem_version.rb +6 -4
  13. data/lib/action_view/helpers/active_model_helper.rb +16 -11
  14. data/lib/action_view/helpers/asset_tag_helper.rb +311 -105
  15. data/lib/action_view/helpers/asset_url_helper.rb +197 -80
  16. data/lib/action_view/helpers/atom_feed_helper.rb +20 -17
  17. data/lib/action_view/helpers/cache_helper.rb +109 -45
  18. data/lib/action_view/helpers/capture_helper.rb +20 -22
  19. data/lib/action_view/helpers/controller_helper.rb +15 -4
  20. data/lib/action_view/helpers/csp_helper.rb +26 -0
  21. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  22. data/lib/action_view/helpers/date_helper.rb +245 -140
  23. data/lib/action_view/helpers/debug_helper.rb +14 -17
  24. data/lib/action_view/helpers/form_helper.rb +875 -148
  25. data/lib/action_view/helpers/form_options_helper.rb +128 -82
  26. data/lib/action_view/helpers/form_tag_helper.rb +253 -91
  27. data/lib/action_view/helpers/javascript_helper.rb +37 -15
  28. data/lib/action_view/helpers/number_helper.rb +100 -77
  29. data/lib/action_view/helpers/output_safety_helper.rb +42 -10
  30. data/lib/action_view/helpers/rendering_helper.rb +26 -15
  31. data/lib/action_view/helpers/sanitize_helper.rb +79 -164
  32. data/lib/action_view/helpers/tag_helper.rb +277 -64
  33. data/lib/action_view/helpers/tags/base.rb +143 -92
  34. data/lib/action_view/helpers/tags/check_box.rb +20 -19
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -30
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  39. data/lib/action_view/helpers/tags/collection_select.rb +4 -2
  40. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  41. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  42. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  43. data/lib/action_view/helpers/tags/datetime_field.rb +14 -5
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  49. data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
  50. data/lib/action_view/helpers/tags/label.rb +41 -22
  51. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  54. data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
  55. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +3 -0
  58. data/lib/action_view/helpers/tags/select.rb +11 -10
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +7 -1
  61. data/lib/action_view/helpers/tags/text_field.rb +11 -7
  62. data/lib/action_view/helpers/tags/time_field.rb +3 -2
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +39 -0
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  68. data/lib/action_view/helpers/tags.rb +4 -1
  69. data/lib/action_view/helpers/text_helper.rb +80 -45
  70. data/lib/action_view/helpers/translation_helper.rb +148 -67
  71. data/lib/action_view/helpers/url_helper.rb +289 -147
  72. data/lib/action_view/helpers.rb +5 -3
  73. data/lib/action_view/layouts.rb +68 -63
  74. data/lib/action_view/log_subscriber.rb +80 -13
  75. data/lib/action_view/lookup_context.rb +137 -92
  76. data/lib/action_view/model_naming.rb +4 -2
  77. data/lib/action_view/path_set.rb +30 -16
  78. data/lib/action_view/railtie.rb +62 -13
  79. data/lib/action_view/record_identifier.rb +53 -26
  80. data/lib/action_view/renderer/abstract_renderer.rb +152 -13
  81. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  82. data/lib/action_view/renderer/object_renderer.rb +34 -0
  83. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  84. data/lib/action_view/renderer/partial_renderer.rb +61 -261
  85. data/lib/action_view/renderer/renderer.rb +67 -6
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +58 -54
  87. data/lib/action_view/renderer/template_renderer.rb +83 -75
  88. data/lib/action_view/rendering.rb +73 -46
  89. data/lib/action_view/routing_url_for.rb +54 -17
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template/error.rb +44 -29
  92. data/lib/action_view/template/handlers/builder.rb +12 -13
  93. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  94. data/lib/action_view/template/handlers/erb.rb +23 -89
  95. data/lib/action_view/template/handlers/html.rb +11 -0
  96. data/lib/action_view/template/handlers/raw.rb +4 -4
  97. data/lib/action_view/template/handlers.rb +22 -9
  98. data/lib/action_view/template/html.rb +10 -11
  99. data/lib/action_view/template/inline.rb +22 -0
  100. data/lib/action_view/template/raw_file.rb +25 -0
  101. data/lib/action_view/template/renderable.rb +24 -0
  102. data/lib/action_view/template/resolver.rb +267 -181
  103. data/lib/action_view/template/sources/file.rb +17 -0
  104. data/lib/action_view/template/sources.rb +13 -0
  105. data/lib/action_view/template/text.rb +8 -10
  106. data/lib/action_view/template/types.rb +18 -18
  107. data/lib/action_view/template.rb +109 -99
  108. data/lib/action_view/test_case.rb +73 -53
  109. data/lib/action_view/testing/resolvers.rb +24 -33
  110. data/lib/action_view/unbound_template.rb +31 -0
  111. data/lib/action_view/version.rb +3 -1
  112. data/lib/action_view/view_paths.rb +74 -44
  113. data/lib/action_view.rb +14 -9
  114. data/lib/assets/compiled/rails-ujs.js +746 -0
  115. metadata +71 -26
  116. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  117. data/lib/action_view/tasks/dependencies.rake +0 -23
  118. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  119. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  120. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  121. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  122. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  123. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
  124. data/lib/action_view/vendor/html-scanner.rb +0 -20
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  module Helpers
3
5
  module Tags # :nodoc:
@@ -11,10 +13,19 @@ module ActionView
11
13
  @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
12
14
  @template_object = template_object
13
15
 
14
- @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
16
+ @object_name.sub!(/\[\]$/, "") || @object_name.sub!(/\[\]\]$/, "]")
15
17
  @object = retrieve_object(options.delete(:object))
18
+ @skip_default_ids = options.delete(:skip_default_ids)
19
+ @allow_method_names_outside_object = options.delete(:allow_method_names_outside_object)
16
20
  @options = options
17
- @auto_index = retrieve_autoindex(Regexp.last_match.pre_match) if Regexp.last_match
21
+
22
+ if Regexp.last_match
23
+ @generate_indexed_names = true
24
+ @auto_index = retrieve_autoindex(Regexp.last_match.pre_match)
25
+ else
26
+ @generate_indexed_names = false
27
+ @auto_index = nil
28
+ end
18
29
  end
19
30
 
20
31
  # This is what child classes implement.
@@ -23,125 +34,165 @@ module ActionView
23
34
  end
24
35
 
25
36
  private
37
+ def value
38
+ if @allow_method_names_outside_object
39
+ object.public_send @method_name if object && object.respond_to?(@method_name)
40
+ else
41
+ object.public_send @method_name if object
42
+ end
43
+ end
26
44
 
27
- def value(object)
28
- object.public_send @method_name if object
29
- end
45
+ def value_before_type_cast
46
+ unless object.nil?
47
+ method_before_type_cast = @method_name + "_before_type_cast"
30
48
 
31
- def value_before_type_cast(object)
32
- unless object.nil?
33
- method_before_type_cast = @method_name + "_before_type_cast"
49
+ if value_came_from_user? && object.respond_to?(method_before_type_cast)
50
+ object.public_send(method_before_type_cast)
51
+ else
52
+ value
53
+ end
54
+ end
55
+ end
34
56
 
35
- object.respond_to?(method_before_type_cast) ?
36
- object.send(method_before_type_cast) :
37
- value(object)
57
+ def value_came_from_user?
58
+ method_name = "#{@method_name}_came_from_user?"
59
+ !object.respond_to?(method_name) || object.public_send(method_name)
38
60
  end
39
- end
40
61
 
41
- def retrieve_object(object)
42
- if object
43
- object
44
- elsif @template_object.instance_variable_defined?("@#{@object_name}")
45
- @template_object.instance_variable_get("@#{@object_name}")
62
+ def retrieve_object(object)
63
+ if object
64
+ object
65
+ elsif @template_object.instance_variable_defined?("@#{@object_name}")
66
+ @template_object.instance_variable_get("@#{@object_name}")
67
+ end
68
+ rescue NameError
69
+ # As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
70
+ nil
46
71
  end
47
- rescue NameError
48
- # As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
49
- nil
50
- end
51
72
 
52
- def retrieve_autoindex(pre_match)
53
- object = self.object || @template_object.instance_variable_get("@#{pre_match}")
54
- if object && object.respond_to?(:to_param)
55
- object.to_param
56
- else
57
- raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
73
+ def retrieve_autoindex(pre_match)
74
+ object = self.object || @template_object.instance_variable_get("@#{pre_match}")
75
+ if object && object.respond_to?(:to_param)
76
+ object.to_param
77
+ else
78
+ raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
79
+ end
58
80
  end
59
- end
60
81
 
61
- def add_default_name_and_id_for_value(tag_value, options)
62
- if tag_value.nil?
63
- add_default_name_and_id(options)
64
- else
65
- specified_id = options["id"]
66
- add_default_name_and_id(options)
82
+ def add_default_name_and_id_for_value(tag_value, options)
83
+ if tag_value.nil?
84
+ add_default_name_and_id(options)
85
+ else
86
+ specified_id = options["id"]
87
+ add_default_name_and_id(options)
67
88
 
68
- if specified_id.blank? && options["id"].present?
69
- options["id"] += "_#{sanitized_value(tag_value)}"
89
+ if specified_id.blank? && options["id"].present?
90
+ options["id"] += "_#{sanitized_value(tag_value)}"
91
+ end
70
92
  end
71
93
  end
72
- end
73
94
 
74
- def add_default_name_and_id(options)
75
- if options.has_key?("index")
76
- options["name"] ||= options.fetch("name"){ tag_name_with_index(options["index"], options["multiple"]) }
77
- options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) }
78
- options.delete("index")
79
- elsif defined?(@auto_index)
80
- options["name"] ||= options.fetch("name"){ tag_name_with_index(@auto_index, options["multiple"]) }
81
- options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) }
82
- else
83
- options["name"] ||= options.fetch("name"){ tag_name(options["multiple"]) }
84
- options["id"] = options.fetch("id"){ tag_id }
95
+ def add_default_name_and_id(options)
96
+ index = name_and_id_index(options)
97
+ options["name"] = options.fetch("name") { tag_name(options["multiple"], index) }
98
+
99
+ if generate_ids?
100
+ options["id"] = options.fetch("id") { tag_id(index) }
101
+ if namespace = options.delete("namespace")
102
+ options["id"] = options["id"] ? "#{namespace}_#{options['id']}" : namespace
103
+ end
104
+ end
85
105
  end
86
106
 
87
- options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence
88
- end
107
+ def tag_name(multiple = false, index = nil)
108
+ # a little duplication to construct fewer strings
109
+ case
110
+ when @object_name.empty?
111
+ "#{sanitized_method_name}#{multiple ? "[]" : ""}"
112
+ when index
113
+ "#{@object_name}[#{index}][#{sanitized_method_name}]#{multiple ? "[]" : ""}"
114
+ else
115
+ "#{@object_name}[#{sanitized_method_name}]#{multiple ? "[]" : ""}"
116
+ end
117
+ end
89
118
 
90
- def tag_name(multiple = false)
91
- "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
92
- end
119
+ def tag_id(index = nil)
120
+ # a little duplication to construct fewer strings
121
+ case
122
+ when @object_name.empty?
123
+ sanitized_method_name.dup
124
+ when index
125
+ "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
126
+ else
127
+ "#{sanitized_object_name}_#{sanitized_method_name}"
128
+ end
129
+ end
93
130
 
94
- def tag_name_with_index(index, multiple = false)
95
- "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
96
- end
131
+ def sanitized_object_name
132
+ @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").delete_suffix("_")
133
+ end
97
134
 
98
- def tag_id
99
- "#{sanitized_object_name}_#{sanitized_method_name}"
100
- end
135
+ def sanitized_method_name
136
+ @sanitized_method_name ||= @method_name.delete_suffix("?")
137
+ end
101
138
 
102
- def tag_id_with_index(index)
103
- "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
104
- end
139
+ def sanitized_value(value)
140
+ value.to_s.gsub(/[\s\.]/, "_").gsub(/[^-[[:word:]]]/, "").downcase
141
+ end
105
142
 
106
- def sanitized_object_name
107
- @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
108
- end
143
+ def select_content_tag(option_tags, options, html_options)
144
+ html_options = html_options.stringify_keys
145
+ add_default_name_and_id(html_options)
109
146
 
110
- def sanitized_method_name
111
- @sanitized_method_name ||= @method_name.sub(/\?$/,"")
112
- end
147
+ if placeholder_required?(html_options)
148
+ raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
149
+ options[:include_blank] ||= true unless options[:prompt]
150
+ end
113
151
 
114
- def sanitized_value(value)
115
- value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase
116
- end
152
+ value = options.fetch(:selected) { value() }
153
+ select = content_tag("select", add_options(option_tags, options, value), html_options)
117
154
 
118
- def select_content_tag(option_tags, options, html_options)
119
- html_options = html_options.stringify_keys
120
- add_default_name_and_id(html_options)
121
- options[:include_blank] ||= true unless options[:prompt] || select_not_required?(html_options)
122
- value = options.fetch(:selected) { value(object) }
123
- select = content_tag("select", add_options(option_tags, options, value), html_options)
155
+ if html_options["multiple"] && options.fetch(:include_hidden, true)
156
+ tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "") + select
157
+ else
158
+ select
159
+ end
160
+ end
124
161
 
125
- if html_options["multiple"] && options.fetch(:include_hidden, true)
126
- tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
127
- else
128
- select
162
+ def placeholder_required?(html_options)
163
+ # See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
164
+ html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
129
165
  end
130
- end
131
166
 
132
- def select_not_required?(html_options)
133
- !html_options["required"] || html_options["multiple"] || html_options["size"].to_i > 1
134
- end
167
+ def add_options(option_tags, options, value = nil)
168
+ if options[:include_blank]
169
+ content = (options[:include_blank] if options[:include_blank].is_a?(String))
170
+ label = (" " unless content)
171
+ option_tags = tag_builder.content_tag_string("option", content, value: "", label: label) + "\n" + option_tags
172
+ end
173
+
174
+ if value.blank? && options[:prompt]
175
+ tag_options = { value: "" }.tap do |prompt_opts|
176
+ prompt_opts[:disabled] = true if options[:disabled] == ""
177
+ prompt_opts[:selected] = true if options[:selected] == ""
178
+ end
179
+ option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
180
+ end
181
+
182
+ option_tags
183
+ end
135
184
 
136
- def add_options(option_tags, options, value = nil)
137
- if options[:include_blank]
138
- option_tags = content_tag_string('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags
185
+ def name_and_id_index(options)
186
+ if options.key?("index")
187
+ options.delete("index") || ""
188
+ elsif @generate_indexed_names
189
+ @auto_index || ""
190
+ end
139
191
  end
140
- if value.blank? && options[:prompt]
141
- option_tags = content_tag_string('option', prompt_text(options[:prompt]), :value => '') + "\n" + option_tags
192
+
193
+ def generate_ids?
194
+ !@skip_default_ids
142
195
  end
143
- option_tags
144
- end
145
196
  end
146
197
  end
147
198
  end
@@ -1,4 +1,6 @@
1
- require 'action_view/helpers/tags/checkable'
1
+ # frozen_string_literal: true
2
+
3
+ require "action_view/helpers/tags/checkable"
2
4
 
3
5
  module ActionView
4
6
  module Helpers
@@ -16,7 +18,7 @@ module ActionView
16
18
  options = @options.stringify_keys
17
19
  options["type"] = "checkbox"
18
20
  options["value"] = @checked_value
19
- options["checked"] = "checked" if input_checked?(object, options)
21
+ options["checked"] = "checked" if input_checked?(options)
20
22
 
21
23
  if options["multiple"]
22
24
  add_default_name_and_id_for_value(@checked_value, options)
@@ -37,27 +39,26 @@ module ActionView
37
39
  end
38
40
 
39
41
  private
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)
42
+ def checked?(value)
43
+ case value
44
+ when TrueClass, FalseClass
45
+ value == !!@checked_value
46
+ when NilClass
47
+ false
48
+ when String
49
+ value == @checked_value
52
50
  else
53
- value.to_i == @checked_value.to_i
51
+ if value.respond_to?(:include?)
52
+ value.include?(@checked_value)
53
+ else
54
+ value.to_i == @checked_value.to_i
55
+ end
54
56
  end
55
57
  end
56
- end
57
58
 
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
59
+ def hidden_field_for_checkbox(options)
60
+ @unchecked_value ? tag("input", options.slice("name", "disabled", "form").merge!("type" => "hidden", "value" => @unchecked_value)) : "".html_safe
61
+ end
61
62
  end
62
63
  end
63
64
  end
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  module Helpers
3
5
  module Tags # :nodoc:
4
6
  module Checkable # :nodoc:
5
- def input_checked?(object, options)
7
+ def input_checked?(options)
6
8
  if options.has_key?("checked")
7
9
  checked = options.delete "checked"
8
10
  checked == true || checked == "checked"
9
11
  else
10
- checked?(value(object))
12
+ checked?(value)
11
13
  end
12
14
  end
13
15
  end
@@ -1,4 +1,6 @@
1
- require 'action_view/helpers/tags/collection_helpers'
1
+ # frozen_string_literal: true
2
+
3
+ require "action_view/helpers/tags/collection_helpers"
2
4
 
3
5
  module ActionView
4
6
  module Helpers
@@ -7,46 +9,26 @@ module ActionView
7
9
  include CollectionHelpers
8
10
 
9
11
  class CheckBoxBuilder < Builder # :nodoc:
10
- def check_box(extra_html_options={})
12
+ def check_box(extra_html_options = {})
11
13
  html_options = extra_html_options.merge(@input_html_options)
14
+ html_options[:multiple] = true
15
+ html_options[:skip_default_ids] = false
12
16
  @template_object.check_box(@object_name, @method_name, html_options, @value, nil)
13
17
  end
14
18
  end
15
19
 
16
20
  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
- rendered_collection + hidden_field
21
+ render_collection_for(CheckBoxBuilder, &block)
31
22
  end
32
23
 
33
24
  private
34
-
35
- def render_component(builder)
36
- builder.check_box + builder.label
37
- end
38
-
39
- def hidden_field
40
- hidden_name = @html_options[:name]
41
-
42
- hidden_name ||= if @options.has_key?(:index)
43
- "#{tag_name_with_index(@options[:index])}[]"
44
- else
45
- "#{tag_name}[]"
25
+ def render_component(builder)
26
+ builder.check_box + builder.label
46
27
  end
47
28
 
48
- @template_object.hidden_field_tag(hidden_name, "", id: nil)
49
- end
29
+ def hidden_field_name
30
+ "#{super}[]"
31
+ end
50
32
  end
51
33
  end
52
34
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  module Helpers
3
5
  module Tags # :nodoc:
@@ -17,8 +19,10 @@ module ActionView
17
19
  @input_html_options = input_html_options
18
20
  end
19
21
 
20
- def label(label_html_options={}, &block)
22
+ def label(label_html_options = {}, &block)
21
23
  html_options = @input_html_options.slice(:index, :namespace).merge(label_html_options)
24
+ html_options[:for] ||= @input_html_options[:id] if @input_html_options[:id]
25
+
22
26
  @template_object.label(@object_name, @sanitized_attribute_name, @text, html_options, &block)
23
27
  end
24
28
  end
@@ -33,52 +37,81 @@ module ActionView
33
37
  end
34
38
 
35
39
  private
40
+ def instantiate_builder(builder_class, item, value, text, html_options)
41
+ builder_class.new(@template_object, @object_name, @method_name, item,
42
+ sanitize_attribute_name(value), text, value, html_options)
43
+ end
36
44
 
37
- def instantiate_builder(builder_class, item, value, text, html_options)
38
- builder_class.new(@template_object, @object_name, @method_name, item,
39
- sanitize_attribute_name(value), text, value, html_options)
40
- end
45
+ # Generate default options for collection helpers, such as :checked and
46
+ # :disabled.
47
+ def default_html_options_for_collection(item, value)
48
+ html_options = @html_options.dup
41
49
 
42
- # Generate default options for collection helpers, such as :checked and
43
- # :disabled.
44
- def default_html_options_for_collection(item, value) #:nodoc:
45
- html_options = @html_options.dup
50
+ [:checked, :selected, :disabled, :readonly].each do |option|
51
+ current_value = @options[option]
52
+ next if current_value.nil?
46
53
 
47
- [:checked, :selected, :disabled].each do |option|
48
- current_value = @options[option]
49
- next if current_value.nil?
54
+ accept = if current_value.respond_to?(:call)
55
+ current_value.call(item)
56
+ else
57
+ Array(current_value).map(&:to_s).include?(value.to_s)
58
+ end
50
59
 
51
- accept = if current_value.respond_to?(:call)
52
- current_value.call(item)
53
- else
54
- Array(current_value).map(&:to_s).include?(value.to_s)
60
+ if accept
61
+ html_options[option] = true
62
+ elsif option == :checked
63
+ html_options[option] = false
64
+ end
55
65
  end
56
66
 
57
- if accept
58
- html_options[option] = true
59
- elsif option == :checked
60
- html_options[option] = false
61
- end
67
+ html_options[:object] = @object
68
+ html_options
62
69
  end
63
70
 
64
- html_options[:object] = @object
65
- html_options
66
- end
71
+ def sanitize_attribute_name(value)
72
+ "#{sanitized_method_name}_#{sanitized_value(value)}"
73
+ end
67
74
 
68
- def sanitize_attribute_name(value) #:nodoc:
69
- "#{sanitized_method_name}_#{sanitized_value(value)}"
70
- end
75
+ def render_collection
76
+ @collection.map do |item|
77
+ value = value_for_collection(item, @value_method)
78
+ text = value_for_collection(item, @text_method)
79
+ default_html_options = default_html_options_for_collection(item, value)
80
+ additional_html_options = option_html_attributes(item)
71
81
 
72
- def render_collection #:nodoc:
73
- @collection.map do |item|
74
- value = value_for_collection(item, @value_method)
75
- text = value_for_collection(item, @text_method)
76
- default_html_options = default_html_options_for_collection(item, value)
77
- additional_html_options = option_html_attributes(item)
82
+ yield item, value, text, default_html_options.merge(additional_html_options)
83
+ end.join.html_safe
84
+ end
78
85
 
79
- yield item, value, text, default_html_options.merge(additional_html_options)
80
- end.join.html_safe
81
- end
86
+ def render_collection_for(builder_class, &block)
87
+ options = @options.stringify_keys
88
+ rendered_collection = render_collection do |item, value, text, default_html_options|
89
+ builder = instantiate_builder(builder_class, item, value, text, default_html_options)
90
+
91
+ if block_given?
92
+ @template_object.capture(builder, &block)
93
+ else
94
+ render_component(builder)
95
+ end
96
+ end
97
+
98
+ # Prepend a hidden field to make sure something will be sent back to the
99
+ # server if all radio buttons are unchecked.
100
+ if options.fetch("include_hidden", true)
101
+ hidden_field + rendered_collection
102
+ else
103
+ rendered_collection
104
+ end
105
+ end
106
+
107
+ def hidden_field
108
+ hidden_name = @html_options[:name] || hidden_field_name
109
+ @template_object.hidden_field_tag(hidden_name, "", id: nil)
110
+ end
111
+
112
+ def hidden_field_name
113
+ "#{tag_name(false, @options[:index])}"
114
+ end
82
115
  end
83
116
  end
84
117
  end