actionview 4.2.11.1 → 7.0.2.4

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 +4 -4
  2. data/CHANGELOG.md +229 -215
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -8
  5. data/lib/action_view/base.rb +116 -43
  6. data/lib/action_view/buffers.rb +20 -3
  7. data/lib/action_view/cache_expiry.rb +66 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
  10. data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
  11. data/lib/action_view/dependency_tracker.rb +21 -122
  12. data/lib/action_view/digestor.rb +92 -85
  13. data/lib/action_view/flows.rb +15 -16
  14. data/lib/action_view/gem_version.rb +6 -4
  15. data/lib/action_view/helpers/active_model_helper.rb +17 -12
  16. data/lib/action_view/helpers/asset_tag_helper.rb +356 -101
  17. data/lib/action_view/helpers/asset_url_helper.rb +180 -74
  18. data/lib/action_view/helpers/atom_feed_helper.rb +21 -19
  19. data/lib/action_view/helpers/cache_helper.rb +156 -43
  20. data/lib/action_view/helpers/capture_helper.rb +21 -14
  21. data/lib/action_view/helpers/controller_helper.rb +16 -5
  22. data/lib/action_view/helpers/csp_helper.rb +26 -0
  23. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  24. data/lib/action_view/helpers/date_helper.rb +288 -132
  25. data/lib/action_view/helpers/debug_helper.rb +9 -6
  26. data/lib/action_view/helpers/form_helper.rb +956 -173
  27. data/lib/action_view/helpers/form_options_helper.rb +178 -97
  28. data/lib/action_view/helpers/form_tag_helper.rb +220 -101
  29. data/lib/action_view/helpers/javascript_helper.rb +33 -19
  30. data/lib/action_view/helpers/number_helper.rb +88 -63
  31. data/lib/action_view/helpers/output_safety_helper.rb +38 -6
  32. data/lib/action_view/helpers/rendering_helper.rb +21 -10
  33. data/lib/action_view/helpers/sanitize_helper.rb +31 -32
  34. data/lib/action_view/helpers/tag_helper.rb +332 -71
  35. data/lib/action_view/helpers/tags/base.rb +123 -99
  36. data/lib/action_view/helpers/tags/check_box.rb +21 -20
  37. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  38. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -34
  39. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  40. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  41. data/lib/action_view/helpers/tags/collection_select.rb +5 -3
  42. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  43. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  44. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  45. data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
  46. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  47. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  48. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  49. data/lib/action_view/helpers/tags/file_field.rb +18 -0
  50. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  51. data/lib/action_view/helpers/tags/hidden_field.rb +6 -0
  52. data/lib/action_view/helpers/tags/label.rb +7 -2
  53. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  54. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  55. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  56. data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
  57. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  58. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  59. data/lib/action_view/helpers/tags/search_field.rb +14 -9
  60. data/lib/action_view/helpers/tags/select.rb +11 -10
  61. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  62. data/lib/action_view/helpers/tags/text_area.rb +4 -2
  63. data/lib/action_view/helpers/tags/text_field.rb +8 -8
  64. data/lib/action_view/helpers/tags/time_field.rb +12 -2
  65. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  66. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  67. data/lib/action_view/helpers/tags/translator.rb +15 -16
  68. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  69. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  70. data/lib/action_view/helpers/tags/weekday_select.rb +28 -0
  71. data/lib/action_view/helpers/tags.rb +5 -2
  72. data/lib/action_view/helpers/text_helper.rb +80 -51
  73. data/lib/action_view/helpers/translation_helper.rb +120 -69
  74. data/lib/action_view/helpers/url_helper.rb +398 -171
  75. data/lib/action_view/helpers.rb +29 -27
  76. data/lib/action_view/layouts.rb +68 -63
  77. data/lib/action_view/log_subscriber.rb +77 -10
  78. data/lib/action_view/lookup_context.rb +137 -113
  79. data/lib/action_view/model_naming.rb +4 -2
  80. data/lib/action_view/path_set.rb +28 -32
  81. data/lib/action_view/railtie.rb +74 -13
  82. data/lib/action_view/record_identifier.rb +53 -26
  83. data/lib/action_view/render_parser.rb +188 -0
  84. data/lib/action_view/renderer/abstract_renderer.rb +152 -15
  85. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  86. data/lib/action_view/renderer/object_renderer.rb +34 -0
  87. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  88. data/lib/action_view/renderer/partial_renderer.rb +51 -333
  89. data/lib/action_view/renderer/renderer.rb +68 -11
  90. data/lib/action_view/renderer/streaming_template_renderer.rb +60 -56
  91. data/lib/action_view/renderer/template_renderer.rb +87 -74
  92. data/lib/action_view/rendering.rb +73 -47
  93. data/lib/action_view/ripper_ast_parser.rb +198 -0
  94. data/lib/action_view/routing_url_for.rb +35 -24
  95. data/lib/action_view/tasks/cache_digests.rake +25 -0
  96. data/lib/action_view/template/error.rb +151 -41
  97. data/lib/action_view/template/handlers/builder.rb +12 -13
  98. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  99. data/lib/action_view/template/handlers/erb.rb +29 -89
  100. data/lib/action_view/template/handlers/html.rb +11 -0
  101. data/lib/action_view/template/handlers/raw.rb +4 -4
  102. data/lib/action_view/template/handlers.rb +14 -10
  103. data/lib/action_view/template/html.rb +12 -13
  104. data/lib/action_view/template/inline.rb +22 -0
  105. data/lib/action_view/template/raw_file.rb +25 -0
  106. data/lib/action_view/template/renderable.rb +24 -0
  107. data/lib/action_view/template/resolver.rb +139 -300
  108. data/lib/action_view/template/sources/file.rb +17 -0
  109. data/lib/action_view/template/sources.rb +13 -0
  110. data/lib/action_view/template/text.rb +10 -12
  111. data/lib/action_view/template/types.rb +28 -26
  112. data/lib/action_view/template.rb +123 -91
  113. data/lib/action_view/template_details.rb +66 -0
  114. data/lib/action_view/template_path.rb +64 -0
  115. data/lib/action_view/test_case.rb +70 -53
  116. data/lib/action_view/testing/resolvers.rb +25 -35
  117. data/lib/action_view/unbound_template.rb +57 -0
  118. data/lib/action_view/version.rb +3 -1
  119. data/lib/action_view/view_paths.rb +73 -58
  120. data/lib/action_view.rb +16 -11
  121. data/lib/assets/compiled/rails-ujs.js +746 -0
  122. metadata +52 -32
  123. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  124. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -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,132 +34,145 @@ module ActionView
