poetry-simple_form 0.0.3
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/CHANGELOG.md +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +61 -0
- data/lib/generators/poetry/simple_form/install/install_generator.rb +30 -0
- data/lib/poetry/simple_form/inputs/base.rb +52 -0
- data/lib/poetry/simple_form/inputs/boolean_input.rb +15 -0
- data/lib/poetry/simple_form/inputs/collection_inputs.rb +158 -0
- data/lib/poetry/simple_form/inputs/custom_inputs.rb +60 -0
- data/lib/poetry/simple_form/inputs/date_time_input.rb +20 -0
- data/lib/poetry/simple_form/inputs/file_input.rb +16 -0
- data/lib/poetry/simple_form/inputs/numeric_input.rb +15 -0
- data/lib/poetry/simple_form/inputs/password_input.rb +15 -0
- data/lib/poetry/simple_form/inputs/string_input.rb +23 -0
- data/lib/poetry/simple_form/inputs/text_input.rb +15 -0
- data/lib/poetry/simple_form/inputs.rb +12 -0
- data/lib/poetry/simple_form/version.rb +8 -0
- data/lib/poetry/simple_form.rb +81 -0
- metadata +100 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 938d26d4ca15b34d244fdc9d37b32310d8bab58ada91a2e53f097649d74ddbb8
|
|
4
|
+
data.tar.gz: 989eb21341bad9296b31e74f4c62410930db2d5350aef9da3bba7c45c9b21749
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ba8eac52747366e0d54c09ca3c4bb293a155ffed98bdcb7bacbff33d0502d433ad8136eed3f39898989696c9ca48800eb45e65b3740b9a576df2ef8f1ae4937e
|
|
7
|
+
data.tar.gz: 3ce40235c63fbf05532db9d055de1e7aa19e844fe29b309ea453c5d9852e73b594106a218cdc3bd551ac6460ee9fce4ee069202bc3334631576ee68fa09f43fb
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Matt Solt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# poetry-simple_form
|
|
2
|
+
|
|
3
|
+
The **migration bridge** from simple_form to poetry. One initializer re-maps
|
|
4
|
+
simple_form's input types onto classes that render whole poetry Fields
|
|
5
|
+
through `Poetry::Ui::FormBuilder` — existing `f.input` calls keep working,
|
|
6
|
+
now with the quartet (label / hint / error / aria) derived from the model
|
|
7
|
+
exactly like the native builder, because the shim renders *through* it.
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
# Gemfile
|
|
11
|
+
gem "poetry-simple_form"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
bin/rails g poetry:simple_form:install # writes the one-line initializer
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## What you get
|
|
19
|
+
|
|
20
|
+
- `f.input` for string/email/url/tel/search/uuid/password/text/json/boolean/
|
|
21
|
+
numeric/range/date/time/file and the collection trio (select /
|
|
22
|
+
radio_buttons / check_boxes), plus grouped_select and time_zone, renders
|
|
23
|
+
poetry Fields: `aria-required` (never native), maxlength/min/max/step
|
|
24
|
+
from validations, errors wired via `aria-describedby`.
|
|
25
|
+
- `f.association` works unchanged (simple_form fetches the records; the
|
|
26
|
+
shim's collection inputs render them as poetry pickers).
|
|
27
|
+
- Every poetry form control is reachable by its poetry name through `as:`,
|
|
28
|
+
even the ones simple_form never had: `:switch`, `:slider`, `:otp`,
|
|
29
|
+
`:sensitive`, `:tag_group`, `:date_picker`, `:calendar`, `:combobox`,
|
|
30
|
+
`:autocomplete`, `:native_select`.
|
|
31
|
+
- `required: true/false` overrides the model's presence inference; `label_method:`
|
|
32
|
+
and `value_method:` (symbols or callables) are honored on every collection
|
|
33
|
+
input, with simple_form's detection chain as the fallback.
|
|
34
|
+
- Poetry-only options ride a `poetry:` hash - `f.input :active, poetry:
|
|
35
|
+
{ switch: true }`, `f.input :code, as: :otp, poetry: { length: 4 }` -
|
|
36
|
+
merged last, so it wins over anything simple_form derived. `input_html`
|
|
37
|
+
still lands on the control (minus class and id, which the component owns).
|
|
38
|
+
- `:datetime` renders poetry's DateTimeField (one datetime-local control,
|
|
39
|
+
local wall time on the wire). `:rich_text_area`, `:hidden`, and `:country`
|
|
40
|
+
fall back to stock simple_form rendering on purpose (no editor, nothing to
|
|
41
|
+
render, and a country list poetry does not carry).
|
|
42
|
+
- Remove the initializer to restore stock rendering instantly.
|
|
43
|
+
|
|
44
|
+
The full type-to-component table is `Poetry::SimpleForm::COVERAGE`; a
|
|
45
|
+
parity test in this gem fails when a new poetry form control has no
|
|
46
|
+
simple_form route, so the two vocabularies cannot drift apart.
|
|
47
|
+
|
|
48
|
+
## What this is not
|
|
49
|
+
|
|
50
|
+
Not the end state. The bridge exists so views can migrate to
|
|
51
|
+
`form_with(model:, builder: Poetry::Ui::FormBuilder)` (and `f.input`
|
|
52
|
+
there) at your own pace — see the poetry docs `/forms` guide. simple_form
|
|
53
|
+
itself contributes only type resolution here; its wrapper tree is bypassed
|
|
54
|
+
by design (a flat wrapper cannot express the Field quartet).
|
|
55
|
+
|
|
56
|
+
Existing `simple_form.*` i18n keys (labels/hints/placeholders) keep
|
|
57
|
+
working — poetry's builder reads them as a fallback chain.
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
Available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module Poetry
|
|
6
|
+
module SimpleForm
|
|
7
|
+
# The gem's Rails generators.
|
|
8
|
+
module Generators
|
|
9
|
+
# `rails g poetry:simple_form:install` - one initializer, the whole
|
|
10
|
+
# shim: activate! re-maps the input types and installs the
|
|
11
|
+
# passthrough wrapper. Delete the file to fall back to stock
|
|
12
|
+
# simple_form rendering.
|
|
13
|
+
class InstallGenerator < Rails::Generators::Base
|
|
14
|
+
source_root File.expand_path("templates", __dir__)
|
|
15
|
+
|
|
16
|
+
# Write config/initializers/poetry_simple_form.rb.
|
|
17
|
+
# @api private
|
|
18
|
+
def create_initializer
|
|
19
|
+
create_file "config/initializers/poetry_simple_form.rb", <<~RUBY_FILE
|
|
20
|
+
# poetry renders simple_form's inputs (the migration bridge):
|
|
21
|
+
# f.input keeps working while views move to
|
|
22
|
+
# form_with(builder: Poetry::Ui::FormBuilder). Remove this file
|
|
23
|
+
# to restore stock simple_form rendering.
|
|
24
|
+
Poetry::SimpleForm.activate!
|
|
25
|
+
RUBY_FILE
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module SimpleForm
|
|
5
|
+
# The input classes the bridge maps simple_form's types onto.
|
|
6
|
+
module Inputs
|
|
7
|
+
# The shim's shared floor: every input renders through a
|
|
8
|
+
# Poetry::Ui::FormBuilder bound to the SAME object/template, so the
|
|
9
|
+
# poetry Field quartet (label/hint/error/aria) is byte-identical to
|
|
10
|
+
# the native-builder path - no duplicated derivation.
|
|
11
|
+
class Base < ::SimpleForm::Inputs::Base
|
|
12
|
+
# Render the attribute as a whole poetry Field through the bound
|
|
13
|
+
# Poetry::Ui::FormBuilder; the subclass's `poetry_as` pins the
|
|
14
|
+
# control type (nil lets poetry infer it).
|
|
15
|
+
#
|
|
16
|
+
# @param _wrapper_options [Hash, nil] unused - the Field owns its chrome
|
|
17
|
+
# @return [String] the rendered Field HTML
|
|
18
|
+
def input(_wrapper_options = nil)
|
|
19
|
+
poetry_builder.input(attribute_name, as: poetry_as, **poetry_options)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# Subclasses answer the poetry `as:` value (nil lets poetry infer).
|
|
25
|
+
def poetry_as = nil
|
|
26
|
+
|
|
27
|
+
def poetry_builder
|
|
28
|
+
Poetry::Ui::FormBuilder.new(object_name, object, template, {})
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# simple_form option vocabulary -> poetry's: label/hint/placeholder
|
|
32
|
+
# pass through (false stays false-y as an omission), required:
|
|
33
|
+
# explicit override wins, input_html lands on the control minus the
|
|
34
|
+
# class string (poetry components own their classes; append via
|
|
35
|
+
# input_html: { class: } consciously with poetry_class:). A `poetry:`
|
|
36
|
+
# hash is the first-class channel for poetry-only options (switch:,
|
|
37
|
+
# length:, orientation:, ...) - merged last, so it wins.
|
|
38
|
+
def poetry_options
|
|
39
|
+
opts = input_html_options.except(:class, :id)
|
|
40
|
+
opts[:hint] = options[:hint] if options[:hint].is_a?(String)
|
|
41
|
+
label = options[:label].is_a?(String) ? options[:label] : translate_from_namespace(:labels)
|
|
42
|
+
opts[:label] = label if label.is_a?(String) # simple_form.labels.* keys, its own lookup chain
|
|
43
|
+
placeholder = options[:placeholder] || input_html_options[:placeholder]
|
|
44
|
+
opts[:placeholder] = placeholder if placeholder.is_a?(String)
|
|
45
|
+
opts[:required] = options[:required] if options.key?(:required) # explicit beats inference
|
|
46
|
+
opts.merge!(options[:poetry].to_h.transform_keys(&:to_sym)) if options[:poetry]
|
|
47
|
+
opts
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module SimpleForm
|
|
5
|
+
module Inputs
|
|
6
|
+
# The horizontal boolean Field (poetry's f.input boolean story);
|
|
7
|
+
# input_html: { switch: true } opts into the setting-row Switch.
|
|
8
|
+
class BooleanInput < Base
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def poetry_as = :boolean
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module SimpleForm
|
|
5
|
+
module Inputs
|
|
6
|
+
# The collection floor: pairs from options[:collection] (both
|
|
7
|
+
# f.input(collection:) and f.association arrive here - association
|
|
8
|
+
# fetches the records first), label/value methods resolved with
|
|
9
|
+
# simple_form's own detection chain.
|
|
10
|
+
class CollectionBase < Base
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def label_value_pairs(collection = Array(options[:collection]))
|
|
14
|
+
return [] if collection.empty?
|
|
15
|
+
|
|
16
|
+
sample = collection.first
|
|
17
|
+
label = options[:label_method]
|
|
18
|
+
value = options[:value_method]
|
|
19
|
+
if (sample.is_a?(Array) || !sample.respond_to?(:id)) && !(label || value)
|
|
20
|
+
collection.map { |item| item.is_a?(Array) ? item : [item.to_s, item] }
|
|
21
|
+
else
|
|
22
|
+
# simple_form's label_method:/value_method: (symbols or callables)
|
|
23
|
+
# win; otherwise its detection chain (name, title, to_s / id, to_s).
|
|
24
|
+
label ||= ::SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) }
|
|
25
|
+
value ||= ::SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) }
|
|
26
|
+
collection.map { |item| [read(item, label), read(item, value)] }
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def read(item, method)
|
|
31
|
+
method.respond_to?(:call) ? method.call(item) : item.public_send(method)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Serves `as: :select` (and f.association's default): a poetry Field
|
|
36
|
+
# wrapping the Select component; the hidden native <select> stays the
|
|
37
|
+
# serialization truth.
|
|
38
|
+
class CollectionSelectInput < CollectionBase
|
|
39
|
+
# Render the collection as a Field-wrapped Select;
|
|
40
|
+
# include_blank/prompt becomes the placeholder option.
|
|
41
|
+
#
|
|
42
|
+
# @return [String] the rendered Field HTML
|
|
43
|
+
def input(_wrapper_options = nil)
|
|
44
|
+
poetry_builder.poetry_select(attribute_name, label_value_pairs,
|
|
45
|
+
include_blank: options[:include_blank] || options[:prompt],
|
|
46
|
+
**poetry_options)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Serves `as: :radio_buttons`: a poetry Field wrapping the
|
|
51
|
+
# RadioGroup component, one row per collection item.
|
|
52
|
+
class CollectionRadioButtonsInput < CollectionBase
|
|
53
|
+
# Render the collection as a Field-wrapped RadioGroup.
|
|
54
|
+
#
|
|
55
|
+
# @return [String] the rendered Field HTML
|
|
56
|
+
def input(_wrapper_options = nil)
|
|
57
|
+
pairs = label_value_pairs.map { |label, value| [value, label] }
|
|
58
|
+
poetry_builder.radio_group(attribute_name, pairs, **poetry_options)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Serves `as: :check_boxes`: a poetry Field wrapping the checkbox
|
|
63
|
+
# group - one Checkbox row per collection item, posting an array.
|
|
64
|
+
class CollectionCheckBoxesInput < CollectionBase
|
|
65
|
+
# Render the collection as a Field-wrapped checkbox group.
|
|
66
|
+
#
|
|
67
|
+
# @return [String] the rendered Field HTML
|
|
68
|
+
def input(_wrapper_options = nil)
|
|
69
|
+
pairs = label_value_pairs.map { |label, value| [value, label] }
|
|
70
|
+
poetry_builder.checkbox_group(attribute_name, pairs, **poetry_options)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Serves `as: :native_select`: a poetry Field wrapping the styled
|
|
75
|
+
# native <select> (the no-JS picker).
|
|
76
|
+
class NativeSelectInput < CollectionBase
|
|
77
|
+
# @return [String] the rendered Field HTML
|
|
78
|
+
def input(_wrapper_options = nil)
|
|
79
|
+
poetry_builder.native_select(attribute_name, label_value_pairs,
|
|
80
|
+
include_blank: options[:include_blank] || options[:prompt],
|
|
81
|
+
**poetry_options)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Serves `as: :combobox`: a poetry Field wrapping the filterable Combobox.
|
|
86
|
+
class ComboboxInput < CollectionBase
|
|
87
|
+
# @return [String] the rendered Field HTML
|
|
88
|
+
def input(_wrapper_options = nil)
|
|
89
|
+
poetry_builder.poetry_combobox(attribute_name, label_value_pairs,
|
|
90
|
+
include_blank: options[:include_blank] || options[:prompt],
|
|
91
|
+
**poetry_options)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Serves `as: :autocomplete`: a poetry Field wrapping the Autocomplete;
|
|
96
|
+
# the collection becomes its suggestions.
|
|
97
|
+
class AutocompleteInput < CollectionBase
|
|
98
|
+
# @return [String] the rendered Field HTML
|
|
99
|
+
def input(_wrapper_options = nil)
|
|
100
|
+
poetry_builder.autocomplete(attribute_name, label_value_pairs, **poetry_options)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Serves `as: :grouped_select`: simple_form's group_method /
|
|
105
|
+
# group_label_method resolve the groups, poetry's Select renders them
|
|
106
|
+
# as labelled groups.
|
|
107
|
+
class GroupedCollectionSelectInput < CollectionBase
|
|
108
|
+
# @return [String] the rendered Field HTML
|
|
109
|
+
def input(_wrapper_options = nil)
|
|
110
|
+
poetry_builder.poetry_select(attribute_name, grouped_pairs,
|
|
111
|
+
include_blank: options[:include_blank] || options[:prompt],
|
|
112
|
+
**poetry_options)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
# { "Group label" => [[label, value], ...] } in collection order.
|
|
118
|
+
def grouped_pairs
|
|
119
|
+
groups = options[:collection]
|
|
120
|
+
groups = groups.call if groups.respond_to?(:call)
|
|
121
|
+
group_method = options.fetch(:group_method)
|
|
122
|
+
Array(groups).to_h do |group|
|
|
123
|
+
[group_label_for(group).to_s, label_value_pairs(Array(group.public_send(group_method)))]
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def group_label_for(group)
|
|
128
|
+
method = options[:group_label_method] ||
|
|
129
|
+
::SimpleForm.collection_label_methods.find { |m| group.respond_to?(m) }
|
|
130
|
+
method ? group.public_send(method) : group
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Serves `as: :time_zone`: every ActiveSupport::TimeZone in a poetry
|
|
135
|
+
# Select, the priority zones (priority: or SimpleForm.time_zone_priority)
|
|
136
|
+
# listed first.
|
|
137
|
+
class TimeZoneInput < CollectionBase
|
|
138
|
+
# @return [String] the rendered Field HTML
|
|
139
|
+
def input(_wrapper_options = nil)
|
|
140
|
+
poetry_builder.poetry_select(attribute_name, zone_pairs,
|
|
141
|
+
include_blank: options[:include_blank] || options[:prompt],
|
|
142
|
+
**poetry_options)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
private
|
|
146
|
+
|
|
147
|
+
def zone_pairs
|
|
148
|
+
zones = ActiveSupport::TimeZone.all
|
|
149
|
+
priority = Array(options[:priority] || ::SimpleForm.time_zone_priority)
|
|
150
|
+
first = priority.filter_map do |zone|
|
|
151
|
+
zone.is_a?(ActiveSupport::TimeZone) ? zone : ActiveSupport::TimeZone[zone]
|
|
152
|
+
end
|
|
153
|
+
(first + (zones - first)).map { |zone| [zone.to_s, zone.name] }
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module SimpleForm
|
|
5
|
+
module Inputs
|
|
6
|
+
# The poetry controls with no simple_form ancestor, each reachable by
|
|
7
|
+
# its poetry name: `f.input :active, as: :switch`. They are thin on
|
|
8
|
+
# purpose - the Field quartet and every option ride the shared Base.
|
|
9
|
+
|
|
10
|
+
# Serves `as: :switch`: a Field in the setting orientation wrapping the Switch.
|
|
11
|
+
class SwitchInput < Base
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def poetry_as = :switch
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Serves `as: :slider` and simple_form's `:range`: a Field wrapping the Slider.
|
|
18
|
+
class SliderInput < Base
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def poetry_as = :slider
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Serves `as: :otp`: a Field wrapping the InputOtp (length: via poetry:).
|
|
25
|
+
class OtpInput < Base
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def poetry_as = :otp
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Serves `as: :sensitive`: a Field wrapping the masked, reveal-on-demand SensitiveInput.
|
|
32
|
+
class SensitiveInput < Base
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def poetry_as = :sensitive
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Serves `as: :tag_group`: a Field wrapping the TagGroup.
|
|
39
|
+
class TagGroupInput < Base
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def poetry_as = :tag_group
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Serves `as: :date_picker`: a Field wrapping the DatePicker (popover calendar).
|
|
46
|
+
class DatePickerInput < Base
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def poetry_as = :date_picker
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Serves `as: :calendar`: a Field wrapping the inline Calendar.
|
|
53
|
+
class CalendarInput < Base
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def poetry_as = :calendar
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module SimpleForm
|
|
5
|
+
module Inputs
|
|
6
|
+
# date -> DateField, time -> TimeField; :datetime falls back to
|
|
7
|
+
# poetry's own DateTimeField (one datetime-local control, no select trio).
|
|
8
|
+
class DateTimeInput < ::SimpleForm::Inputs::DateTimeInput
|
|
9
|
+
# Render :date/:time/:datetime attributes as poetry DateField /
|
|
10
|
+
# TimeField / DateTimeField - one control each, ISO on the wire.
|
|
11
|
+
#
|
|
12
|
+
# @return [String] the rendered HTML
|
|
13
|
+
def input(_wrapper_options = nil)
|
|
14
|
+
Poetry::Ui::FormBuilder.new(object_name, object, template, {})
|
|
15
|
+
.input(attribute_name, as: input_type)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module SimpleForm
|
|
5
|
+
module Inputs
|
|
6
|
+
# Serves `as: :file`: a poetry Field wrapping the FileInput
|
|
7
|
+
# component (the native file control in poetry chrome; input_html
|
|
8
|
+
# options such as variant:/multiple: pass through).
|
|
9
|
+
class FileInput < Base
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def poetry_as = :file
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module SimpleForm
|
|
5
|
+
module Inputs
|
|
6
|
+
# NumberField; poetry re-derives min/max/step from numericality, so
|
|
7
|
+
# the shim passes nothing simple_form computed.
|
|
8
|
+
class NumericInput < Base
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def poetry_as = :number
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module SimpleForm
|
|
5
|
+
module Inputs
|
|
6
|
+
# Serves `as: :password`: a poetry Field wrapping a type=password
|
|
7
|
+
# Input; the value never round-trips into the markup.
|
|
8
|
+
class PasswordInput < Base
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def poetry_as = :password
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module SimpleForm
|
|
5
|
+
module Inputs
|
|
6
|
+
# string/email/url/tel/search/citext: the resolved simple_form
|
|
7
|
+
# input_type IS poetry's as: vocabulary (citext folds to string).
|
|
8
|
+
class StringInput < Base
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def poetry_as
|
|
12
|
+
# :string is simple_form's no-column fallback (ActiveModel,
|
|
13
|
+
# virtual attributes) - hand poetry a nil so ITS inference runs
|
|
14
|
+
# (type_for_attribute + name heuristics); a concretely resolved
|
|
15
|
+
# type passes through.
|
|
16
|
+
return nil if %i[string citext uuid].include?(input_type)
|
|
17
|
+
|
|
18
|
+
input_type
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "poetry/simple_form/inputs/base"
|
|
4
|
+
require "poetry/simple_form/inputs/string_input"
|
|
5
|
+
require "poetry/simple_form/inputs/password_input"
|
|
6
|
+
require "poetry/simple_form/inputs/text_input"
|
|
7
|
+
require "poetry/simple_form/inputs/boolean_input"
|
|
8
|
+
require "poetry/simple_form/inputs/numeric_input"
|
|
9
|
+
require "poetry/simple_form/inputs/date_time_input"
|
|
10
|
+
require "poetry/simple_form/inputs/file_input"
|
|
11
|
+
require "poetry/simple_form/inputs/collection_inputs"
|
|
12
|
+
require "poetry/simple_form/inputs/custom_inputs"
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "simple_form"
|
|
4
|
+
require "poetry/simple_form/version"
|
|
5
|
+
require "poetry/simple_form/inputs"
|
|
6
|
+
|
|
7
|
+
# The Poetry namespace, shared by every gem in the family.
|
|
8
|
+
module Poetry
|
|
9
|
+
# The simple_form migration bridge: one initializer re-maps simple_form's
|
|
10
|
+
# input types onto classes that render whole poetry Fields through
|
|
11
|
+
# Poetry::Ui::FormBuilder. Every f.input keeps working; every poetry form
|
|
12
|
+
# control is reachable by name through as:.
|
|
13
|
+
module SimpleForm
|
|
14
|
+
# simple_form input type => bridge input class. activate! installs
|
|
15
|
+
# exactly this table, so the coverage below cannot drift from it.
|
|
16
|
+
INPUTS = {
|
|
17
|
+
string: Inputs::StringInput, email: Inputs::StringInput, url: Inputs::StringInput,
|
|
18
|
+
tel: Inputs::StringInput, search: Inputs::StringInput, citext: Inputs::StringInput,
|
|
19
|
+
uuid: Inputs::StringInput,
|
|
20
|
+
password: Inputs::PasswordInput, sensitive: Inputs::SensitiveInput,
|
|
21
|
+
text: Inputs::TextInput, hstore: Inputs::TextInput, json: Inputs::TextInput, jsonb: Inputs::TextInput,
|
|
22
|
+
boolean: Inputs::BooleanInput, switch: Inputs::SwitchInput,
|
|
23
|
+
integer: Inputs::NumericInput, decimal: Inputs::NumericInput, float: Inputs::NumericInput,
|
|
24
|
+
range: Inputs::SliderInput, slider: Inputs::SliderInput,
|
|
25
|
+
date: Inputs::DateTimeInput, time: Inputs::DateTimeInput, datetime: Inputs::DateTimeInput,
|
|
26
|
+
date_picker: Inputs::DatePickerInput, calendar: Inputs::CalendarInput,
|
|
27
|
+
file: Inputs::FileInput, otp: Inputs::OtpInput, tag_group: Inputs::TagGroupInput,
|
|
28
|
+
select: Inputs::CollectionSelectInput, grouped_select: Inputs::GroupedCollectionSelectInput,
|
|
29
|
+
time_zone: Inputs::TimeZoneInput, native_select: Inputs::NativeSelectInput,
|
|
30
|
+
combobox: Inputs::ComboboxInput, autocomplete: Inputs::AutocompleteInput,
|
|
31
|
+
radio_buttons: Inputs::CollectionRadioButtonsInput, check_boxes: Inputs::CollectionCheckBoxesInput
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
# simple_form input type => the poetry component it renders (a registry
|
|
35
|
+
# key).
|
|
36
|
+
# The parity gate (test/poetry/simple_form/coverage_test.rb) checks this
|
|
37
|
+
# against every form-facing component in the registry.
|
|
38
|
+
COVERAGE = {
|
|
39
|
+
string: "input", email: "input", url: "input", tel: "input", citext: "input", uuid: "input",
|
|
40
|
+
password: "input", search: "search_field", sensitive: "sensitive_input",
|
|
41
|
+
text: "textarea", hstore: "textarea", json: "textarea", jsonb: "textarea",
|
|
42
|
+
boolean: "checkbox", switch: "switch",
|
|
43
|
+
integer: "number_field", decimal: "number_field", float: "number_field",
|
|
44
|
+
range: "slider", slider: "slider",
|
|
45
|
+
date: "date_field", time: "time_field", datetime: "date_time_field",
|
|
46
|
+
date_picker: "date_picker", calendar: "calendar",
|
|
47
|
+
file: "file_input", otp: "input_otp", tag_group: "tag_group",
|
|
48
|
+
select: "select", grouped_select: "select", time_zone: "select", native_select: "native_select",
|
|
49
|
+
combobox: "combobox", autocomplete: "autocomplete",
|
|
50
|
+
radio_buttons: "radio_group", check_boxes: "checkbox"
|
|
51
|
+
}.freeze
|
|
52
|
+
|
|
53
|
+
# simple_form types left to simple_form entirely, with the reason.
|
|
54
|
+
STOCK = {
|
|
55
|
+
rich_text_area: "poetry ships no rich-text editor",
|
|
56
|
+
hidden: "nothing to render",
|
|
57
|
+
country: "the country list comes from the country_select gem, which poetry does not depend on"
|
|
58
|
+
}.freeze
|
|
59
|
+
|
|
60
|
+
# Form-facing poetry components deliberately not reachable through a
|
|
61
|
+
# simple_form type, with the reason (empty today; the gate reads it).
|
|
62
|
+
BRIDGE_EXEMPT = {}.freeze
|
|
63
|
+
|
|
64
|
+
# Install the bridge: map every type in INPUTS and make the flat poetry
|
|
65
|
+
# wrapper the default (the Field owns its chrome, so the wrapper
|
|
66
|
+
# contributes only display: contents).
|
|
67
|
+
#
|
|
68
|
+
# @return [void]
|
|
69
|
+
def self.activate!
|
|
70
|
+
builder = ::SimpleForm::FormBuilder
|
|
71
|
+
INPUTS.each { |type, klass| builder.map_type type, to: klass }
|
|
72
|
+
|
|
73
|
+
::SimpleForm.setup do |config|
|
|
74
|
+
config.wrappers :poetry, tag: :div, class: "contents" do |b|
|
|
75
|
+
b.use :input
|
|
76
|
+
end
|
|
77
|
+
config.default_wrapper = :poetry
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: poetry-simple_form
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.3
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Matt Solt
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: poetry-ui
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - '='
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.0.3
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - '='
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.0.3
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: simple_form
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '5.3'
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '6'
|
|
36
|
+
type: :runtime
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '5.3'
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '6'
|
|
46
|
+
description: An opt-in shim that re-maps simple_form's input types onto Poetry's model-bound
|
|
47
|
+
Field components, so existing simple_form views keep working mid-migration. The
|
|
48
|
+
end state is Poetry::Ui::FormBuilder - this bridge exists to make getting there
|
|
49
|
+
boring.
|
|
50
|
+
email:
|
|
51
|
+
- mattsolt@gmail.com
|
|
52
|
+
executables: []
|
|
53
|
+
extensions: []
|
|
54
|
+
extra_rdoc_files: []
|
|
55
|
+
files:
|
|
56
|
+
- CHANGELOG.md
|
|
57
|
+
- LICENSE.txt
|
|
58
|
+
- README.md
|
|
59
|
+
- lib/generators/poetry/simple_form/install/install_generator.rb
|
|
60
|
+
- lib/poetry/simple_form.rb
|
|
61
|
+
- lib/poetry/simple_form/inputs.rb
|
|
62
|
+
- lib/poetry/simple_form/inputs/base.rb
|
|
63
|
+
- lib/poetry/simple_form/inputs/boolean_input.rb
|
|
64
|
+
- lib/poetry/simple_form/inputs/collection_inputs.rb
|
|
65
|
+
- lib/poetry/simple_form/inputs/custom_inputs.rb
|
|
66
|
+
- lib/poetry/simple_form/inputs/date_time_input.rb
|
|
67
|
+
- lib/poetry/simple_form/inputs/file_input.rb
|
|
68
|
+
- lib/poetry/simple_form/inputs/numeric_input.rb
|
|
69
|
+
- lib/poetry/simple_form/inputs/password_input.rb
|
|
70
|
+
- lib/poetry/simple_form/inputs/string_input.rb
|
|
71
|
+
- lib/poetry/simple_form/inputs/text_input.rb
|
|
72
|
+
- lib/poetry/simple_form/version.rb
|
|
73
|
+
homepage: https://poetryui.com
|
|
74
|
+
licenses:
|
|
75
|
+
- MIT
|
|
76
|
+
metadata:
|
|
77
|
+
homepage_uri: https://poetryui.com
|
|
78
|
+
documentation_uri: https://poetryui.com/docs
|
|
79
|
+
source_code_uri: https://github.com/roboruby/poetry-simple_form
|
|
80
|
+
changelog_uri: https://github.com/roboruby/poetry-simple_form/blob/main/CHANGELOG.md
|
|
81
|
+
bug_tracker_uri: https://github.com/roboruby/poetry-simple_form/issues
|
|
82
|
+
rubygems_mfa_required: 'true'
|
|
83
|
+
rdoc_options: []
|
|
84
|
+
require_paths:
|
|
85
|
+
- lib
|
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: 3.4.0
|
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0'
|
|
96
|
+
requirements: []
|
|
97
|
+
rubygems_version: 4.0.16
|
|
98
|
+
specification_version: 4
|
|
99
|
+
summary: 'The simple_form migration bridge for Poetry: f.input renders Poetry Fields.'
|
|
100
|
+
test_files: []
|