formalist 0.6.0 → 0.7.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 +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile +1 -1
- data/lib/formalist/child_forms/builder.rb +46 -0
- data/lib/formalist/child_forms/child_form.rb +48 -0
- data/lib/formalist/child_forms/params_processor.rb +30 -0
- data/lib/formalist/child_forms/validity_check.rb +30 -0
- data/lib/formalist/elements/form_field.rb +25 -0
- data/lib/formalist/elements/many.rb +6 -12
- data/lib/formalist/elements/many_forms.rb +115 -0
- data/lib/formalist/elements.rb +4 -0
- data/lib/formalist/form/validity_check.rb +11 -2
- data/lib/formalist/rich_text/embedded_form_compiler.rb +1 -1
- data/lib/formalist/rich_text/embedded_forms_container/registration.rb +4 -1
- data/lib/formalist/rich_text/rendering/embedded_form_renderer.rb +22 -3
- data/lib/formalist/version.rb +1 -1
- metadata +13 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0d3e4752a943653fc73963ad57981a923b541b23e4d621fbe938bdd07bb42ff
|
4
|
+
data.tar.gz: 20ec653404675c1ac5dfae830151a383450c908d4e46a8f4edf32dc0bf989ae2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da69af398ecc1393915e7f92d86402f16ed0af4be7b60fe99cabb4229f1f44947fb6bca4ce869911e7643fb6188df4d0c3f3f4a8903311195adaafd4f0f35009
|
7
|
+
data.tar.gz: 4e54bf6266a30d01cbd6cc037871325dc9c503c55252c4e53ab062f93b113976ebc14a02e9b16e8b2b385608f1aeac7d6472d042fcf94e13b73adc0ab2bffeb0
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
# 0.7.0 / 2022-04-07
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
- Add support a field with an arbitrary list of forms. Supporting updates in formalist-standard-react@^4.2.0
|
6
|
+
- Add namespace and paths options to embedded form renderer
|
7
|
+
- Support dry-schema/dry-validation 1.0
|
8
|
+
|
1
9
|
# 0.6.0 / 2020-05-06
|
2
10
|
|
3
11
|
### Changed
|
data/Gemfile
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
require "json"
|
2
|
+
require_relative "child_form"
|
3
|
+
|
4
|
+
module Formalist
|
5
|
+
module ChildForms
|
6
|
+
class Builder
|
7
|
+
attr_reader :embedded_forms
|
8
|
+
MissingFormDefinitionError = Class.new(StandardError)
|
9
|
+
|
10
|
+
def initialize(embedded_form_collection)
|
11
|
+
@embedded_forms = embedded_form_collection
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(input)
|
15
|
+
return input if input.nil?
|
16
|
+
input.map { |node| visit(node) }
|
17
|
+
end
|
18
|
+
alias_method :[], :call
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def visit(node)
|
23
|
+
name, data = node.values_at(:name, :data)
|
24
|
+
|
25
|
+
embedded_form = embedded_forms[name]
|
26
|
+
if embedded_form.nil?
|
27
|
+
raise MissingFormDefinitionError, "Form +#{embedded_forms[name]}+ is missing from the embeddable forms collection"
|
28
|
+
end
|
29
|
+
child_form(name, embedded_form).fill(input: data)
|
30
|
+
end
|
31
|
+
|
32
|
+
def child_form(name, embedded_form)
|
33
|
+
ChildForm.build(
|
34
|
+
name: name,
|
35
|
+
attributes: {
|
36
|
+
label: embedded_form.label,
|
37
|
+
form: embedded_form.form,
|
38
|
+
schema: embedded_form.schema,
|
39
|
+
input_processor: embedded_form.input_processor,
|
40
|
+
preview_image_url: embedded_form.preview_image_url
|
41
|
+
}
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "formalist/element"
|
2
|
+
|
3
|
+
module Formalist
|
4
|
+
module ChildForms
|
5
|
+
class ChildForm < Element
|
6
|
+
DEFAULT_INPUT_PROCESSOR = -> input { input }.freeze
|
7
|
+
|
8
|
+
attribute :label
|
9
|
+
attribute :form
|
10
|
+
attribute :schema
|
11
|
+
attribute :input_processor, default: DEFAULT_INPUT_PROCESSOR
|
12
|
+
attribute :preview_image_url
|
13
|
+
|
14
|
+
def fill(input: {}, errors: {})
|
15
|
+
super(input: form_input_ast(input), errors: errors.to_a)
|
16
|
+
end
|
17
|
+
|
18
|
+
def attributes
|
19
|
+
super.merge(form: form_attribute_ast)
|
20
|
+
end
|
21
|
+
|
22
|
+
def form_attribute_ast
|
23
|
+
@attributes[:form].to_ast
|
24
|
+
end
|
25
|
+
|
26
|
+
def form_input_ast(data)
|
27
|
+
# Run the raw data through the validation schema
|
28
|
+
validation = @attributes[:schema].(data)
|
29
|
+
|
30
|
+
# And then through the embedded form's input processor (which may add
|
31
|
+
# extra system-generated information necessary for the form to render
|
32
|
+
# fully)
|
33
|
+
input = @attributes[:input_processor].(validation.to_h)
|
34
|
+
|
35
|
+
@attributes[:form].fill(input: input, errors: validation.errors.to_h).to_ast
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_ast
|
39
|
+
[:child_form, [
|
40
|
+
name,
|
41
|
+
type,
|
42
|
+
input,
|
43
|
+
Element::Attributes.new(attributes).to_ast,
|
44
|
+
]]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "json"
|
2
|
+
require_relative "builder"
|
3
|
+
|
4
|
+
module Formalist
|
5
|
+
module ChildForms
|
6
|
+
class ParamsProcessor
|
7
|
+
attr_reader :embedded_forms
|
8
|
+
|
9
|
+
def initialize(embedded_form_collection)
|
10
|
+
@embedded_forms = embedded_form_collection
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(input)
|
14
|
+
return input if input.nil?
|
15
|
+
input.inject([]) { |output, node| output.push(process(node)) }
|
16
|
+
end
|
17
|
+
alias_method :[], :call
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def process(node)
|
22
|
+
name, data = node.values_at(:name, :data)
|
23
|
+
|
24
|
+
validation = embedded_forms[name].schema.(data)
|
25
|
+
node.merge(data: validation.to_h)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "json"
|
2
|
+
require_relative "builder"
|
3
|
+
|
4
|
+
module Formalist
|
5
|
+
module ChildForms
|
6
|
+
class ValidityCheck
|
7
|
+
attr_reader :embedded_forms
|
8
|
+
|
9
|
+
def initialize(embedded_form_collection)
|
10
|
+
@embedded_forms = embedded_form_collection
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(input)
|
14
|
+
return input if input.nil?
|
15
|
+
input.map { |node| valid?(node) }.all?
|
16
|
+
end
|
17
|
+
alias_method :[], :call
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def valid?(node)
|
22
|
+
name, data = node.values_at(:name, :data)
|
23
|
+
|
24
|
+
validation = embedded_forms[name].schema
|
25
|
+
validation.(data).success?
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "formalist/child_forms/child_form"
|
2
|
+
|
3
|
+
module Formalist
|
4
|
+
class Elements
|
5
|
+
class FormField < ChildForms::ChildForm
|
6
|
+
attribute :hint
|
7
|
+
|
8
|
+
def fill(input: {}, errors: {})
|
9
|
+
input = input[name]
|
10
|
+
errors = errors[name].to_a
|
11
|
+
|
12
|
+
super(input: input, errors: errors)
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_ast
|
16
|
+
[:form_field, [
|
17
|
+
name,
|
18
|
+
type,
|
19
|
+
input,
|
20
|
+
Element::Attributes.new(attributes).to_ast,
|
21
|
+
]]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -5,10 +5,15 @@ module Formalist
|
|
5
5
|
class Many < Element
|
6
6
|
attribute :action_label
|
7
7
|
attribute :sortable
|
8
|
+
attribute :moveable
|
8
9
|
attribute :label
|
9
10
|
attribute :max_height
|
10
11
|
attribute :placeholder
|
11
12
|
attribute :validation
|
13
|
+
attribute :allow_create, default: true
|
14
|
+
attribute :allow_update, default: true
|
15
|
+
attribute :allow_destroy, default: true
|
16
|
+
attribute :allow_reorder, default: true
|
12
17
|
|
13
18
|
# @api private
|
14
19
|
attr_reader :child_template
|
@@ -28,7 +33,7 @@ module Formalist
|
|
28
33
|
# @api private
|
29
34
|
def fill(input: {}, errors: {})
|
30
35
|
input = input.fetch(name) { [] }
|
31
|
-
errors = errors
|
36
|
+
errors = errors[name] || {}
|
32
37
|
|
33
38
|
# Errors look like this when they are on the array itself: ["size cannot be greater than 2"]
|
34
39
|
# Errors look like this when they are on children: {0=>{:summary=>["must be filled"]}
|
@@ -47,17 +52,6 @@ module Formalist
|
|
47
52
|
)
|
48
53
|
end
|
49
54
|
|
50
|
-
# Until we can put defaults on `Types::Bool`, supply them here
|
51
|
-
# @api private
|
52
|
-
def attributes
|
53
|
-
{
|
54
|
-
allow_create: true,
|
55
|
-
allow_update: true,
|
56
|
-
allow_destroy: true,
|
57
|
-
allow_reorder: true,
|
58
|
-
}.merge(super)
|
59
|
-
end
|
60
|
-
|
61
55
|
# Converts a collection of "many" repeating elements into an abstract
|
62
56
|
# syntax tree.
|
63
57
|
#
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require "formalist/element"
|
2
|
+
require "formalist/child_forms/builder"
|
3
|
+
|
4
|
+
module Formalist
|
5
|
+
class Elements
|
6
|
+
class ManyForms < Element
|
7
|
+
attribute :action_label
|
8
|
+
attribute :sortable
|
9
|
+
attribute :moveable
|
10
|
+
attribute :label
|
11
|
+
attribute :max_height
|
12
|
+
attribute :placeholder
|
13
|
+
attribute :embeddable_forms
|
14
|
+
attribute :validation
|
15
|
+
attribute :allow_create, default: true
|
16
|
+
attribute :allow_update, default: true
|
17
|
+
attribute :allow_destroy, default: true
|
18
|
+
attribute :allow_reorder, default: true
|
19
|
+
|
20
|
+
# FIXME: it would be tidier to have a reader method for each attribute
|
21
|
+
def attributes
|
22
|
+
super.merge(embeddable_forms: embeddable_forms_ast)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @api private
|
26
|
+
def fill(input: {}, errors: {})
|
27
|
+
input = input[name] || []
|
28
|
+
errors = errors[name].to_a
|
29
|
+
|
30
|
+
children = child_form_builder.(input)
|
31
|
+
|
32
|
+
super(
|
33
|
+
input: input,
|
34
|
+
errors: errors,
|
35
|
+
children: children,
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Replace the form objects with their AST
|
40
|
+
def embeddable_forms_ast
|
41
|
+
@attributes[:embeddable_forms].to_h.map { |key, attrs|
|
42
|
+
template_attrs = attrs.slice(:label, :preview_image_url)
|
43
|
+
|
44
|
+
[
|
45
|
+
key,
|
46
|
+
attrs.merge(
|
47
|
+
form: attrs[:form].to_ast,
|
48
|
+
attributes_template: Element::Attributes.new(template_attrs).to_ast
|
49
|
+
)
|
50
|
+
]
|
51
|
+
}.to_h
|
52
|
+
end
|
53
|
+
|
54
|
+
def child_form_builder
|
55
|
+
ChildForms::Builder.new(@attributes[:embeddable_forms])
|
56
|
+
end
|
57
|
+
|
58
|
+
# Converts a collection of "many" repeating elements into an abstract
|
59
|
+
# syntax tree.
|
60
|
+
#
|
61
|
+
# It takes the following format:
|
62
|
+
#
|
63
|
+
# ```
|
64
|
+
# [:many_forms, [params]]
|
65
|
+
# ```
|
66
|
+
#
|
67
|
+
# With the following parameters:
|
68
|
+
#
|
69
|
+
# 1. Collection name
|
70
|
+
# 2. Custom form element type (or `:many_forms` otherwise)
|
71
|
+
# 3. Collection-level error messages
|
72
|
+
# 4. Form element attributes
|
73
|
+
# 6. Child elements, one for each of the entries in the input data (or
|
74
|
+
# none, if there is no or empty input data)
|
75
|
+
#
|
76
|
+
# @see Formalist::Element::Attributes#to_ast "Form element attributes" structure
|
77
|
+
#
|
78
|
+
# @example "components" collection
|
79
|
+
# many_forms.to_ast
|
80
|
+
# # => [:many_forms, [
|
81
|
+
# :components,
|
82
|
+
# :many_forms,
|
83
|
+
# ["components size cannot be less than 3"],
|
84
|
+
# [:object, [
|
85
|
+
# [:allow_create, [:value, [true]]],
|
86
|
+
# [:allow_update, [:value, [true]]],
|
87
|
+
# [:allow_destroy, [:value, [true]]],
|
88
|
+
# [:allow_reorder, [:value, [true]]]
|
89
|
+
# ]],
|
90
|
+
# [
|
91
|
+
# [
|
92
|
+
# [:child_form,
|
93
|
+
# [:image_with_captions,
|
94
|
+
# :child_form,
|
95
|
+
# [[:field, [:image_id, :text_field, "", ["must be filled"], [:object, []]]], [:field, [:caption, :text_field, "Large panda", [], [:object, []]]]],
|
96
|
+
# [:object, []]
|
97
|
+
# ]
|
98
|
+
# ]]
|
99
|
+
#
|
100
|
+
# @return [Array] the collection as an abstract syntax tree.
|
101
|
+
def to_ast
|
102
|
+
local_errors = errors.is_a?(Array) ? errors : []
|
103
|
+
|
104
|
+
[:many_forms, [
|
105
|
+
name,
|
106
|
+
type,
|
107
|
+
local_errors,
|
108
|
+
Element::Attributes.new(attributes).to_ast,
|
109
|
+
children.map(&:to_ast)
|
110
|
+
]]
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/formalist/elements.rb
CHANGED
@@ -4,6 +4,8 @@ require "formalist/elements/compound_field"
|
|
4
4
|
require "formalist/elements/field"
|
5
5
|
require "formalist/elements/group"
|
6
6
|
require "formalist/elements/many"
|
7
|
+
require "formalist/elements/many_forms"
|
8
|
+
require "formalist/elements/form_field"
|
7
9
|
require "formalist/elements/section"
|
8
10
|
|
9
11
|
module Formalist
|
@@ -15,6 +17,8 @@ module Formalist
|
|
15
17
|
register :field, Field
|
16
18
|
register :group, Group
|
17
19
|
register :many, Many
|
20
|
+
register :many_forms, ManyForms
|
21
|
+
register :form_field, FormField
|
18
22
|
register :section, Section
|
19
23
|
end
|
20
24
|
end
|
@@ -41,11 +41,20 @@ module Formalist
|
|
41
41
|
def visit_many(node)
|
42
42
|
_name, _type, errors, _attributes, _child_template, children = node
|
43
43
|
|
44
|
-
# The `children
|
45
|
-
#
|
44
|
+
# The `children parameter for `many` elements is nested since there are
|
45
|
+
# many groups of elements, we need to flatten to traverse them all
|
46
46
|
errors.empty? && children.flatten(1).map { |child| visit(child) }.all?
|
47
47
|
end
|
48
48
|
|
49
|
+
# TODO work out what to do with this.
|
50
|
+
# I think it's only relevant to many_forms
|
51
|
+
# nested in rich text ast
|
52
|
+
def visit_many_forms(node)
|
53
|
+
_name, _type, errors, _attributes, children = node
|
54
|
+
|
55
|
+
errors.empty? && children.map { |child| visit(child[:form]) }.all?
|
56
|
+
end
|
57
|
+
|
49
58
|
def visit_section(node)
|
50
59
|
_name, _type, _attributes, children = node
|
51
60
|
|
@@ -79,7 +79,7 @@ module Formalist
|
|
79
79
|
# fully)
|
80
80
|
input = embedded_form.input_processor.(validation.to_h)
|
81
81
|
|
82
|
-
embedded_form.form.fill(input: input, errors: validation.
|
82
|
+
embedded_form.form.fill(input: input, errors: validation.errors.to_h).to_ast
|
83
83
|
end
|
84
84
|
end
|
85
85
|
end
|
@@ -8,12 +8,14 @@ module Formalist
|
|
8
8
|
attr_reader :form
|
9
9
|
attr_reader :schema
|
10
10
|
attr_reader :input_processor
|
11
|
+
attr_reader :preview_image_url
|
11
12
|
|
12
|
-
def initialize(label:, form:, schema:, input_processor: DEFAULT_INPUT_PROCESSOR)
|
13
|
+
def initialize(label:, form:, schema:, preview_image_url: nil, input_processor: DEFAULT_INPUT_PROCESSOR)
|
13
14
|
@label = label
|
14
15
|
@form = form
|
15
16
|
@schema = schema
|
16
17
|
@input_processor = input_processor
|
18
|
+
@preview_image_url = preview_image_url
|
17
19
|
end
|
18
20
|
|
19
21
|
def to_h
|
@@ -22,6 +24,7 @@ module Formalist
|
|
22
24
|
form: form,
|
23
25
|
schema: schema,
|
24
26
|
input_processor: input_processor,
|
27
|
+
preview_image_url: preview_image_url
|
25
28
|
}
|
26
29
|
end
|
27
30
|
end
|
@@ -3,22 +3,41 @@ module Formalist
|
|
3
3
|
module Rendering
|
4
4
|
class EmbeddedFormRenderer
|
5
5
|
attr_reader :container
|
6
|
+
attr_reader :namespace
|
7
|
+
attr_reader :paths
|
6
8
|
attr_reader :options
|
7
9
|
|
8
|
-
def initialize(container = {}, **options)
|
10
|
+
def initialize(container = {}, namespace: nil, paths: [], **options)
|
9
11
|
@container = container
|
12
|
+
@namespace = namespace
|
13
|
+
@paths = paths
|
10
14
|
@options = options
|
11
15
|
end
|
12
16
|
|
13
17
|
def call(form_data)
|
14
18
|
type, data = form_data.values_at(:name, :data)
|
15
19
|
|
16
|
-
|
17
|
-
|
20
|
+
key = resolve_key(type)
|
21
|
+
|
22
|
+
if key
|
23
|
+
container[key].(data, options)
|
18
24
|
else
|
19
25
|
""
|
20
26
|
end
|
21
27
|
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def resolve_key(type)
|
32
|
+
paths.each do |path|
|
33
|
+
path_key = path.tr("/", ".")
|
34
|
+
key = [namespace, path_key, type].compact.join(".")
|
35
|
+
return key if container.key?(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
key = [namespace, type].compact.join(".")
|
39
|
+
return key if container.key?(key)
|
40
|
+
end
|
22
41
|
end
|
23
42
|
end
|
24
43
|
end
|
data/lib/formalist/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: formalist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Riley
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-configurable
|
@@ -136,7 +136,7 @@ dependencies:
|
|
136
136
|
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
|
-
description:
|
139
|
+
description:
|
140
140
|
email:
|
141
141
|
- tim@icelab.com.au
|
142
142
|
executables: []
|
@@ -155,6 +155,10 @@ files:
|
|
155
155
|
- bin/console
|
156
156
|
- formalist.gemspec
|
157
157
|
- lib/formalist.rb
|
158
|
+
- lib/formalist/child_forms/builder.rb
|
159
|
+
- lib/formalist/child_forms/child_form.rb
|
160
|
+
- lib/formalist/child_forms/params_processor.rb
|
161
|
+
- lib/formalist/child_forms/validity_check.rb
|
158
162
|
- lib/formalist/definition.rb
|
159
163
|
- lib/formalist/element.rb
|
160
164
|
- lib/formalist/element/attributes.rb
|
@@ -163,8 +167,10 @@ files:
|
|
163
167
|
- lib/formalist/elements/attr.rb
|
164
168
|
- lib/formalist/elements/compound_field.rb
|
165
169
|
- lib/formalist/elements/field.rb
|
170
|
+
- lib/formalist/elements/form_field.rb
|
166
171
|
- lib/formalist/elements/group.rb
|
167
172
|
- lib/formalist/elements/many.rb
|
173
|
+
- lib/formalist/elements/many_forms.rb
|
168
174
|
- lib/formalist/elements/section.rb
|
169
175
|
- lib/formalist/elements/standard.rb
|
170
176
|
- lib/formalist/elements/standard/check_box.rb
|
@@ -199,7 +205,7 @@ homepage: https://github.com/icelab/formalist
|
|
199
205
|
licenses:
|
200
206
|
- MIT
|
201
207
|
metadata: {}
|
202
|
-
post_install_message:
|
208
|
+
post_install_message:
|
203
209
|
rdoc_options: []
|
204
210
|
require_paths:
|
205
211
|
- lib
|
@@ -214,8 +220,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
214
220
|
- !ruby/object:Gem::Version
|
215
221
|
version: '0'
|
216
222
|
requirements: []
|
217
|
-
rubygems_version: 3.
|
218
|
-
signing_key:
|
223
|
+
rubygems_version: 3.1.6
|
224
|
+
signing_key:
|
219
225
|
specification_version: 4
|
220
226
|
summary: Flexible form builder
|
221
227
|
test_files: []
|