23
34
  end
24
35
 
25
36
  private
26
-
27
- def value(object)
28
- object.public_send @method_name if object
29
- end
30
-
31
- def value_before_type_cast(object)
32
- unless object.nil?
33
- method_before_type_cast = @method_name + "_before_type_cast"
34
-
35
- if value_came_from_user?(object) && object.respond_to?(method_before_type_cast)
36
- object.public_send(method_before_type_cast)
37
+ def value
38
+ if @allow_method_names_outside_object
39
+ object.public_send @method_name if object && object.respond_to?(@method_name)
37
40
  else
38
- value(object)
41
+ object.public_send @method_name if object
39
42
  end
40
43
  end
41
- end
42
44
 
43
- def value_came_from_user?(object)
44
- method_name = "#{@method_name}_came_from_user?"
45
- !object.respond_to?(method_name) || object.public_send(method_name)
46
- end
45
+ def value_before_type_cast
46
+ unless object.nil?
47
+ method_before_type_cast = @method_name + "_before_type_cast"
47
48
 
48
- def retrieve_object(object)
49
- if object
50
- object
51
- elsif @template_object.instance_variable_defined?("@#{@object_name}")
52
- @template_object.instance_variable_get("@#{@object_name}")
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
53
55
  end
54
- rescue NameError
55
- # As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
56
- nil
57
- end
58
56
 
59
- def retrieve_autoindex(pre_match)
60
- object = self.object || @template_object.instance_variable_get("@#{pre_match}")
61
- if object && object.respond_to?(:to_param)
62
- object.to_param
63
- else
64
- raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
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)
65
60
  end
66
- end
67
61
 
68
- def add_default_name_and_id_for_value(tag_value, options)
69
- if tag_value.nil?
70
- add_default_name_and_id(options)
71
- else
72
- specified_id = options["id"]
73
- add_default_name_and_id(options)
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
71
+ end
74
72
 
75
- if specified_id.blank? && options["id"].present?
76
- options["id"] += "_#{sanitized_value(tag_value)}"
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}"
77
79
  end
