actionview_attribute_builders 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []