formalist 0.6.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 812b680bec83258e220c4ad7e003db7aacc7f333cee556cd93079c22aeebbca6
4
- data.tar.gz: fc45315295d88673cb31220b0e2dddd299f5ec2804f6230b9ee99135fcaba1f7
3
+ metadata.gz: 8cb9ec48a2b65c7e8b44b9ea1db47b9a466e5b664f8c9a1ef799dc998f7ae339
4
+ data.tar.gz: aa3a86a47295192edb6278e9a19a5e675c01e041fba98f30be87c7ee04aa108a
5
5
  SHA512:
6
- metadata.gz: dd154e92d8e422108e4c46005a68c3072b41dee80781499da1c3c521e82bc5aeda5471466c30ee1ed021bdab703215bd589c92b042b35936503e1dc740884575
7
- data.tar.gz: b8d7a67e86633126f6d5a0620018ea4698742e52d511be24bd9eaaffa85c1bebcdf62d0d0b7cda4cb6b4fdf68d46052d9bd2d29dfd0bdda1531221126128d30e
6
+ metadata.gz: ea0667c4a32ca779b49b1d1fdce21a717b04e0a630680c267bea3f960b04a13d6223e3bbaabb16ae77291d14975f74fe6c2d51afc34c6d453eb293f1e0b9e791
7
+ data.tar.gz: 20c9ff5caa796d18312122cc5da852980cc51b9ebffd6eeabc182a161034997241633213478910debed24897bb93513fabf6010a4ea146abb3f4999f03511d11
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ # 0.9.0 / 2022-06-30
2
+
3
+ ### Added
4
+
5
+ - Added a `#with` method to `EmbeddedFormRenderer` to support additional call time options
6
+
7
+ # 0.8.0 / 2022-06-29
8
+
9
+ ### Changed
10
+
11
+ - Update to ruby to version 3
12
+ - Update dry-configurable to version 0.13
13
+
14
+ # 0.7.0 / 2022-04-07
15
+
16
+ ### Added
17
+
18
+ - Add support a field with an arbitrary list of forms. Supporting updates in formalist-standard-react@^4.2.0
19
+ - Add namespace and paths options to embedded form renderer
20
+ - Support dry-schema/dry-validation 1.0
21
+
1
22
  # 0.6.0 / 2020-05-06
2
23
 
3
24
  ### 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
data/formalist.gemspec CHANGED
@@ -17,9 +17,9 @@ Gem::Specification.new do |spec|
17
17
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
18
  spec.require_paths = ['lib']
19
19
 
20
- spec.required_ruby_version = ">= 2.2.0"
20
+ spec.required_ruby_version = ">= 3.0.0"
21
21
 
22
- spec.add_runtime_dependency "dry-configurable", "~> 0.7"
22
+ spec.add_runtime_dependency "dry-configurable", "~> 0.13"
23
23
  spec.add_runtime_dependency "dry-core", "~> 0.4"
24
24
  spec.add_runtime_dependency "dry-container", "~> 0.6"
25
25
  spec.add_runtime_dependency "inflecto"
@@ -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
@@ -10,12 +10,12 @@ module Formalist
10
10
 
11
11
  # @api private
12
12
  def self.build(**args)
13
- new(args)
13
+ new(**args)
14
14
  end
15
15
 
16
16
  # @api private
17
17
  def self.fill(input: {}, errors: {}, **args)
18
- new(args).fill(input: input, errors: errors)
18
+ new(**args).fill(input: input, errors: errors)
19
19
  end
20
20
 
21
21
  # @api private
@@ -43,7 +43,7 @@ module Formalist
43
43
  errors: errors,
44
44
  }.merge(args)
45
45
 
46
- self.class.new(args)
46
+ self.class.new(**args)
47
47
  end
48
48
 
49
49
  def type
@@ -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
@@ -6,7 +6,7 @@ module Formalist
6
6
  class CheckBox < Field
7
7
  attribute :question_text
8
8
 
9
- def initialize(*)
9
+ def initialize(**)
10
10
  super
11
11
 
12
12
  # Ensure value is a boolean (also: default to false for nil values)
@@ -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
 
@@ -8,7 +8,7 @@ module Formalist
8
8
  extend Dry::Configurable
9
9
  include Dry::Core::Constants
10
10
 
11
- setting :elements_container, Elements
11
+ setting :elements_container, default: Elements
12
12
 
13
13
  class << self
14
14
  attr_reader :definition
@@ -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
@@ -25,7 +25,7 @@ module Formalist
25
25
  end
26
26
 
27
27
  def register(key, **attrs)
28
- super(key.to_s, Registration.new(attrs))
28
+ super(key.to_s, Registration.new(**attrs))
29
29
  end
30
30
 
31
31
  def to_h
@@ -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,50 @@ 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
+ def with(**context_options)
30
+ self.class.new(
31
+ container,
32
+ namespace: namespace,
33
+ paths: paths,
34
+ **options.merge(context_options)
35
+ )
36
+ end
37
+
38
+ private
39
+
40
+ def resolve_key(type)
41
+ paths.each do |path|
42
+ path_key = path.tr("/", ".")
43
+ key = [namespace, path_key, type].compact.join(".")
44
+ return key if container.key?(key)
45
+ end
46
+
47
+ key = [namespace, type].compact.join(".")
48
+ return key if container.key?(key)
49
+ end
22
50
  end
23
51
  end
24
52
  end
@@ -1,3 +1,3 @@
1
1
  module Formalist
2
- VERSION = "0.6.0".freeze
2
+ VERSION = "0.9.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.9.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-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-configurable
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.7'
19
+ version: '0.13'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.7'
26
+ version: '0.13'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: dry-core
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -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
@@ -207,15 +213,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
207
213
  requirements:
208
214
  - - ">="
209
215
  - !ruby/object:Gem::Version
210
- version: 2.2.0
216
+ version: 3.0.0
211
217
  required_rubygems_version: !ruby/object:Gem::Requirement
212
218
  requirements:
213
219
  - - ">="
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.3.3
224
+ signing_key:
219
225
  specification_version: 4
220
226
  summary: Flexible form builder
221
227
  test_files: []