actionview_attribute_builders 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +8 -0
  5. data/config/routes.rb +2 -0
  6. data/lib/actionview_attribute_builders/attribute_builders/base.rb +183 -0
  7. data/lib/actionview_attribute_builders/attribute_builders/check_box.rb +52 -0
  8. data/lib/actionview_attribute_builders/attribute_builders/checkable.rb +18 -0
  9. data/lib/actionview_attribute_builders/attribute_builders/color_field.rb +26 -0
  10. data/lib/actionview_attribute_builders/attribute_builders/date_field.rb +14 -0
  11. data/lib/actionview_attribute_builders/attribute_builders/datetime_field.rb +39 -0
  12. data/lib/actionview_attribute_builders/attribute_builders/datetime_local_field.rb +29 -0
  13. data/lib/actionview_attribute_builders/attribute_builders/email_field.rb +10 -0
  14. data/lib/actionview_attribute_builders/attribute_builders/file_field.rb +10 -0
  15. data/lib/actionview_attribute_builders/attribute_builders/hidden_field.rb +14 -0
  16. data/lib/actionview_attribute_builders/attribute_builders/label.rb +39 -0
  17. data/lib/actionview_attribute_builders/attribute_builders/month_field.rb +14 -0
  18. data/lib/actionview_attribute_builders/attribute_builders/number_field.rb +20 -0
  19. data/lib/actionview_attribute_builders/attribute_builders/password_field.rb +14 -0
  20. data/lib/actionview_attribute_builders/attribute_builders/radio_button.rb +33 -0
  21. data/lib/actionview_attribute_builders/attribute_builders/range_field.rb +10 -0
  22. data/lib/actionview_attribute_builders/attribute_builders/search_field.rb +27 -0
  23. data/lib/actionview_attribute_builders/attribute_builders/select.rb +42 -0
  24. data/lib/actionview_attribute_builders/attribute_builders/tel_field.rb +10 -0
  25. data/lib/actionview_attribute_builders/attribute_builders/text_area.rb +26 -0
  26. data/lib/actionview_attribute_builders/attribute_builders/text_field.rb +34 -0
  27. data/lib/actionview_attribute_builders/attribute_builders/time_field.rb +23 -0
  28. data/lib/actionview_attribute_builders/attribute_builders/url_field.rb +10 -0
  29. data/lib/actionview_attribute_builders/attribute_builders/week_field.rb +14 -0
  30. data/lib/actionview_attribute_builders/attribute_builders.rb +61 -0
  31. data/lib/actionview_attribute_builders/attribute_builders_helper.rb +39 -0
  32. data/lib/actionview_attribute_builders/engine.rb +11 -0
  33. data/lib/actionview_attribute_builders/version.rb +3 -0
  34. data/lib/actionview_attribute_builders.rb +6 -0
  35. data/lib/tasks/actionview_attribute_builders_tasks.rake +4 -0
  36. metadata +93 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b86da009d7cc1776185d530b5a36d0b987c45ae6decbe56e6417d16d491462bb
