phlexi-display 0.0.1
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 +8 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +13 -0
- data/Rakefile +14 -0
- data/TODO +0 -0
- data/config.ru +6 -0
- data/gemfiles/default.gemfile +5 -0
- data/gemfiles/rails_7.gemfile +8 -0
- data/lib/phlexi/display/base.rb +243 -0
- data/lib/phlexi/display/components/base.rb +51 -0
- data/lib/phlexi/display/components/checkbox.rb +48 -0
- data/lib/phlexi/display/components/collection_checkboxes.rb +44 -0
- data/lib/phlexi/display/components/collection_radio_buttons.rb +35 -0
- data/lib/phlexi/display/components/concerns/handles_array_input.rb +21 -0
- data/lib/phlexi/display/components/concerns/handles_input.rb +53 -0
- data/lib/phlexi/display/components/concerns/has_options.rb +37 -0
- data/lib/phlexi/display/components/concerns/submits_form.rb +39 -0
- data/lib/phlexi/display/components/error.rb +21 -0
- data/lib/phlexi/display/components/file_input.rb +32 -0
- data/lib/phlexi/display/components/full_error.rb +21 -0
- data/lib/phlexi/display/components/hint.rb +21 -0
- data/lib/phlexi/display/components/input.rb +84 -0
- data/lib/phlexi/display/components/input_array.rb +45 -0
- data/lib/phlexi/display/components/label.rb +27 -0
- data/lib/phlexi/display/components/radio_button.rb +41 -0
- data/lib/phlexi/display/components/select.rb +69 -0
- data/lib/phlexi/display/components/submit_button.rb +41 -0
- data/lib/phlexi/display/components/textarea.rb +34 -0
- data/lib/phlexi/display/components/wrapper.rb +31 -0
- data/lib/phlexi/display/field_options/associations.rb +21 -0
- data/lib/phlexi/display/field_options/autofocus.rb +18 -0
- data/lib/phlexi/display/field_options/collection.rb +54 -0
- data/lib/phlexi/display/field_options/disabled.rb +18 -0
- data/lib/phlexi/display/field_options/errors.rb +92 -0
- data/lib/phlexi/display/field_options/hints.rb +22 -0
- data/lib/phlexi/display/field_options/inferred_types.rb +155 -0
- data/lib/phlexi/display/field_options/labels.rb +28 -0
- data/lib/phlexi/display/field_options/length.rb +53 -0
- data/lib/phlexi/display/field_options/limit.rb +66 -0
- data/lib/phlexi/display/field_options/min_max.rb +92 -0
- data/lib/phlexi/display/field_options/multiple.rb +65 -0
- data/lib/phlexi/display/field_options/pattern.rb +38 -0
- data/lib/phlexi/display/field_options/placeholder.rb +18 -0
- data/lib/phlexi/display/field_options/readonly.rb +18 -0
- data/lib/phlexi/display/field_options/required.rb +37 -0
- data/lib/phlexi/display/field_options/themes.rb +207 -0
- data/lib/phlexi/display/field_options/validators.rb +48 -0
- data/lib/phlexi/display/option_mapper.rb +154 -0
- data/lib/phlexi/display/structure/dom.rb +62 -0
- data/lib/phlexi/display/structure/field_builder.rb +236 -0
- data/lib/phlexi/display/structure/field_collection.rb +54 -0
- data/lib/phlexi/display/structure/namespace.rb +135 -0
- data/lib/phlexi/display/structure/namespace_collection.rb +48 -0
- data/lib/phlexi/display/structure/node.rb +18 -0
- data/lib/phlexi/display/version.rb +7 -0
- data/lib/phlexi/display.rb +31 -0
- data/lib/phlexi-display.rb +3 -0
- data/sig/phlexi/display.rbs +6 -0
- metadata +262 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
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,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bigdecimal"
|
4
|
+
|
5
|
+
module Phlexi
|
6
|
+
module Display
|
7
|
+
module FieldOptions
|
8
|
+
module InferredTypes
|
9
|
+
def inferred_db_type
|
10
|
+
@inferred_db_type ||= infer_db_type
|
11
|
+
end
|
12
|
+
|
13
|
+
def inferred_input_component
|
14
|
+
@inferred_input_component ||= infer_input_component
|
15
|
+
end
|
16
|
+
|
17
|
+
def inferred_input_type
|
18
|
+
@inferred_input_type ||= infer_input_type(inferred_input_component)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# this returns the element type
|
24
|
+
# one of :input, :textarea, :select, :botton
|
25
|
+
def infer_input_component
|
26
|
+
return :select unless collection.blank?
|
27
|
+
|
28
|
+
case inferred_db_type
|
29
|
+
when :text, :json, :jsonb, :hstore
|
30
|
+
:textarea
|
31
|
+
else
|
32
|
+
:input
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# this only applies when input_component is `:input`
|
37
|
+
# resolves the type attribute of input components
|
38
|
+
def infer_input_type(component)
|
39
|
+
case inferred_db_type
|
40
|
+
when :string
|
41
|
+
infer_string_input_type(key)
|
42
|
+
when :integer, :float, :decimal
|
43
|
+
:number
|
44
|
+
when :date
|
45
|
+
:date
|
46
|
+
when :datetime
|
47
|
+
:datetime
|
48
|
+
when :time
|
49
|
+
:time
|
50
|
+
when :boolean
|
51
|
+
:checkbox
|
52
|
+
else
|
53
|
+
:text
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def infer_db_type
|
58
|
+
if object.class.respond_to?(:columns_hash)
|
59
|
+
# ActiveRecord object
|
60
|
+
column = object.class.columns_hash[key.to_s]
|
61
|
+
return column.type if column
|
62
|
+
end
|
63
|
+
|
64
|
+
if object.class.respond_to?(:attribute_types)
|
65
|
+
# ActiveModel::Attributes
|
66
|
+
custom_type = object.class.attribute_types[key.to_s]
|
67
|
+
return custom_type.type if custom_type
|
68
|
+
end
|
69
|
+
|
70
|
+
# Check if object responds to the key
|
71
|
+
if object.respond_to?(key)
|
72
|
+
# Fallback to inferring type from the value
|
73
|
+
return infer_db_type_from_value(object.send(key))
|
74
|
+
end
|
75
|
+
|
76
|
+
# Default to string if we can't determine the type
|
77
|
+
:string
|
78
|
+
end
|
79
|
+
|
80
|
+
def infer_db_type_from_value(value)
|
81
|
+
case value
|
82
|
+
when Integer
|
83
|
+
:integer
|
84
|
+
when Float, BigDecimal
|
85
|
+
:float
|
86
|
+
when TrueClass, FalseClass
|
87
|
+
:boolean
|
88
|
+
when Date
|
89
|
+
:date
|
90
|
+
when Time, DateTime
|
91
|
+
:datetime
|
92
|
+
else
|
93
|
+
:string
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def infer_string_input_type(key)
|
98
|
+
key = key.to_s.downcase
|
99
|
+
|
100
|
+
return :password if is_password_field?
|
101
|
+
|
102
|
+
custom_type = custom_string_input_type(key)
|
103
|
+
return custom_type if custom_type
|
104
|
+
|
105
|
+
if has_validators?
|
106
|
+
infer_string_input_type_from_validations
|
107
|
+
else
|
108
|
+
:text
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def custom_string_input_type(key)
|
113
|
+
custom_mappings = {
|
114
|
+
/url$|^link|^site/ => :url,
|
115
|
+
/^email/ => :email,
|
116
|
+
/^search/ => :search,
|
117
|
+
/phone|tel(ephone)?/ => :tel,
|
118
|
+
/^time/ => :time,
|
119
|
+
/^date/ => :date,
|
120
|
+
/^number|_count$|_amount$/ => :number,
|
121
|
+
/^color/ => :color
|
122
|
+
}
|
123
|
+
|
124
|
+
custom_mappings.each do |pattern, type|
|
125
|
+
return type if key.match?(pattern)
|
126
|
+
end
|
127
|
+
|
128
|
+
nil
|
129
|
+
end
|
130
|
+
|
131
|
+
def infer_string_input_type_from_validations
|
132
|
+
if attribute_validators.find { |v| v.kind == :numericality }
|
133
|
+
:number
|
134
|
+
elsif attribute_validators.find { |v| v.kind == :format && v.options[:with] == URI::MailTo::EMAIL_REGEXP }
|
135
|
+
:email
|
136
|
+
else
|
137
|
+
:text
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def is_password_field?
|
142
|
+
key = self.key.to_s.downcase
|
143
|
+
|
144
|
+
exact_matches = ["password"]
|
145
|
+
prefixes = ["encrypted_"]
|
146
|
+
suffixes = ["_password", "_digest", "_hash"]
|
147
|
+
|
148
|
+
exact_matches.include?(key) ||
|
149
|
+
prefixes.any? { |prefix| key.start_with?(prefix) } ||
|
150
|
+
suffixes.any? { |suffix| key.end_with?(suffix) }
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
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 Display
|
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 Display
|
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
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module FieldOptions
|
6
|
+
module MinMax
|
7
|
+
def min(min_value = nil)
|
8
|
+
if min_value.nil?
|
9
|
+
options[:min] = options.fetch(:min) { calculate_min }
|
10
|
+
else
|
11
|
+
options[:min] = min_value
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def max(max_value = nil)
|
17
|
+
if max_value.nil?
|
18
|
+
options[:max] = options.fetch(:max) { calculate_max }
|
19
|
+
else
|
20
|
+
options[:max] = max_value
|
21
|
+
self
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def step
|
26
|
+
1 if min || max
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def calculate_min
|
32
|
+
if (numericality_validator = find_numericality_validator)
|
33
|
+
get_min_from_validator(numericality_validator)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def calculate_max
|
38
|
+
if (numericality_validator = find_numericality_validator)
|
39
|
+
get_max_from_validator(numericality_validator)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def find_numericality_validator
|
44
|
+
find_validator(:numericality)
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_min_from_validator(validator)
|
48
|
+
options = validator.options
|
49
|
+
min = if options.key?(:greater_than)
|
50
|
+
{value: options[:greater_than], exclusive: true}
|
51
|
+
elsif options.key?(:greater_than_or_equal_to)
|
52
|
+
{value: options[:greater_than_or_equal_to], exclusive: false}
|
53
|
+
end
|
54
|
+
evaluate_and_adjust_min(min)
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_max_from_validator(validator)
|
58
|
+
options = validator.options
|
59
|
+
max = if options.key?(:less_than)
|
60
|
+
{value: options[:less_than], exclusive: true}
|
61
|
+
elsif options.key?(:less_than_or_equal_to)
|
62
|
+
{value: options[:less_than_or_equal_to], exclusive: false}
|
63
|
+
end
|
64
|
+
evaluate_and_adjust_max(max)
|
65
|
+
end
|
66
|
+
|
67
|
+
def evaluate_and_adjust_min(min)
|
68
|
+
return nil unless min
|
69
|
+
|
70
|
+
value = evaluate_numericality_validator_option(min[:value])
|
71
|
+
min[:exclusive] ? value + 1 : value
|
72
|
+
end
|
73
|
+
|
74
|
+
def evaluate_and_adjust_max(max)
|
75
|
+
return nil unless max
|
76
|
+
|
77
|
+
value = evaluate_numericality_validator_option(max[:value])
|
78
|
+
max[:exclusive] ? value - 1 : value
|
79
|
+
end
|
80
|
+
|
81
|
+
def evaluate_numericality_validator_option(option)
|
82
|
+
case option
|
83
|
+
when Proc
|
84
|
+
option.arity.zero? ? option.call : option.call(object)
|
85
|
+
else
|
86
|
+
option
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module FieldOptions
|
6
|
+
module Multiple
|
7
|
+
def multiple?
|
8
|
+
options[:multiple] = options.fetch(:multiple) { calculate_multiple_field_value }
|
9
|
+
end
|
10
|
+
|
11
|
+
def multiple!(multiple = true)
|
12
|
+
options[:multiple] = multiple
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def calculate_multiple_field_value
|
19
|
+
return true if reflection&.macro == :has_many
|
20
|
+
return true if multiple_field_array_attribute?
|
21
|
+
|
22
|
+
check_multiple_field_from_validators
|
23
|
+
end
|
24
|
+
|
25
|
+
def multiple_field_array_attribute?
|
26
|
+
return false unless object.class.respond_to?(:columns_hash)
|
27
|
+
|
28
|
+
column = object.class.columns_hash[key.to_s]
|
29
|
+
return false unless column
|
30
|
+
|
31
|
+
case object.class.connection.adapter_name.downcase
|
32
|
+
when "postgresql"
|
33
|
+
column.array? || (column.type == :string && column.sql_type.include?("[]"))
|
34
|
+
end # || object.class.attribute_types[key.to_s].is_a?(ActiveRecord::Type::Serialized)
|
35
|
+
rescue
|
36
|
+
# Rails.logger.warn("Error checking multiple field array attribute: #{e.message}")
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
def check_multiple_field_from_validators
|
41
|
+
inclusion_validator = find_validator(:inclusion)
|
42
|
+
length_validator = find_validator(:length)
|
43
|
+
|
44
|
+
return false unless inclusion_validator || length_validator
|
45
|
+
|
46
|
+
check_multiple_field_inclusion_validator(inclusion_validator) ||
|
47
|
+
check_multiple_field_length_validator(length_validator)
|
48
|
+
end
|
49
|
+
|
50
|
+
def check_multiple_field_inclusion_validator(validator)
|
51
|
+
return false unless validator
|
52
|
+
in_option = validator.options[:in]
|
53
|
+
return false unless in_option.is_a?(Array)
|
54
|
+
|
55
|
+
validator.options[:multiple] == true || (multiple_field_array_attribute? && in_option.size > 1)
|
56
|
+
end
|
57
|
+
|
58
|
+
def check_multiple_field_length_validator(validator)
|
59
|
+
return false unless validator
|
60
|
+
validator.options[:maximum].to_i > 1 if validator.options[:maximum]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module FieldOptions
|
6
|
+
module Pattern
|
7
|
+
def pattern(pattern = nil)
|
8
|
+
if pattern.nil?
|
9
|
+
options[:pattern] = options.fetch(:pattern) { calculate_pattern }
|
10
|
+
else
|
11
|
+
options[:pattern] = pattern
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def calculate_pattern
|
19
|
+
if (pattern_validator = find_pattern_validator) && (with = pattern_validator.options[:with])
|
20
|
+
evaluate_format_validator_option(with).source
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_pattern_validator
|
25
|
+
find_validator(:format)
|
26
|
+
end
|
27
|
+
|
28
|
+
def evaluate_format_validator_option(option)
|
29
|
+
if option.respond_to?(:call)
|
30
|
+
option.call(object)
|
31
|
+
else
|
32
|
+
option
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module FieldOptions
|
6
|
+
module Placeholder
|
7
|
+
def placeholder(placeholder = nil)
|
8
|
+
if placeholder.nil?
|
9
|
+
options[:placeholder]
|
10
|
+
else
|
11
|
+
options[:placeholder] = placeholder
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Display
|
5
|
+
module FieldOptions
|
6
|
+
module Readonly
|
7
|
+
def readonly?
|
8
|
+
options[:readonly] == true
|
9
|
+
end
|
10
|
+
|
11
|
+
def readonly!(readonly = true)
|
12
|
+
options[:readonly] = readonly
|
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 Display
|
5
|
+
module FieldOptions
|
6
|
+
module Required
|
7
|
+
def required?
|
8
|
+
options[:required] = options.fetch(:required) { calculate_required }
|
9
|
+
end
|
10
|
+
|
11
|
+
def required!(required = true)
|
12
|
+
options[:required] = required
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def calculate_required
|
19
|
+
if has_validators?
|
20
|
+
required_by_validators?
|
21
|
+
else
|
22
|
+
required_by_default?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def required_by_validators?
|
27
|
+
(attribute_validators + reflection_validators).any? { |v| v.kind == :presence && valid_validator?(v) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def required_by_default?
|
31
|
+
# TODO: get this from configuration
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|