phlexi-field 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 +196 -0
- data/Rakefile +14 -0
- data/TODO +4 -0
- data/config.ru +6 -0
- data/gemfiles/default.gemfile +5 -0
- data/gemfiles/default.gemfile.lock +194 -0
- data/gemfiles/rails_7.gemfile +8 -0
- data/gemfiles/rails_7.gemfile.lock +282 -0
- data/lib/phlexi/field/builder.rb +72 -0
- data/lib/phlexi/field/components/base.rb +35 -0
- data/lib/phlexi/field/options/associations.rb +21 -0
- data/lib/phlexi/field/options/attachments.rb +21 -0
- data/lib/phlexi/field/options/descriptions.rb +22 -0
- data/lib/phlexi/field/options/hints.rb +22 -0
- data/lib/phlexi/field/options/inferred_types.rb +130 -0
- data/lib/phlexi/field/options/labels.rb +28 -0
- data/lib/phlexi/field/options/placeholders.rb +19 -0
- data/lib/phlexi/field/structure/dom.rb +62 -0
- data/lib/phlexi/field/structure/field_collection.rb +45 -0
- data/lib/phlexi/field/structure/namespace.rb +124 -0
- data/lib/phlexi/field/structure/namespace_collection.rb +40 -0
- data/lib/phlexi/field/structure/node.rb +28 -0
- data/lib/phlexi/field/theme.rb +61 -0
- data/lib/phlexi/field/version.rb +7 -0
- data/lib/phlexi/field.rb +26 -0
- data/lib/phlexi-field.rb +3 -0
- data/sig/phlexi/field.rbs +6 -0
- metadata +247 -0
@@ -0,0 +1,282 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
phlexi-form (0.3.0.rc1)
|
5
|
+
activesupport
|
6
|
+
phlex (~> 1.11)
|
7
|
+
zeitwerk
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
actioncable (7.1.3.4)
|
13
|
+
actionpack (= 7.1.3.4)
|
14
|
+
activesupport (= 7.1.3.4)
|
15
|
+
nio4r (~> 2.0)
|
16
|
+
websocket-driver (>= 0.6.1)
|
17
|
+
zeitwerk (~> 2.6)
|
18
|
+
actionmailbox (7.1.3.4)
|
19
|
+
actionpack (= 7.1.3.4)
|
20
|
+
activejob (= 7.1.3.4)
|
21
|
+
activerecord (= 7.1.3.4)
|
22
|
+
activestorage (= 7.1.3.4)
|
23
|
+
activesupport (= 7.1.3.4)
|
24
|
+
mail (>= 2.7.1)
|
25
|
+
net-imap
|
26
|
+
net-pop
|
27
|
+
net-smtp
|
28
|
+
actionmailer (7.1.3.4)
|
29
|
+
actionpack (= 7.1.3.4)
|
30
|
+
actionview (= 7.1.3.4)
|
31
|
+
activejob (= 7.1.3.4)
|
32
|
+
activesupport (= 7.1.3.4)
|
33
|
+
mail (~> 2.5, >= 2.5.4)
|
34
|
+
net-imap
|
35
|
+
net-pop
|
36
|
+
net-smtp
|
37
|
+
rails-dom-testing (~> 2.2)
|
38
|
+
actionpack (7.1.3.4)
|
39
|
+
actionview (= 7.1.3.4)
|
40
|
+
activesupport (= 7.1.3.4)
|
41
|
+
nokogiri (>= 1.8.5)
|
42
|
+
racc
|
43
|
+
rack (>= 2.2.4)
|
44
|
+
rack-session (>= 1.0.1)
|
45
|
+
rack-test (>= 0.6.3)
|
46
|
+
rails-dom-testing (~> 2.2)
|
47
|
+
rails-html-sanitizer (~> 1.6)
|
48
|
+
actiontext (7.1.3.4)
|
49
|
+
actionpack (= 7.1.3.4)
|
50
|
+
activerecord (= 7.1.3.4)
|
51
|
+
activestorage (= 7.1.3.4)
|
52
|
+
activesupport (= 7.1.3.4)
|
53
|
+
globalid (>= 0.6.0)
|
54
|
+
nokogiri (>= 1.8.5)
|
55
|
+
actionview (7.1.3.4)
|
56
|
+
activesupport (= 7.1.3.4)
|
57
|
+
builder (~> 3.1)
|
58
|
+
erubi (~> 1.11)
|
59
|
+
rails-dom-testing (~> 2.2)
|
60
|
+
rails-html-sanitizer (~> 1.6)
|
61
|
+
activejob (7.1.3.4)
|
62
|
+
activesupport (= 7.1.3.4)
|
63
|
+
globalid (>= 0.3.6)
|
64
|
+
activemodel (7.1.3.4)
|
65
|
+
activesupport (= 7.1.3.4)
|
66
|
+
activerecord (7.1.3.4)
|
67
|
+
activemodel (= 7.1.3.4)
|
68
|
+
activesupport (= 7.1.3.4)
|
69
|
+
timeout (>= 0.4.0)
|
70
|
+
activestorage (7.1.3.4)
|
71
|
+
actionpack (= 7.1.3.4)
|
72
|
+
activejob (= 7.1.3.4)
|
73
|
+
activerecord (= 7.1.3.4)
|
74
|
+
activesupport (= 7.1.3.4)
|
75
|
+
marcel (~> 1.0)
|
76
|
+
activesupport (7.1.3.4)
|
77
|
+
base64
|
78
|
+
bigdecimal
|
79
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
80
|
+
connection_pool (>= 2.2.5)
|
81
|
+
drb
|
82
|
+
i18n (>= 1.6, < 2)
|
83
|
+
minitest (>= 5.1)
|
84
|
+
mutex_m
|
85
|
+
tzinfo (~> 2.0)
|
86
|
+
addressable (2.8.7)
|
87
|
+
public_suffix (>= 2.0.2, < 7.0)
|
88
|
+
ansi (1.5.0)
|
89
|
+
appraisal (2.5.0)
|
90
|
+
bundler
|
91
|
+
rake
|
92
|
+
thor (>= 0.14.0)
|
93
|
+
ast (2.4.2)
|
94
|
+
base64 (0.2.0)
|
95
|
+
bigdecimal (3.1.8)
|
96
|
+
builder (3.3.0)
|
97
|
+
bundle-audit (0.1.0)
|
98
|
+
bundler-audit
|
99
|
+
bundler-audit (0.9.1)
|
100
|
+
bundler (>= 1.2.0, < 3)
|
101
|
+
thor (~> 1.0)
|
102
|
+
capybara (3.40.0)
|
103
|
+
addressable
|
104
|
+
matrix
|
105
|
+
mini_mime (>= 0.1.3)
|
106
|
+
nokogiri (~> 1.11)
|
107
|
+
rack (>= 1.6.0)
|
108
|
+
rack-test (>= 0.6.3)
|
109
|
+
regexp_parser (>= 1.5, < 3.0)
|
110
|
+
xpath (~> 3.2)
|
111
|
+
combustion (1.5.0)
|
112
|
+
activesupport (>= 3.0.0)
|
113
|
+
railties (>= 3.0.0)
|
114
|
+
thor (>= 0.14.6)
|
115
|
+
concurrent-ruby (1.3.3)
|
116
|
+
connection_pool (2.4.1)
|
117
|
+
crass (1.0.6)
|
118
|
+
date (3.3.4)
|
119
|
+
drb (2.2.1)
|
120
|
+
erubi (1.13.0)
|
121
|
+
globalid (1.2.1)
|
122
|
+
activesupport (>= 6.1)
|
123
|
+
i18n (1.14.5)
|
124
|
+
concurrent-ruby (~> 1.0)
|
125
|
+
io-console (0.7.2)
|
126
|
+
irb (1.14.0)
|
127
|
+
rdoc (>= 4.0.0)
|
128
|
+
reline (>= 0.4.2)
|
129
|
+
json (2.7.2)
|
130
|
+
language_server-protocol (3.17.0.3)
|
131
|
+
lint_roller (1.1.0)
|
132
|
+
loofah (2.22.0)
|
133
|
+
crass (~> 1.0.2)
|
134
|
+
nokogiri (>= 1.12.0)
|
135
|
+
mail (2.8.1)
|
136
|
+
mini_mime (>= 0.1.1)
|
137
|
+
net-imap
|
138
|
+
net-pop
|
139
|
+
net-smtp
|
140
|
+
marcel (1.0.4)
|
141
|
+
matrix (0.4.2)
|
142
|
+
mini_mime (1.1.5)
|
143
|
+
minitest (5.24.1)
|
144
|
+
minitest-reporters (1.7.1)
|
145
|
+
ansi
|
146
|
+
builder
|
147
|
+
minitest (>= 5.0)
|
148
|
+
ruby-progressbar
|
149
|
+
mutex_m (0.2.0)
|
150
|
+
net-imap (0.4.14)
|
151
|
+
date
|
152
|
+
net-protocol
|
153
|
+
net-pop (0.1.2)
|
154
|
+
net-protocol
|
155
|
+
net-protocol (0.2.2)
|
156
|
+
timeout
|
157
|
+
net-smtp (0.5.0)
|
158
|
+
net-protocol
|
159
|
+
nio4r (2.7.3)
|
160
|
+
nokogiri (1.16.7-x86_64-darwin)
|
161
|
+
racc (~> 1.4)
|
162
|
+
parallel (1.25.1)
|
163
|
+
parser (3.3.4.0)
|
164
|
+
ast (~> 2.4.1)
|
165
|
+
racc
|
166
|
+
phlex (1.11.0)
|
167
|
+
phlex-testing-capybara (0.1.0)
|
168
|
+
capybara (~> 3.38)
|
169
|
+
phlex (>= 0.5)
|
170
|
+
psych (5.1.2)
|
171
|
+
stringio
|
172
|
+
public_suffix (6.0.1)
|
173
|
+
racc (1.8.1)
|
174
|
+
rack (3.1.7)
|
175
|
+
rack-session (2.0.0)
|
176
|
+
rack (>= 3.0.0)
|
177
|
+
rack-test (2.1.0)
|
178
|
+
rack (>= 1.3)
|
179
|
+
rackup (2.1.0)
|
180
|
+
rack (>= 3)
|
181
|
+
webrick (~> 1.8)
|
182
|
+
rails (7.1.3.4)
|
183
|
+
actioncable (= 7.1.3.4)
|
184
|
+
actionmailbox (= 7.1.3.4)
|
185
|
+
actionmailer (= 7.1.3.4)
|
186
|
+
actionpack (= 7.1.3.4)
|
187
|
+
actiontext (= 7.1.3.4)
|
188
|
+
actionview (= 7.1.3.4)
|
189
|
+
activejob (= 7.1.3.4)
|
190
|
+
activemodel (= 7.1.3.4)
|
191
|
+
activerecord (= 7.1.3.4)
|
192
|
+
activestorage (= 7.1.3.4)
|
193
|
+
activesupport (= 7.1.3.4)
|
194
|
+
bundler (>= 1.15.0)
|
195
|
+
railties (= 7.1.3.4)
|
196
|
+
rails-dom-testing (2.2.0)
|
197
|
+
activesupport (>= 5.0.0)
|
198
|
+
minitest
|
199
|
+
nokogiri (>= 1.6)
|
200
|
+
rails-html-sanitizer (1.6.0)
|
201
|
+
loofah (~> 2.21)
|
202
|
+
nokogiri (~> 1.14)
|
203
|
+
railties (7.1.3.4)
|
204
|
+
actionpack (= 7.1.3.4)
|
205
|
+
activesupport (= 7.1.3.4)
|
206
|
+
irb
|
207
|
+
rackup (>= 1.0.0)
|
208
|
+
rake (>= 12.2)
|
209
|
+
thor (~> 1.0, >= 1.2.2)
|
210
|
+
zeitwerk (~> 2.6)
|
211
|
+
rainbow (3.1.1)
|
212
|
+
rake (13.2.1)
|
213
|
+
rdoc (6.7.0)
|
214
|
+
psych (>= 4.0.0)
|
215
|
+
regexp_parser (2.9.2)
|
216
|
+
reline (0.5.9)
|
217
|
+
io-console (~> 0.5)
|
218
|
+
rexml (3.3.2)
|
219
|
+
strscan
|
220
|
+
rubocop (1.64.1)
|
221
|
+
json (~> 2.3)
|
222
|
+
language_server-protocol (>= 3.17.0)
|
223
|
+
parallel (~> 1.10)
|
224
|
+
parser (>= 3.3.0.2)
|
225
|
+
rainbow (>= 2.2.2, < 4.0)
|
226
|
+
regexp_parser (>= 1.8, < 3.0)
|
227
|
+
rexml (>= 3.2.5, < 4.0)
|
228
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
229
|
+
ruby-progressbar (~> 1.7)
|
230
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
231
|
+
rubocop-ast (1.31.3)
|
232
|
+
parser (>= 3.3.1.0)
|
233
|
+
rubocop-performance (1.21.1)
|
234
|
+
rubocop (>= 1.48.1, < 2.0)
|
235
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
236
|
+
ruby-progressbar (1.13.0)
|
237
|
+
sqlite3 (1.7.3-x86_64-darwin)
|
238
|
+
standard (1.39.2)
|
239
|
+
language_server-protocol (~> 3.17.0.2)
|
240
|
+
lint_roller (~> 1.0)
|
241
|
+
rubocop (~> 1.64.0)
|
242
|
+
standard-custom (~> 1.0.0)
|
243
|
+
standard-performance (~> 1.4)
|
244
|
+
standard-custom (1.0.2)
|
245
|
+
lint_roller (~> 1.0)
|
246
|
+
rubocop (~> 1.50)
|
247
|
+
standard-performance (1.4.0)
|
248
|
+
lint_roller (~> 1.1)
|
249
|
+
rubocop-performance (~> 1.21.0)
|
250
|
+
stringio (3.1.1)
|
251
|
+
strscan (3.1.0)
|
252
|
+
thor (1.3.1)
|
253
|
+
timeout (0.4.1)
|
254
|
+
tzinfo (2.0.6)
|
255
|
+
concurrent-ruby (~> 1.0)
|
256
|
+
unicode-display_width (2.5.0)
|
257
|
+
webrick (1.8.1)
|
258
|
+
websocket-driver (0.7.6)
|
259
|
+
websocket-extensions (>= 0.1.0)
|
260
|
+
websocket-extensions (0.1.5)
|
261
|
+
xpath (3.2.0)
|
262
|
+
nokogiri (~> 1.8)
|
263
|
+
zeitwerk (2.6.17)
|
264
|
+
|
265
|
+
PLATFORMS
|
266
|
+
x86_64-darwin
|
267
|
+
|
268
|
+
DEPENDENCIES
|
269
|
+
appraisal
|
270
|
+
bundle-audit
|
271
|
+
combustion
|
272
|
+
minitest
|
273
|
+
minitest-reporters
|
274
|
+
phlex-testing-capybara
|
275
|
+
phlexi-form!
|
276
|
+
rails (~> 7.1.3, >= 7.1.3.4)
|
277
|
+
rake
|
278
|
+
sqlite3 (~> 1.4)
|
279
|
+
standard
|
280
|
+
|
281
|
+
BUNDLED WITH
|
282
|
+
2.5.16
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "phlex"
|
4
|
+
|
5
|
+
module Phlexi
|
6
|
+
module Field
|
7
|
+
# Builder class is responsible for building fields with various options and components.
|
8
|
+
#
|
9
|
+
# @attr_reader [Structure::DOM] dom The DOM structure for the field.
|
10
|
+
# @attr_reader [Hash] options Options for the field.
|
11
|
+
# @attr_reader [Object] object The object associated with the field.
|
12
|
+
# @attr_reader [Hash] attributes Attributes for the field.
|
13
|
+
# @attr_accessor [Object] value The value of the field.
|
14
|
+
class Builder < Structure::Node
|
15
|
+
include Phlex::Helpers
|
16
|
+
include Options::Associations
|
17
|
+
include Options::Attachments
|
18
|
+
include Options::Descriptions
|
19
|
+
include Options::Hints
|
20
|
+
include Options::InferredTypes
|
21
|
+
include Options::Labels
|
22
|
+
include Options::Placeholders
|
23
|
+
|
24
|
+
class DOM < Structure::DOM; end
|
25
|
+
|
26
|
+
class FieldCollection < Structure::FieldCollection; end
|
27
|
+
|
28
|
+
attr_reader :dom, :options, :object, :value
|
29
|
+
|
30
|
+
# Initializes a new FieldBuilder instance.
|
31
|
+
#
|
32
|
+
# @param key [Symbol, String] The key for the field.
|
33
|
+
# @param parent [Structure::Namespace] The parent object.
|
34
|
+
# @param object [Object, nil] The associated object.
|
35
|
+
# @param value [Object] The initial value for the field.
|
36
|
+
# @param options [Hash] Additional options for the field.
|
37
|
+
def initialize(key, parent:, object: nil, value: NIL_VALUE, **options)
|
38
|
+
super(key, parent: parent)
|
39
|
+
|
40
|
+
@object = object
|
41
|
+
@value = determine_initial_value(value)
|
42
|
+
@options = options
|
43
|
+
@dom = self.class::DOM.new(field: self)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Creates a repeated field collection.
|
47
|
+
#
|
48
|
+
# @param range [#each] The collection of items to generate displays for.
|
49
|
+
# @yield [block] The block to be executed for each item in the collection.
|
50
|
+
# @return [FieldCollection] The field collection.
|
51
|
+
def repeated(collection = nil, &)
|
52
|
+
self.class::FieldCollection.new(field: self, collection:, &)
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def has_value?
|
58
|
+
value.present?
|
59
|
+
end
|
60
|
+
|
61
|
+
def determine_initial_value(value)
|
62
|
+
return value unless value == NIL_VALUE
|
63
|
+
|
64
|
+
determine_value_from_object
|
65
|
+
end
|
66
|
+
|
67
|
+
def determine_value_from_object
|
68
|
+
object.respond_to?(key) ? object.public_send(key) : nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Field
|
5
|
+
module Components
|
6
|
+
class Base < COMPONENT_BASE
|
7
|
+
attr_reader :field, :attributes
|
8
|
+
|
9
|
+
def initialize(field, **attributes)
|
10
|
+
@field = field
|
11
|
+
@attributes = attributes
|
12
|
+
|
13
|
+
build_attributes
|
14
|
+
build_component_class
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def build_attributes
|
20
|
+
attributes.fetch(:id) { attributes[:id] = "#{field.dom.id}_#{component_name}" }
|
21
|
+
end
|
22
|
+
|
23
|
+
def build_component_class
|
24
|
+
return if attributes[:class] == false
|
25
|
+
|
26
|
+
attributes[:class] = tokens(component_name, attributes[:class])
|
27
|
+
end
|
28
|
+
|
29
|
+
def component_name
|
30
|
+
@component_name ||= self.class.name.demodulize.underscore
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Field
|
5
|
+
module Options
|
6
|
+
module Associations
|
7
|
+
protected
|
8
|
+
|
9
|
+
def association_reflection
|
10
|
+
@association_reflection ||= find_association_reflection
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_association_reflection
|
14
|
+
if object.class.respond_to?(:reflect_on_association)
|
15
|
+
object.class.reflect_on_association(key)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Field
|
5
|
+
module Options
|
6
|
+
module Attachments
|
7
|
+
protected
|
8
|
+
|
9
|
+
def attachment_reflection
|
10
|
+
@attachment_reflection ||= find_attachment_reflection
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_attachment_reflection
|
14
|
+
if object.class.respond_to?(:reflect_on_attachment)
|
15
|
+
object.class.reflect_on_attachment(key)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Field
|
5
|
+
module Options
|
6
|
+
module Descriptions
|
7
|
+
def description(description = nil)
|
8
|
+
if description.nil?
|
9
|
+
options[:description]
|
10
|
+
else
|
11
|
+
options[:description] = description
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def has_description?
|
17
|
+
description.present?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Field
|
5
|
+
module Options
|
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
|
+
hint.present?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bigdecimal"
|
4
|
+
|
5
|
+
module Phlexi
|
6
|
+
module Field
|
7
|
+
module Options
|
8
|
+
module InferredTypes
|
9
|
+
def inferred_field_type
|
10
|
+
@inferred_field_type ||= infer_field_type
|
11
|
+
end
|
12
|
+
|
13
|
+
def inferred_field_component
|
14
|
+
@inferred_component ||= infer_field_component
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def infer_field_component
|
20
|
+
case inferred_field_type
|
21
|
+
when :string, :text
|
22
|
+
infer_string_field_component(key)
|
23
|
+
when :integer, :float, :decimal
|
24
|
+
:number
|
25
|
+
when :date, :datetime, :time
|
26
|
+
:date
|
27
|
+
when :boolean
|
28
|
+
:boolean
|
29
|
+
when :json, :jsonb, :hstore
|
30
|
+
:code
|
31
|
+
else
|
32
|
+
if association_reflection
|
33
|
+
:association
|
34
|
+
elsif attachment_reflection
|
35
|
+
:attachment
|
36
|
+
else
|
37
|
+
:text
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def infer_field_type
|
43
|
+
if object.class.respond_to?(:columns_hash)
|
44
|
+
# ActiveRecord object
|
45
|
+
column = object.class.columns_hash[key.to_s]
|
46
|
+
return column.type if column
|
47
|
+
end
|
48
|
+
|
49
|
+
if object.class.respond_to?(:attribute_types)
|
50
|
+
# ActiveModel::Attributes
|
51
|
+
custom_type = object.class.attribute_types[key.to_s]
|
52
|
+
return custom_type.type if custom_type&.type
|
53
|
+
end
|
54
|
+
|
55
|
+
# Check if object responds to the key
|
56
|
+
if object.respond_to?(key)
|
57
|
+
# Fallback to inferring type from the value
|
58
|
+
return infer_field_type_from_value(object.send(key))
|
59
|
+
end
|
60
|
+
|
61
|
+
# Default to string if we can't determine the type
|
62
|
+
:string
|
63
|
+
end
|
64
|
+
|
65
|
+
def infer_field_type_from_value(value)
|
66
|
+
case value
|
67
|
+
when Integer
|
68
|
+
:integer
|
69
|
+
when Float
|
70
|
+
:float
|
71
|
+
when BigDecimal
|
72
|
+
:decimal
|
73
|
+
when TrueClass, FalseClass
|
74
|
+
:boolean
|
75
|
+
when Date
|
76
|
+
:date
|
77
|
+
when Time, DateTime
|
78
|
+
:datetime
|
79
|
+
when Hash
|
80
|
+
:json
|
81
|
+
else
|
82
|
+
:string
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def infer_string_field_component(key)
|
87
|
+
key = key.to_s.downcase
|
88
|
+
|
89
|
+
return :password if is_password_field?
|
90
|
+
|
91
|
+
custom_type = custom_string_field_type(key)
|
92
|
+
return custom_type if custom_type
|
93
|
+
|
94
|
+
:text
|
95
|
+
end
|
96
|
+
|
97
|
+
def custom_string_field_type(key)
|
98
|
+
custom_mappings = {
|
99
|
+
/url$|^link|^site/ => :url,
|
100
|
+
/^email/ => :email,
|
101
|
+
/^search/ => :search,
|
102
|
+
/phone|tel(ephone)?/ => :phone,
|
103
|
+
/^time/ => :time,
|
104
|
+
/^date/ => :date,
|
105
|
+
/^number|_count$|_amount$/ => :number,
|
106
|
+
/^color/ => :color
|
107
|
+
}
|
108
|
+
|
109
|
+
custom_mappings.each do |pattern, type|
|
110
|
+
return type if key.match?(pattern)
|
111
|
+
end
|
112
|
+
|
113
|
+
nil
|
114
|
+
end
|
115
|
+
|
116
|
+
def is_password_field?
|
117
|
+
key = self.key.to_s.downcase
|
118
|
+
|
119
|
+
exact_matches = ["password"]
|
120
|
+
prefixes = ["encrypted_"]
|
121
|
+
suffixes = ["_password", "_digest", "_hash", "_token"]
|
122
|
+
|
123
|
+
exact_matches.include?(key) ||
|
124
|
+
prefixes.any? { |prefix| key.start_with?(prefix) } ||
|
125
|
+
suffixes.any? { |suffix| key.end_with?(suffix) }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Field
|
5
|
+
module Options
|
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,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Phlexi
|
4
|
+
module Field
|
5
|
+
module Options
|
6
|
+
module Placeholders
|
7
|
+
def placeholder(placeholder = nil)
|
8
|
+
if placeholder.nil?
|
9
|
+
options[:placeholder]
|
10
|
+
else
|
11
|
+
|
12
|
+
options[:placeholder] = placeholder
|
13
|
+
self
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|