78
80
  end
79
- end
80
81
 
81
- 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 }
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)
88
+
89
+ if specified_id.blank? && options["id"].present?
90
+ options["id"] += "_#{sanitized_value(tag_value)}"
91
+ end
92
+ end
92
93
  end
93
94
 
94
- options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence
95
- end
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) }
96
98
 
97
- def tag_name(multiple = false)
98
- "#{@object_name}[#{sanitized_method_name}]#{"[]" if multiple}"
99
- end
99
+ if generate_ids?
100
+ options["id"] = options.fetch("id") { tag_id(index, options.delete("namespace")) }
101
+ if namespace = options.delete("namespace")
102
+ options["id"] = options["id"] ? "#{namespace}_#{options['id']}" : namespace
103
+ end
104
+ end
105
+ end
100
106
 
101
- def tag_name_with_index(index, multiple = false)
102
- "#{@object_name}[#{index}][#{sanitized_method_name}]#{"[]" if multiple}"
103
- end
107
+ def tag_name(multiple = false, index = nil)
108
+ @template_object.field_name(@object_name, sanitized_method_name, multiple: multiple, index: index)
109
+ end
104
110
 
105
- def tag_id
106
- "#{sanitized_object_name}_#{sanitized_method_name}"
107
- end
111
+ def tag_id(index = nil, namespace = nil)
112
+ @template_object.field_id(@object_name, @method_name, index: index, namespace: namespace)
113
+ end
108
114
 
109
- def tag_id_with_index(index)
110
- "#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
111
- end
115
+ def sanitized_method_name
116
+ @sanitized_method_name ||= @method_name.delete_suffix("?")
117
+ end
112
118
 
113
- def sanitized_object_name
114
- @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
115
- end
119
+ def sanitized_value(value)
120
+ value.to_s.gsub(/[\s.]/, "_").gsub(/[^-[[:word:]]]/, "").downcase
121
+ end
116
122
 
117
- def sanitized_method_name
118
- @sanitized_method_name ||= @method_name.sub(/\?$/,"")
119
- end
123
+ def select_content_tag(option_tags, options, html_options)
124
+ html_options = html_options.stringify_keys
125
+ add_default_name_and_id(html_options)
120
126
 
121
- def sanitized_value(value)
122
- value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase
123
- end
127
+ if placeholder_required?(html_options)
128
+ raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
129
+ options[:include_blank] ||= true unless options[:prompt]
130
+ end
124
131
 
125
- def select_content_tag(option_tags, options, html_options)
126
- html_options = html_options.stringify_keys
127
- add_default_name_and_id(html_options)
128
- options[:include_blank] ||= true unless options[:prompt] || select_not_required?(html_options)
129
- value = options.fetch(:selected) { value(object) }
130
- select = content_tag("select", add_options(option_tags, options, value), html_options)
132
+ value = options.fetch(:selected) { value() }
133
+ select = content_tag("select", add_options(option_tags, options, value), html_options)
131
134
 
132
- if html_options["multiple"] && options.fetch(:include_hidden, true)
133
- tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
134
- else
135
- select
135
+ if html_options["multiple"] && options.fetch(:include_hidden, true)
136
+ tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "", autocomplete: "off") + select
137
+ else
138
+ select
139
+ end
136
140
  end
137
- end
138
141
 
139
- def select_not_required?(html_options)
140
- !html_options["required"] || html_options["multiple"] || html_options["size"].to_i > 1
141
- end
142
+ def placeholder_required?(html_options)
143
+ # See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
144
+ html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
145
+ end
142
146
 
143
- def add_options(option_tags, options, value = nil)
144
- if options[:include_blank]
145
- option_tags = content_tag_string('option', options[:include_blank].kind_of?(String) ? options[:include_blank] : nil, :value => '') + "\n" + option_tags
147
+ def add_options(option_tags, options, value = nil)
148
+ if options[:include_blank]
149
+ content = (options[:include_blank] if options[:include_blank].is_a?(String))
150
+ label = (" " unless content)
151
+ option_tags = tag_builder.content_tag_string("option", content, value: "", label: label) + "\n" + option_tags
152
+ end
153
+
154
+ if value.blank? && options[:prompt]
155
+ tag_options = { value: "" }.tap do |prompt_opts|
156
+ prompt_opts[:disabled] = true if options[:disabled] == ""
157
+ prompt_opts[:selected] = true if options[:selected] == ""
158
+ end
159
+ option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
160
+ end
161
+
162
+ option_tags
163
+ end
164
+
165
+ def name_and_id_index(options)
166
+ if options.key?("index")
167
+ options.delete("index") || ""
168
+ elsif @generate_indexed_names
169
+ @auto_index || ""
170
+ end
146
171
  end
