phlexi-form 0.2.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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/Appraisals +13 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +395 -0
- data/Rakefile +14 -0
- data/config.ru +9 -0
- data/gemfiles/default.gemfile +5 -0
- data/gemfiles/default.gemfile.lock +174 -0
- data/lib/generators/superform/install/USAGE +8 -0
- data/lib/generators/superform/install/install_generator.rb +34 -0
- data/lib/generators/superform/install/templates/application_form.rb +31 -0
- data/lib/phlexi/form/base.rb +234 -0
- data/lib/phlexi/form/components/base.rb +37 -0
- data/lib/phlexi/form/components/checkbox.rb +43 -0
- data/lib/phlexi/form/components/collection_checkboxes.rb +30 -0
- data/lib/phlexi/form/components/collection_radio_buttons.rb +29 -0
- data/lib/phlexi/form/components/concerns/has_options.rb +33 -0
- data/lib/phlexi/form/components/error.rb +21 -0
- data/lib/phlexi/form/components/full_error.rb +21 -0
- data/lib/phlexi/form/components/hint.rb +21 -0
- data/lib/phlexi/form/components/input.rb +78 -0
- data/lib/phlexi/form/components/label.rb +26 -0
- data/lib/phlexi/form/components/radio_button.rb +31 -0
- data/lib/phlexi/form/components/select.rb +57 -0
- data/lib/phlexi/form/components/textarea.rb +34 -0
- data/lib/phlexi/form/components/wrapper.rb +31 -0
- data/lib/phlexi/form/field_options/autofocus.rb +18 -0
- data/lib/phlexi/form/field_options/collection.rb +37 -0
- data/lib/phlexi/form/field_options/disabled.rb +18 -0
- data/lib/phlexi/form/field_options/errors.rb +82 -0
- data/lib/phlexi/form/field_options/hints.rb +22 -0
- data/lib/phlexi/form/field_options/labels.rb +28 -0
- data/lib/phlexi/form/field_options/length.rb +53 -0
- data/lib/phlexi/form/field_options/limit.rb +66 -0
- data/lib/phlexi/form/field_options/min_max.rb +92 -0
- data/lib/phlexi/form/field_options/multiple.rb +63 -0
- data/lib/phlexi/form/field_options/pattern.rb +38 -0
- data/lib/phlexi/form/field_options/placeholder.rb +18 -0
- data/lib/phlexi/form/field_options/readonly.rb +18 -0
- data/lib/phlexi/form/field_options/required.rb +37 -0
- data/lib/phlexi/form/field_options/type.rb +155 -0
- data/lib/phlexi/form/field_options/validators.rb +48 -0
- data/lib/phlexi/form/option_mapper.rb +154 -0
- data/lib/phlexi/form/structure/dom.rb +57 -0
- data/lib/phlexi/form/structure/field_builder.rb +199 -0
- data/lib/phlexi/form/structure/field_collection.rb +45 -0
- data/lib/phlexi/form/structure/namespace.rb +123 -0
- data/lib/phlexi/form/structure/namespace_collection.rb +48 -0
- data/lib/phlexi/form/structure/node.rb +18 -0
- data/lib/phlexi/form/version.rb +7 -0
- data/lib/phlexi/form.rb +28 -0
- data/lib/phlexi-form.rb +3 -0
- data/sig/phlexi/form.rbs +6 -0
- metadata +243 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module Components
|
6
|
+
class Hint < Base
|
7
|
+
def view_template
|
8
|
+
p(**attributes) do
|
9
|
+
field.hint
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def render?
|
16
|
+
field.hint.present? && (!field.show_errors? || !field.has_errors?)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module Components
|
6
|
+
class Input < Base
|
7
|
+
def view_template
|
8
|
+
input(**attributes)
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def build_attributes
|
14
|
+
super
|
15
|
+
|
16
|
+
# only overwrite id if it was set in Base
|
17
|
+
attributes[:id] = field.dom.id if attributes[:id] == "#{field.dom.id}_#{component_name}"
|
18
|
+
attributes[:name] = field.dom.name
|
19
|
+
attributes[:value] = field.dom.value
|
20
|
+
|
21
|
+
build_input_attributes
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_input_attributes
|
25
|
+
attributes[:type] = attributes.fetch(:type, field.input_type)
|
26
|
+
attributes[:disabled] = attributes.fetch(:disabled, field.disabled?)
|
27
|
+
|
28
|
+
case attributes[:type]
|
29
|
+
when :text, :password, :email, :tel, :url, :search
|
30
|
+
attributes[:autofocus] = attributes.fetch(:autofocus, field.focused?)
|
31
|
+
attributes[:placeholder] = attributes.fetch(:placeholder, field.placeholder)
|
32
|
+
attributes[:minlength] = attributes.fetch(:minlength, field.minlength)
|
33
|
+
attributes[:maxlength] = attributes.fetch(:maxlength, field.maxlength)
|
34
|
+
attributes[:readonly] = attributes.fetch(:readonly, field.readonly?)
|
35
|
+
attributes[:required] = attributes.fetch(:required, field.required?)
|
36
|
+
attributes[:pattern] = attributes.fetch(:pattern, field.pattern)
|
37
|
+
when :number
|
38
|
+
attributes[:autofocus] = attributes.fetch(:autofocus, field.focused?)
|
39
|
+
attributes[:placeholder] = attributes.fetch(:placeholder, field.placeholder)
|
40
|
+
attributes[:readonly] = attributes.fetch(:readonly, field.readonly?)
|
41
|
+
attributes[:required] = attributes.fetch(:required, field.required?)
|
42
|
+
attributes[:min] = attributes.fetch(:min, field.min)
|
43
|
+
attributes[:max] = attributes.fetch(:max, field.max)
|
44
|
+
attributes[:step] = attributes.fetch(:step, field.step)
|
45
|
+
when :checkbox, :radio
|
46
|
+
attributes[:autofocus] = attributes.fetch(:autofocus, field.focused?)
|
47
|
+
attributes[:required] = attributes.fetch(:required, field.required?)
|
48
|
+
when :file
|
49
|
+
attributes[:autofocus] = attributes.fetch(:autofocus, field.focused?)
|
50
|
+
attributes[:required] = attributes.fetch(:required, field.required?)
|
51
|
+
attributes[:multiple] = attributes.fetch(:multiple, field.multiple)
|
52
|
+
attributes[:accept] = attributes.fetch(:accept, field.accept)
|
53
|
+
when :date, :time, :datetime_local
|
54
|
+
attributes[:autofocus] = attributes.fetch(:autofocus, field.focused?)
|
55
|
+
attributes[:readonly] = attributes.fetch(:readonly, field.readonly?)
|
56
|
+
attributes[:required] = attributes.fetch(:required, field.required?)
|
57
|
+
attributes[:min] = attributes.fetch(:min, field.min)
|
58
|
+
attributes[:max] = attributes.fetch(:max, field.max)
|
59
|
+
when :color
|
60
|
+
attributes[:autofocus] = attributes.fetch(:autofocus, field.focused?)
|
61
|
+
when :range
|
62
|
+
attributes[:autofocus] = attributes.fetch(:autofocus, field.focused?)
|
63
|
+
attributes[:min] = attributes.fetch(:min, field.min)
|
64
|
+
attributes[:max] = attributes.fetch(:max, field.max)
|
65
|
+
attributes[:step] = attributes.fetch(:step, field.step)
|
66
|
+
else
|
67
|
+
# Handle any unrecognized input types
|
68
|
+
# Rails.logger.warn("Unhandled input type: #{attributes[:type]}")
|
69
|
+
end
|
70
|
+
|
71
|
+
if (attributes[:type] == :file) ? attributes[:multiple] : attributes.delete(:multiple)
|
72
|
+
attributes[:name] = "#{attributes[:name].sub(/\[]$/, "")}[]"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module Components
|
6
|
+
class Label < Base
|
7
|
+
def view_template
|
8
|
+
label(**attributes) do
|
9
|
+
if field.required?
|
10
|
+
abbr(title: "required") { "*" }
|
11
|
+
whitespace
|
12
|
+
end
|
13
|
+
plain field.label
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def build_attributes
|
20
|
+
super
|
21
|
+
attributes[:for] ||= field.dom.id
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module Components
|
6
|
+
class RadioButton < Input
|
7
|
+
def view_template
|
8
|
+
input(**attributes, value: @checked_value)
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def build_input_attributes
|
14
|
+
attributes[:type] = :radio
|
15
|
+
super
|
16
|
+
|
17
|
+
@checked_value = (attributes.key?(:checked_value) ? attributes.delete(:checked_value) : "1").to_s
|
18
|
+
|
19
|
+
# this is a hack to workaround the fact that radio cannot be indexed/multiple
|
20
|
+
attributes[:name] = attributes[:name].sub(/\[]$/, "")
|
21
|
+
attributes[:value] = @checked_value
|
22
|
+
attributes[:checked] = attributes.fetch(:checked) { checked? }
|
23
|
+
end
|
24
|
+
|
25
|
+
def checked?
|
26
|
+
field.dom.value == @checked_value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module Components
|
6
|
+
class Select < Base
|
7
|
+
include Concerns::HasOptions
|
8
|
+
|
9
|
+
def view_template(&block)
|
10
|
+
select(**attributes) do
|
11
|
+
blank_option { blank_option_text } unless skip_blank_option?
|
12
|
+
options
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def options
|
19
|
+
option_mapper.each do |value, label|
|
20
|
+
option(selected: selected?(value), value: value) { label }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def blank_option(&)
|
25
|
+
option(selected: field.value.nil?, &)
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_attributes
|
29
|
+
super
|
30
|
+
|
31
|
+
attributes[:id] = field.dom.id
|
32
|
+
attributes[:name] = field.dom.name
|
33
|
+
|
34
|
+
build_select_attributes
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_select_attributes
|
38
|
+
@include_blank_option = attributes.delete(:include_blank_option)
|
39
|
+
|
40
|
+
attributes[:autofocus] = attributes.fetch(:autofocus, field.focused?)
|
41
|
+
attributes[:required] = attributes.fetch(:required, field.required?)
|
42
|
+
attributes[:disabled] = attributes.fetch(:disabled, field.disabled?)
|
43
|
+
attributes[:multiple] = attributes.fetch(:multiple, field.multiple?)
|
44
|
+
attributes[:size] = attributes.fetch(:size, field.limit)
|
45
|
+
end
|
46
|
+
|
47
|
+
def blank_option_text
|
48
|
+
field.placeholder
|
49
|
+
end
|
50
|
+
|
51
|
+
def skip_blank_option?
|
52
|
+
@include_blank_option == false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module Components
|
6
|
+
class Textarea < Base
|
7
|
+
def view_template
|
8
|
+
textarea(**attributes) { field.dom.value }
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def build_attributes
|
14
|
+
super
|
15
|
+
|
16
|
+
attributes[:id] = field.dom.id
|
17
|
+
attributes[:name] = field.dom.name
|
18
|
+
|
19
|
+
build_textarea_attributes
|
20
|
+
end
|
21
|
+
|
22
|
+
def build_textarea_attributes
|
23
|
+
attributes[:placeholder] = attributes.fetch(:placeholder, field.placeholder)
|
24
|
+
attributes[:autofocus] = attributes.fetch(:autofocus, field.focused?)
|
25
|
+
attributes[:minlength] = attributes.fetch(:minlength, field.minlength)
|
26
|
+
attributes[:maxlength] = attributes.fetch(:maxlength, field.maxlength)
|
27
|
+
attributes[:readonly] = attributes.fetch(:readonly, field.readonly?)
|
28
|
+
attributes[:required] = attributes.fetch(:required, field.required?)
|
29
|
+
attributes[:disabled] = attributes.fetch(:disabled, field.disabled?)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module Components
|
6
|
+
class Wrapper < Base
|
7
|
+
attr_reader :inner_attributes
|
8
|
+
|
9
|
+
def view_template
|
10
|
+
div(**attributes) do
|
11
|
+
render field.label_tag
|
12
|
+
div(**inner_attributes) do
|
13
|
+
yield field if block_given?
|
14
|
+
render field.full_error_tag
|
15
|
+
render field.hint_tag
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def build_attributes
|
23
|
+
super
|
24
|
+
|
25
|
+
@inner_attributes = attributes.delete(:inner) || {}
|
26
|
+
inner_attributes[:class] = tokens("inner-wrapper", inner_attributes[:class])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module FieldOptions
|
6
|
+
module Autofocus
|
7
|
+
def focused?
|
8
|
+
options[:autofocus] == true
|
9
|
+
end
|
10
|
+
|
11
|
+
def focus(autofocus = true)
|
12
|
+
options[:autofocus] = autofocus
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module FieldOptions
|
6
|
+
module Collection
|
7
|
+
def collection(collection = nil)
|
8
|
+
if collection.nil?
|
9
|
+
options[:collection] = options.fetch(:collection) { infer_collection }
|
10
|
+
else
|
11
|
+
options[:collection] = collection
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def infer_collection
|
19
|
+
if has_validators?
|
20
|
+
inclusion_validator = find_inclusion_validator
|
21
|
+
collection_value_from(inclusion_validator)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def collection_value_from(inclusion_validator)
|
26
|
+
if inclusion_validator
|
27
|
+
inclusion_validator.options[:in] || inclusion_validator.options[:within]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_inclusion_validator
|
32
|
+
find_validator(:inclusion)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module FieldOptions
|
6
|
+
module Disabled
|
7
|
+
def disabled?
|
8
|
+
options[:disabled] == true
|
9
|
+
end
|
10
|
+
|
11
|
+
def disabled!(disabled = true)
|
12
|
+
options[:disabled] = disabled
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module FieldOptions
|
6
|
+
module Errors
|
7
|
+
def custom_error(error)
|
8
|
+
options[:error] = error
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def error
|
13
|
+
error_text if has_errors?
|
14
|
+
end
|
15
|
+
|
16
|
+
def full_error
|
17
|
+
full_error_text if has_errors?
|
18
|
+
end
|
19
|
+
|
20
|
+
def has_errors?
|
21
|
+
object_with_errors? || !object && has_custom_error?
|
22
|
+
end
|
23
|
+
|
24
|
+
def show_errors?
|
25
|
+
options[:error] != false
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid?
|
29
|
+
!has_errors? && has_value?
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def error_text
|
35
|
+
text = has_custom_error? ? options[:error] : errors.send(error_method)
|
36
|
+
|
37
|
+
"#{options[:error_prefix]} #{text}".lstrip
|
38
|
+
end
|
39
|
+
|
40
|
+
def full_error_text
|
41
|
+
has_custom_error? ? options[:error] : full_errors.send(error_method)
|
42
|
+
end
|
43
|
+
|
44
|
+
def object_with_errors?
|
45
|
+
object&.respond_to?(:errors) && errors.present?
|
46
|
+
end
|
47
|
+
|
48
|
+
def error_method
|
49
|
+
options[:error_method] || :first
|
50
|
+
end
|
51
|
+
|
52
|
+
def errors
|
53
|
+
@errors ||= (errors_on_attribute + errors_on_association).compact
|
54
|
+
end
|
55
|
+
|
56
|
+
def full_errors
|
57
|
+
@full_errors ||= (full_errors_on_attribute + full_errors_on_association).compact
|
58
|
+
end
|
59
|
+
|
60
|
+
def errors_on_attribute
|
61
|
+
object.errors[key] || []
|
62
|
+
end
|
63
|
+
|
64
|
+
def full_errors_on_attribute
|
65
|
+
object.errors.full_messages_for(key)
|
66
|
+
end
|
67
|
+
|
68
|
+
def errors_on_association
|
69
|
+
reflection ? object.errors[reflection.name] : []
|
70
|
+
end
|
71
|
+
|
72
|
+
def full_errors_on_association
|
73
|
+
reflection ? object.errors.full_messages_for(reflection.name) : []
|
74
|
+
end
|
75
|
+
|
76
|
+
def has_custom_error?
|
77
|
+
options[:error].is_a?(String)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module FieldOptions
|
6
|
+
module Hints
|
7
|
+
def hint(hint = nil)
|
8
|
+
if hint.nil?
|
9
|
+
options[:hint]
|
10
|
+
else
|
11
|
+
options[:hint] = hint
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_hint?
|
17
|
+
options[:hint] != false && hint.present?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module FieldOptions
|
6
|
+
module Labels
|
7
|
+
def label(label = nil)
|
8
|
+
if label.nil?
|
9
|
+
options[:label] = options.fetch(:label) { calculate_label }
|
10
|
+
else
|
11
|
+
options[:label] = label
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def calculate_label
|
19
|
+
if object.class.respond_to?(:human_attribute_name)
|
20
|
+
object.class.human_attribute_name(key.to_s, {base: object})
|
21
|
+
else
|
22
|
+
key.to_s.humanize
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module FieldOptions
|
6
|
+
module Length
|
7
|
+
def minlength(minlength = nil)
|
8
|
+
if minlength.nil?
|
9
|
+
options[:minlength] = options.fetch(:minlength) { calculate_minlength }
|
10
|
+
else
|
11
|
+
options[:minlength] = minlength
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def maxlength(maxlength = nil)
|
17
|
+
if maxlength.nil?
|
18
|
+
options[:maxlength] = options.fetch(:maxlength) { calculate_maxlength }
|
19
|
+
else
|
20
|
+
options[:maxlength] = maxlength
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def calculate_minlength
|
28
|
+
minimum_length_value_from(find_length_validator)
|
29
|
+
end
|
30
|
+
|
31
|
+
def minimum_length_value_from(length_validator)
|
32
|
+
if length_validator
|
33
|
+
length_validator.options[:is] || length_validator.options[:minimum]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def calculate_maxlength
|
38
|
+
maximum_length_value_from(find_length_validator)
|
39
|
+
end
|
40
|
+
|
41
|
+
def maximum_length_value_from(length_validator)
|
42
|
+
if length_validator
|
43
|
+
length_validator.options[:is] || length_validator.options[:maximum]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_length_validator
|
48
|
+
find_validator(:length)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Form
|
5
|
+
module FieldOptions
|
6
|
+
module Limit
|
7
|
+
def limit(limit = nil)
|
8
|
+
if limit.nil?
|
9
|
+
options[:limit] = options.fetch(:limit) { calculate_limit }
|
10
|
+
else
|
11
|
+
options[:limit] = limit
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def calculate_limit
|
19
|
+
return unless multiple?
|
20
|
+
|
21
|
+
limit_from_validators = [
|
22
|
+
limit_from_length_validator,
|
23
|
+
limit_from_inclusion_validator
|
24
|
+
].compact.min
|
25
|
+
|
26
|
+
limit_from_validators || limit_from_db_column
|
27
|
+
end
|
28
|
+
|
29
|
+
def limit_from_length_validator
|
30
|
+
length_validator = find_validator(:length)
|
31
|
+
return unless length_validator
|
32
|
+
|
33
|
+
length_validator.options[:maximum]
|
34
|
+
end
|
35
|
+
|
36
|
+
def limit_from_inclusion_validator
|
37
|
+
return unless has_validators?
|
38
|
+
|
39
|
+
inclusion_validator = find_validator(:inclusion)
|
40
|
+
return unless inclusion_validator
|
41
|
+
|
42
|
+
in_option = inclusion_validator.options[:in]
|
43
|
+
in_option.is_a?(Array) ? in_option.size : nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def limit_from_db_column
|
47
|
+
return unless object.class.respond_to?(:columns_hash)
|
48
|
+
|
49
|
+
column = object.class.columns_hash[key.to_s]
|
50
|
+
return unless column
|
51
|
+
|
52
|
+
case object.class.connection.adapter_name.downcase
|
53
|
+
when "postgresql"
|
54
|
+
if column.array?
|
55
|
+
# Check if there's a limit on the array size
|
56
|
+
column.limit
|
57
|
+
elsif column.type == :string && column.sql_type.include?("[]")
|
58
|
+
# For string arrays, extract the limit if specified
|
59
|
+
column.sql_type.match(/\[(\d+)\]/)&.captures&.first&.to_i
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|