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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 812b680bec83258e220c4ad7e003db7aacc7f333cee556cd93079c22aeebbca6
4
- data.tar.gz: fc45315295d88673cb31220b0e2dddd299f5ec2804f6230b9ee99135fcaba1f7
3
+ metadata.gz: e0d3e4752a943653fc73963ad57981a923b541b23e4d621fbe938bdd07bb42ff
4
+ data.tar.gz: 20ec653404675c1ac5dfae830151a383450c908d4e46a8f4edf32dc0bf989ae2
5
5
  SHA512:
6
- metadata.gz: dd154e92d8e422108e4c46005a68c3072b41dee80781499da1c3c521e82bc5aeda5471466c30ee1ed021bdab703215bd589c92b042b35936503e1dc740884575
7
- data.tar.gz: b8d7a67e86633126f6d5a0620018ea4698742e52d511be24bd9eaaffa85c1bebcdf62d0d0b7cda4cb6b4fdf68d46052d9bd2d29dfd0bdda1531221126128d30e
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
@@ -5,7 +5,7 @@ gemspec
5
5
  group :test do
6
6
  gem "codeclimate-test-reporter", require: nil
7
7
  gem "dry-auto_inject"
8
- gem "dry-validation"
8
+ gem "dry-validation", "~> 1.0"
9
9
  end
10
10
 
11
11
  group :tools do
@@ -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.fetch(name) { {} }
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
@@ -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`` parameter for `many` elements is for some reason
45
- # doubly nested right now, so we need to flatten it.
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.messages).to_ast
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
- if container.key?(type)
17
- container[type].(data, options)
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
@@ -1,3 +1,3 @@
1
1
  module Formalist
2
- VERSION = "0.6.0".freeze
2
+ VERSION = "0.7.0".freeze
3
3
  end
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.6.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: 2020-05-06 00:00:00.000000000 Z
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.0.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: []