147
- if value.blank? && options[:prompt]
148
- option_tags = content_tag_string('option', prompt_text(options[:prompt]), :value => '') + "\n" + option_tags
172
+
173
+ def generate_ids?
174
+ !@skip_default_ids
149
175
  end
150
- option_tags
151
- end
152
176
  end
153
177
  end
154
178
  end
@@ -1,9 +1,11 @@
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
5
7
  module Tags # :nodoc:
6
- class CheckBox < Base #:nodoc:
8
+ class CheckBox < Base # :nodoc:
7
9
  include Checkable
8
10
 
9
11
  def initialize(object_name, method_name, template_object, checked_value, unchecked_value, options)
@@ -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, "autocomplete" => "off")) : "".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,50 +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
- if @options.fetch(:include_hidden, true)
31
- rendered_collection + hidden_field
32
- else
33
- rendered_collection
34
- end
21
+ render_collection_for(CheckBoxBuilder, &block)
35
22
  end
36
23
 
37
24
  private
38
-
39
- def render_component(builder)
40
- builder.check_box + builder.label
41
- end
42
-
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}[]"
25
+ def render_component(builder)
26
+ builder.check_box + builder.label
50
27
  end
51
28
 
52
- @template_object.hidden_field_tag(hidden_name, "", id: nil)
53
- end
29
+ def hidden_field_name
30
+ "#{super}[]"
31
+ end
54
32
  end
55
33
  end
56
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, :readonly].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
@@ -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,26 +9,18 @@ module ActionView
7
9
  include CollectionHelpers
8
10
 
9
11
  class RadioButtonBuilder < Builder # :nodoc:
10
- def radio_button(extra_html_options={})
12
+ def radio_button(extra_html_options = {})
11
13
  html_options = extra_html_options.merge(@input_html_options)
14
+ html_options[:skip_default_ids] = false
12
15
  @template_object.radio_button(@object_name, @method_name, @value, html_options)
13
16
  end
14
17
  end
15
18
 
16
19
  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
20
+ render_collection_for(RadioButtonBuilder, &block)
26
21
  end
27
22
 
28
23
  private
29
-
30
24
  def render_component(builder)
31
25
  builder.radio_button + builder.label
32
26
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  module Helpers
3
5
  module Tags # :nodoc:
4
- class CollectionSelect < Base #:nodoc:
6
+ class CollectionSelect < Base # :nodoc:
5
7
  def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
6
8
  @collection = collection
7
9
  @value_method = value_method
@@ -13,8 +15,8 @@ module ActionView
13
15
 
14
16
  def render
15
17
  option_tags_options = {
16
- :selected => @options.fetch(:selected) { value(@object) },
17
- :disabled => @options[:disabled]
18
+ selected: @options.fetch(:selected) { value },
19
+ disabled: @options[:disabled]
18
20
  }
19
21
 
20
22
  select_content_tag(
@@ -1,19 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  module Helpers
3
5
  module Tags # :nodoc:
4
6
  class ColorField < TextField # :nodoc:
5
7
  def render
6
8
  options = @options.stringify_keys
7
- options["value"] ||= validate_color_string(value(object))
9
+ options["value"] ||= validate_color_string(value)
8
10
  @options = options
9
11
  super
10
12
  end
11
13
 
12
14
  private
13
-
14
15
  def validate_color_string(string)
15
16
  regex = /#[0-9a-fA-F]{6}/
16
- if regex.match(string)
17
+ if regex.match?(string)
17
18
  string.downcase
18
19
  else
19
20
  "#000000"
@@ -1,11 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  module Helpers
3
5
  module Tags # :nodoc:
4
6
  class DateField < DatetimeField # :nodoc:
5
7
  private
6
-
7
8
  def format_date(value)
8
- value.try(:strftime, "%Y-%m-%d")
9
+ value&.strftime("%Y-%m-%d")
9
10
  end
10
11
  end
11
12
  end