4
+ data.tar.gz: 53e398656df6d9e343c4efa880ebb49b8ae2db108dc5467cb77b6abb8467970f
5
+ SHA512:
6
+ metadata.gz: 599cc0f4d55bddea629a960f26d0f4a681af8510e4c37b82db6085dedc8ca5791acb4024dc7b151347bd8a63aa738b775895b10c2b59fc2cc19b2e869b116fc7
7
+ data.tar.gz: 38594b7e1a5ef99db5ce279ecabc97957803cf099dd1c13183c44ceacd4ccbe8bfe07a522b4125ce3508ea5f5b56f84e6248106fce40c0c67a9e9b6d711954bd
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Julián Pinzón Eslava
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # ActionviewAttributeBuilders
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem "actionview_attribute_builders"
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install actionview_attribute_builders
22
+ ```
23
+
24
+ ## Contributing
25
+ Contribution directions go here.
26
+
27
+ ## License
28
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ ActionviewAttributeBuilders::Engine.routes.draw do
2
+ end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class Base # :nodoc:
7
+ include Helpers::ActiveModelInstanceTag, Helpers::FormTagHelper
8
+ include FormOptionsHelper
9
+
10
+ attr_reader :object
11
+
12
+ def initialize(object_name, method_name, template_object, options = {})
13
+ @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
14
+ @template_object = template_object
15
+
16
+ @object_name.sub!(/\[\]$/, "") || @object_name.sub!(/\[\]\]$/, "]")
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)
20
+ @options = options
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
29
+ end
30
+
31
+ # This is what child classes implement.
32
+ def html_attributes
33
+ raise NotImplementedError, "Subclasses must implement a render method"
34
+ end
35
+
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
44
+
45
+ def value_before_type_cast
46
+ unless object.nil?
47
+ method_before_type_cast = @method_name + "_before_type_cast"
48
+
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
56
+
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)
60
+ end
61
+
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
72
+
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
80
+ end
81
+
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
93
+ end
94
+
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, options.delete("namespace")) }
101
+ if namespace = options.delete("namespace")
102
+ options["id"] = options["id"] ? "#{namespace}_#{options['id']}" : namespace
103
+ end
104
+ end
105
+ end
106
+
107
+ def tag_name(multiple = false, index = nil)
108
+ @template_object.field_name(@object_name, sanitized_method_name, multiple: multiple, index: index)
109
+ end
110
+
111
+ def tag_id(index = nil, namespace = nil)
112
+ @template_object.field_id(@object_name, @method_name, index: index, namespace: namespace)
113
+ end
114
+
115
+ def sanitized_method_name
116
+ @sanitized_method_name ||= @method_name.delete_suffix("?")
117
+ end
118
+
119
+ def sanitized_value(value)
120
+ value.to_s.gsub(/[\s.]/, "_").gsub(/[^-[[:word:]]]/, "").downcase
121
+ end
122
+
123
+ def select_content_tag(option_tags, options, html_options)
124
+ html_options = html_options.stringify_keys
125
+ [:required, :multiple, :size].each do |prop|
126
+ html_options[prop.to_s] = options.delete(prop) if options.key?(prop) && !html_options.key?(prop.to_s)
127
+ end
128
+
129
+ add_default_name_and_id(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
+
136
+ value = options.fetch(:selected) { value() }
137
+ select = content_tag("select", add_options(option_tags, options, value), html_options)
138
+
139
+ if html_options["multiple"] && options.fetch(:include_hidden, true)
140
+ tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "", autocomplete: "off") + select
141
+ else
142
+ select
143
+ end
144
+ end
145
+
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
150
+
151
+ def add_options(option_tags, options, value = nil)
152
+ if options[:include_blank]
153
+ content = (options[:include_blank] if options[:include_blank].is_a?(String))
154
+ label = (" " unless content)
155
+ option_tags = tag_builder.content_tag_string("option", content, value: "", label: label) + "\n" + option_tags
156
+ end
157
+
158
+ if value.blank? && options[:prompt]
159
+ tag_options = { value: "" }.tap do |prompt_opts|
160
+ prompt_opts[:disabled] = true if options[:disabled] == ""
161
+ prompt_opts[:selected] = true if options[:selected] == ""
162
+ end
163
+ option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
164
+ end
165
+
166
+ option_tags
167
+ end
168
+
169
+ def name_and_id_index(options)
170
+ if options.key?("index")
171
+ options.delete("index") || ""
172
+ elsif @generate_indexed_names
173
+ @auto_index || ""
174
+ end
175
+ end
176
+
177
+ def generate_ids?
178
+ !@skip_default_ids
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ # require "action_view/helpers/attribute_builders/checkable"
4
+
5
+ module ActionView
6
+ module Helpers
7
+ module AttributeBuilders # :nodoc:
8
+ class CheckBox < Base # :nodoc:
9
+ include Checkable
10
+
11
+ def initialize(object_name, method_name, template_object, checked_value, options)
12
+ @checked_value = checked_value
13
+ super(object_name, method_name, template_object, options)
14
+ end
15
+
16
+ def html_attributes
17
+ options = @options.stringify_keys
18
+ options["type"] = "checkbox"
19
+ options["value"] = @checked_value
20
+
21
+ options["checked"] = "checked" if input_checked?(options)
22
+
23
+ if options["multiple"]
24
+ add_default_name_and_id_for_value(@checked_value, options)
25
+ options.delete("multiple")
26
+ else
27
+ add_default_name_and_id(options)
28
+ end
29
+ return options
30
+ end
31
+
32
+ private
33
+ def checked?(value)
34
+ case value
35
+ when TrueClass, FalseClass
36
+ value == !!@checked_value
37
+ when NilClass
38
+ false
39
+ when String
40
+ value == @checked_value
41
+ else
42
+ if value.respond_to?(:include?)
43
+ value.include?(@checked_value)
44
+ else
45
+ value.to_i == @checked_value.to_i
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ module Checkable # :nodoc:
7
+ def input_checked?(options)
8
+ if options.has_key?("checked")
9
+ checked = options.delete "checked"
10
+ checked == true || checked == "checked"
11
+ else
12
+ checked?(value)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class ColorField < TextField # :nodoc:
7
+ def html_attributes
8
+ options = @options.stringify_keys
9
+ options["value"] ||= validate_color_string(value)
10
+ @options = options
11
+ super
12
+ end
13
+
14
+ private
15
+ def validate_color_string(string)
16
+ regex = /#[0-9a-fA-F]{6}/
17
+ if regex.match?(string)
18
+ string.downcase
19
+ else
20
+ "#000000"
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class DateField < DatetimeField # :nodoc:
7
+ private
8
+ def format_datetime(value)
9
+ value&.strftime("%Y-%m-%d")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class DatetimeField < TextField # :nodoc:
7
+ def html_attributes
8
+ options = @options.stringify_keys
9
+ options["value"] = datetime_value(options["value"] || value)
10
+ options["min"] = format_datetime(parse_datetime(options["min"]))
11
+ options["max"] = format_datetime(parse_datetime(options["max"]))
12
+ @options = options
13
+ super
14
+ end
15
+
16
+ private
17
+ def datetime_value(value)
18
+ if value.is_a?(String)
19
+ value
20
+ else
21
+ format_datetime(value)
22
+ end
23
+ end
24
+
25
+ def format_datetime(value)
26
+ raise NotImplementedError
27
+ end
28
+
29
+ def parse_datetime(value)
30
+ if value.is_a?(String)
31
+ DateTime.parse(value) rescue nil
32
+ else
33
+ value
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class DatetimeLocalField < DatetimeField # :nodoc:
7
+ def initialize(object_name, method_name, template_object, options = {})
8
+ @include_seconds = options.delete(:include_seconds) { true }
9
+ super
10
+ end
11
+
12
+ class << self
13
+ def field_type
14
+ @field_type ||= "datetime-local"
15
+ end
16
+ end
17
+
18
+ private
19
+ def format_datetime(value)
20
+ if @include_seconds
21
+ value&.strftime("%Y-%m-%dT%T")
22
+ else
23
+ value&.strftime("%Y-%m-%dT%H:%M")
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class EmailField < TextField # :nodoc:
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class FileField < TextField # :nodoc:
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class HiddenField < TextField # :nodoc:
7
+ def html_attributes
8
+ @options[:autocomplete] = "off"
9
+ super
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class Label < Base # :nodoc:
7
+ def initialize(object_name, method_name, template_object, content_or_options, options = nil)
8
+ options ||= {}
9
+ if content_or_options.is_a?(Hash)
10
+ options.merge! content_or_options
11
+ else
12
+ @content = content_or_options
13
+ end
14
+ super(object_name, method_name, template_object, options)
15
+ end
16
+
17
+ attr_reader :content
18
+
19
+ def html_attributes(&block)
20
+ options = @options.stringify_keys
21
+ name_and_id = options.dup
22
+
23
+ if name_and_id["for"]
24
+ name_and_id["id"] = name_and_id["for"]
25
+ else
26
+ name_and_id.delete("id")
27
+ end
28
+
29
+ add_default_name_and_id_for_value(options["value"], name_and_id)
30
+ options.delete("index")
31
+ options.delete("namespace")
32
+ options["for"] = name_and_id["id"] unless options.key?("for")
33
+
34
+ options
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class MonthField < DatetimeField # :nodoc:
7
+ private
8
+ def format_datetime(value)
9
+ value&.strftime("%Y-%m")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class NumberField < TextField # :nodoc:
7
+ def html_attributes
8
+ options = @options.stringify_keys
9
+
10
+ if range = options.delete("in") || options.delete("within")
11
+ options.update("min" => range.min, "max" => range.max)
12
+ end
13
+
14
+ @options = options
15
+ super
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class PasswordField < TextField # :nodoc:
7
+ def html_attributes
8
+ @options = { value: nil }.merge!(@options)
9
+ super
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_view/helpers/tags/checkable"
4
+
5
+ module ActionView
6
+ module Helpers
7
+ module AttributeBuilders # :nodoc:
8
+ class RadioButton < Base # :nodoc:
9
+ include Tags::Checkable
10
+
11
+ def initialize(object_name, method_name, template_object, tag_value, options)
12
+ @tag_value = tag_value
13
+ super(object_name, method_name, template_object, options)
14
+ end
15
+
16
+ def html_attributes
17
+ options = @options.stringify_keys
18
+ options["type"] = "radio"
19
+ options["value"] = @tag_value
20
+ options["checked"] = "checked" if input_checked?(options)
21
+ add_default_name_and_id_for_value(@tag_value, options)
22
+
23
+ return options
24
+ end
25
+
26
+ private
27
+ def checked?(value)
28
+ value.to_s == @tag_value.to_s
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class RangeField < NumberField # :nodoc:
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class SearchField < TextField # :nodoc:
7
+ def html_attributes
8
+ options = @options.stringify_keys
9
+
10
+ if options["autosave"]
11
+ if options["autosave"] == true
12
+ options["autosave"] = request.host.split(".").reverse.join(".")
13
+ end
14
+ options["results"] ||= 10
15
+ end
16
+
17
+ if options["onsearch"]
18
+ options["incremental"] = true unless options.has_key?("incremental")
19
+ end
20
+
21
+ @options = options
22
+ super
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class Select < Base # :nodoc:
7
+ def initialize(object_name, method_name, template_object, options, html_options)
8
+ @html_options = html_options
9
+
10
+ super(object_name, method_name, template_object, options)
11
+
12
+ @html_options = @html_options.stringify_keys
13
+ [:required, :multiple, :size].each do |prop|
14
+ @html_options[prop.to_s] = @options.delete(prop) if @options.key?(prop) && !@html_options.key?(prop.to_s)
15
+ end
16
+
17
+ add_default_name_and_id(@html_options)
18
+ @html_options["value"] = @options.fetch(:selected) { value() }
19
+ end
20
+
21
+ def html_attributes
22
+ @html_options
23
+ end
24
+
25
+ def options
26
+ return @options unless placeholder_required?(@html_options)
27
+
28
+ raise ArgumentError, "include_blank cannot be false for a required field." if @options[:include_blank] == false
29
+ @options[:include_blank] ||= true unless @options[:prompt]
30
+
31
+ @options
32
+ end
33
+
34
+ private
35
+ def placeholder_required?(html_options)
36
+ # See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
37
+ html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class TelField < TextField # :nodoc:
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_view/helpers/tags/placeholderable"
4
+
5
+ module ActionView
6
+ module Helpers
7
+ module AttributeBuilders # :nodoc:
8
+ class TextArea < Base # :nodoc:
9
+ include Tags::Placeholderable
10
+
11
+ def html_attributes
12
+ options = @options.stringify_keys
13
+ add_default_name_and_id(options)
14
+
15
+ if size = options.delete("size")
16
+ options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
17
+ end
18
+
19
+ options["value"] = options.delete("value") { value_before_type_cast }
20
+
21
+ return options
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_view/helpers/tags/placeholderable"
4
+
5
+ module ActionView
6
+ module Helpers
7
+ module AttributeBuilders # :nodoc:
8
+ class TextField < Base # :nodoc:
9
+ include Tags::Placeholderable
10
+
11
+ def html_attributes
12
+ options = @options.stringify_keys
13
+ options["size"] = options["maxlength"] unless options.key?("size")
14
+ options["type"] ||= field_type
15
+ options["value"] = options.fetch("value") { value_before_type_cast } unless field_type == "file"
16
+ add_default_name_and_id(options)
17
+
18
+ return options
19
+ end
20
+
21
+ class << self
22
+ def field_type
23
+ @field_type ||= name.split("::").last.sub("Field", "").downcase
24
+ end
25
+ end
26
+
27
+ private
28
+ def field_type
29
+ self.class.field_type
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class TimeField < DatetimeField # :nodoc:
7
+ def initialize(object_name, method_name, template_object, options = {})
8
+ @include_seconds = options.delete(:include_seconds) { true }
9
+ super
10
+ end
11
+
12
+ private
13
+ def format_datetime(value)
14
+ if @include_seconds
15
+ value&.strftime("%T.%L")
16
+ else
17
+ value&.strftime("%H:%M")
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class UrlField < TextField # :nodoc:
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module AttributeBuilders # :nodoc:
6
+ class WeekField < DatetimeField # :nodoc:
7
+ private
8
+ def format_datetime(value)
9
+ value&.strftime("%Y-W%V")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ require_relative "attribute_builders/base"
4
+ require_relative "attribute_builders/checkable"
5
+ require_relative "attribute_builders/text_field"
6
+ require_relative "attribute_builders/datetime_field"
7
+
8
+ require_relative "attribute_builders/check_box"
9
+ require_relative "attribute_builders/color_field"
10
+ require_relative "attribute_builders/date_field"
11
+ require_relative "attribute_builders/datetime_local_field"
12
+ require_relative "attribute_builders/email_field"
13
+ require_relative "attribute_builders/file_field"
14
+ require_relative "attribute_builders/hidden_field"
15
+ require_relative "attribute_builders/label"
16
+ require_relative "attribute_builders/month_field"
17
+ require_relative "attribute_builders/number_field"
18
+ require_relative "attribute_builders/radio_button"
19
+ require_relative "attribute_builders/search_field"
20
+ require_relative "attribute_builders/select"
21
+ require_relative "attribute_builders/tel_field"
22
+ require_relative "attribute_builders/text_area"
23
+ require_relative "attribute_builders/time_field"
24
+ require_relative "attribute_builders/password_field"
25
+ require_relative "attribute_builders/range_field"
26
+ require_relative "attribute_builders/url_field"
27
+ require_relative "attribute_builders/week_field"
28
+
29
+ module ActionView
30
+ module Helpers # :nodoc:
31
+ module AttributeBuilders # :nodoc:
32
+ # extend ActiveSupport::Autoload
33
+ #
34
+ # eager_autoload do
35
+ # autoload :Base
36
+ # autoload :CheckBox
37
+ # autoload :ColorField
38
+ # autoload :DateField
39
+ # autoload :DatetimeField
40
+ # autoload :DatetimeLocalField
41
+ # autoload :EmailField
42
+ # autoload :FileField
43
+ # autoload :HiddenField
44
+ # autoload :Label
45
+ # autoload :MonthField
46
+ # autoload :NumberField
47
+ # autoload :RadioButton
48
+ # autoload :SearchField
49
+ # autoload :Select
50
+ # autoload :TelField
51
+ # autoload :TextArea
52
+ # autoload :TextField
53
+ # autoload :TimeField
54
+ # autoload :PasswordField
55
+ # autoload :RangeField
56
+ # autoload :UrlField
57
+ # autoload :WeekField
58
+ # end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,39 @@
1
+ module ActionviewAttributeBuilders
2
+ module AttributeBuildersHelper
3
+ def color_field_attribute_builder(method, options)
4
+ ActionView::Helpers::AttributeBuilders::ColorField.new(object_name, method, @template, options)
5
+ end
6
+
7
+ def email_field_attribute_builder(method, options)
8
+ ActionView::Helpers::AttributeBuilders::EmailField.new(object_name, method, @template, options)
9
+ end
10
+
11
+ def number_field_attribute_builder(method, options)
12
+ ActionView::Helpers::AttributeBuilders::NumberField.new(object_name, method, @template, options)
13
+ end
14
+
15
+ def search_field_attribute_builder(method, options)
16
+ ActionView::Helpers::AttributeBuilders::SearchField.new(object_name, method, @template, options)
17
+ end
18
+
19
+ def tel_field_attribute_builder(method, options)
20
+ ActionView::Helpers::AttributeBuilders::TelField.new(object_name, method, @template, options)
21
+ end
22
+
23
+ def text_field_attribute_builder(method, options)
24
+ ActionView::Helpers::AttributeBuilders::TextField.new(object_name, method, @template, options)
25
+ end
26
+
27
+ def password_field_attribute_builder(method, options)
28
+ ActionView::Helpers::AttributeBuilders::PasswordField.new(object_name, method, @template, options)
29
+ end
30
+
31
+ def url_field_attribute_builder(method, options)
32
+ ActionView::Helpers::AttributeBuilders::UrlField.new(object_name, method, @template, options)
33
+ end
34
+
35
+ def checkbox_attribute_builder(method, options, checked_value)
36
+ ActionView::Helpers::AttributeBuilders::CheckBox.new(object_name, method, @template, checked_value, options)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+ module ActionviewAttributeBuilders
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace ActionviewAttributeBuilders
4
+
5
+ ActiveSupport.on_load(:action_view) do
6
+ require "actionview_attribute_builders/attribute_builders"
7
+ require "actionview_attribute_builders/attribute_builders_helper"
8
+ ActionView::Helpers::FormBuilder.include(ActionviewAttributeBuilders::AttributeBuildersHelper)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module ActionviewAttributeBuilders
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,6 @@
1
+ require "actionview_attribute_builders/version"
2
+ require "actionview_attribute_builders/engine"
3
+
4
+ module ActionviewAttributeBuilders
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :actionview_attribute_builders do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: actionview_attribute_builders
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Julián Pinzón Eslava
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-10-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 7.1.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 7.1.1
27
+ description:
28
+ email:
29
+ - dev@julianpinzon.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - MIT-LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - config/routes.rb
38
+ - lib/actionview_attribute_builders.rb
39
+ - lib/actionview_attribute_builders/attribute_builders.rb
40
+ - lib/actionview_attribute_builders/attribute_builders/base.rb
41
+ - lib/actionview_attribute_builders/attribute_builders/check_box.rb
42
+ - lib/actionview_attribute_builders/attribute_builders/checkable.rb
43
+ - lib/actionview_attribute_builders/attribute_builders/color_field.rb
44
+ - lib/actionview_attribute_builders/attribute_builders/date_field.rb
45
+ - lib/actionview_attribute_builders/attribute_builders/datetime_field.rb
46
+ - lib/actionview_attribute_builders/attribute_builders/datetime_local_field.rb
47
+ - lib/actionview_attribute_builders/attribute_builders/email_field.rb
48
+ - lib/actionview_attribute_builders/attribute_builders/file_field.rb
49
+ - lib/actionview_attribute_builders/attribute_builders/hidden_field.rb
50
+ - lib/actionview_attribute_builders/attribute_builders/label.rb
51
+ - lib/actionview_attribute_builders/attribute_builders/month_field.rb
52
+ - lib/actionview_attribute_builders/attribute_builders/number_field.rb
53
+ - lib/actionview_attribute_builders/attribute_builders/password_field.rb
54
+ - lib/actionview_attribute_builders/attribute_builders/radio_button.rb
55
+ - lib/actionview_attribute_builders/attribute_builders/range_field.rb
56
+ - lib/actionview_attribute_builders/attribute_builders/search_field.rb
57
+ - lib/actionview_attribute_builders/attribute_builders/select.rb
58
+ - lib/actionview_attribute_builders/attribute_builders/tel_field.rb
59
+ - lib/actionview_attribute_builders/attribute_builders/text_area.rb
60
+ - lib/actionview_attribute_builders/attribute_builders/text_field.rb
61
+ - lib/actionview_attribute_builders/attribute_builders/time_field.rb
62
+ - lib/actionview_attribute_builders/attribute_builders/url_field.rb
63
+ - lib/actionview_attribute_builders/attribute_builders/week_field.rb
64
+ - lib/actionview_attribute_builders/attribute_builders_helper.rb
65
+ - lib/actionview_attribute_builders/engine.rb
66
+ - lib/actionview_attribute_builders/version.rb
67
+ - lib/tasks/actionview_attribute_builders_tasks.rake
68
+ homepage: https://github.com/pinzonjulian/actionview_attribute_builders
69
+ licenses:
70
+ - MIT
71
+ metadata:
72
+ homepage_uri: https://github.com/pinzonjulian/actionview_attribute_builders
73
+ source_code_uri: https://github.com/pinzonjulian/actionview_attribute_builders
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubygems_version: 3.4.10
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: Use Rails' attribute building capabilities to create custom form input elements
93
+ test_files: []