formalist 0.2.2 → 0.2.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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -3
  3. data/Gemfile.lock +36 -38
  4. data/README.md +8 -4
  5. data/Rakefile +0 -7
  6. data/lib/formalist/element/attributes.rb +54 -0
  7. data/lib/formalist/element/class_interface.rb +133 -0
  8. data/lib/formalist/element/definition.rb +55 -0
  9. data/lib/formalist/element/permitted_children.rb +46 -0
  10. data/lib/formalist/element.rb +51 -0
  11. data/lib/formalist/elements/attr.rb +74 -0
  12. data/lib/formalist/elements/compound_field.rb +49 -0
  13. data/lib/formalist/elements/field.rb +73 -0
  14. data/lib/formalist/elements/group.rb +50 -0
  15. data/lib/formalist/elements/many.rb +125 -0
  16. data/lib/formalist/elements/section.rb +58 -0
  17. data/lib/formalist/elements/standard/check_box.rb +20 -0
  18. data/lib/formalist/elements/standard/date_field.rb +12 -0
  19. data/lib/formalist/elements/standard/date_time_field.rb +12 -0
  20. data/lib/formalist/elements/standard/hidden_field.rb +11 -0
  21. data/lib/formalist/elements/standard/multi_selection_field.rb +16 -0
  22. data/lib/formalist/elements/standard/number_field.rb +17 -0
  23. data/lib/formalist/elements/standard/radio_buttons.rb +13 -0
  24. data/lib/formalist/elements/standard/select_box.rb +13 -0
  25. data/lib/formalist/elements/standard/selection_field.rb +16 -0
  26. data/lib/formalist/elements/standard/text_area.rb +15 -0
  27. data/lib/formalist/elements/standard/text_field.rb +14 -0
  28. data/lib/formalist/elements/standard.rb +11 -0
  29. data/lib/formalist/elements.rb +20 -0
  30. data/lib/formalist/form/definition_context.rb +58 -4
  31. data/lib/formalist/form/result.rb +5 -27
  32. data/lib/formalist/form.rb +15 -35
  33. data/lib/formalist/types.rb +30 -0
  34. data/lib/formalist/version.rb +1 -1
  35. data/lib/formalist.rb +0 -20
  36. data/spec/examples.txt +8 -7
  37. data/spec/integration/dependency_injection_spec.rb +54 -0
  38. data/spec/integration/form_spec.rb +86 -13
  39. data/spec/spec_helper.rb +12 -5
  40. data/spec/support/constants.rb +11 -0
  41. data/spec/unit/elements/standard/check_box_spec.rb +33 -0
  42. metadata +36 -63
  43. data/lib/formalist/definition_compiler.rb +0 -61
  44. data/lib/formalist/display_adapters/default.rb +0 -9
  45. data/lib/formalist/display_adapters/radio.rb +0 -19
  46. data/lib/formalist/display_adapters/select.rb +0 -19
  47. data/lib/formalist/display_adapters/textarea.rb +0 -14
  48. data/lib/formalist/display_adapters.rb +0 -16
  49. data/lib/formalist/form/definition/attr.rb +0 -20
  50. data/lib/formalist/form/definition/component.rb +0 -31
  51. data/lib/formalist/form/definition/field.rb +0 -29
  52. data/lib/formalist/form/definition/group.rb +0 -31
  53. data/lib/formalist/form/definition/many.rb +0 -41
  54. data/lib/formalist/form/definition/section.rb +0 -23
  55. data/lib/formalist/form/definition.rb +0 -37
  56. data/lib/formalist/form/result/attr.rb +0 -82
  57. data/lib/formalist/form/result/component.rb +0 -51
  58. data/lib/formalist/form/result/field.rb +0 -77
  59. data/lib/formalist/form/result/group.rb +0 -51
  60. data/lib/formalist/form/result/many.rb +0 -123
  61. data/lib/formalist/form/result/section.rb +0 -54
  62. data/lib/formalist/form/validated_result.rb +0 -35
  63. data/lib/formalist/output_compiler.rb +0 -43
  64. data/lib/formalist/validation/collection_rules_compiler.rb +0 -77
  65. data/lib/formalist/validation/predicate_list_compiler.rb +0 -73
  66. data/lib/formalist/validation/value_rules_compiler.rb +0 -96
  67. data/spec/integration/display_adapters_spec.rb +0 -55
  68. data/spec/integration/validation_spec.rb +0 -86
  69. data/spec/unit/output_compiler_spec.rb +0 -70
@@ -1,35 +0,0 @@
1
- require "forwardable"
2
-
3
- module Formalist
4
- class Form
5
- class ValidatedResult
6
- extend Forwardable
7
-
8
- def_delegators :@result,
9
- :input,
10
- :schema,
11
- :elements,
12
- :validation,
13
- :output
14
-
15
- # @api private
16
- attr_reader :result
17
-
18
- def initialize(result)
19
- @result = result
20
- end
21
-
22
- def success?
23
- validation.success?
24
- end
25
-
26
- def messages
27
- validation.messages
28
- end
29
-
30
- def to_ast
31
- elements.map { |el| el.(output, schema.rules.map(&:to_ary), messages).to_ast }
32
- end
33
- end
34
- end
35
- end
@@ -1,43 +0,0 @@
1
- module Formalist
2
- class OutputCompiler
3
- def call(ast)
4
- ast.map { |node| visit(node) }.inject(:merge)
5
- end
6
-
7
- private
8
-
9
- def visit(node)
10
- send(:"visit_#{node[0]}", node[1])
11
- end
12
-
13
- def visit_attr(data)
14
- name, predicates, errors, children = data
15
-
16
- {name => children.map { |node| visit(node) }.inject(:merge) }
17
- end
18
-
19
- def visit_field(data)
20
- name, _type, _display_variant, value, _predicates, _errors = data
21
-
22
- {name => value.to_s}
23
- end
24
-
25
- def visit_group(data)
26
- config, children = data
27
-
28
- children.map { |node| visit(node) }.inject(:merge)
29
- end
30
-
31
- def visit_many(data)
32
- name, predicates, errors, config, template, children = data
33
-
34
- {name => children.map { |item| item.map { |node| visit(node) }.inject(:merge) }}
35
- end
36
-
37
- def visit_section(data)
38
- name, config, children = data
39
-
40
- children.map { |node| visit(node) }.inject(:merge)
41
- end
42
- end
43
- end
@@ -1,77 +0,0 @@
1
- module Formalist
2
- module Validation
3
- class CollectionRulesCompiler
4
- attr_reader :target_name
5
-
6
- def initialize(target_name)
7
- @target_name = target_name
8
- end
9
-
10
- def call(ast)
11
- ast.map { |node| visit(node) }.reduce([], :concat).each_slice(2).to_a
12
- end
13
-
14
- private
15
-
16
- def visit(node)
17
- name, nodes = node
18
- send(:"visit_#{name}", nodes)
19
- end
20
-
21
- def visit_set(node)
22
- name, rules = node
23
- return [] unless name == target_name
24
-
25
- rules.flatten(1)
26
- end
27
-
28
- def visit_each(node)
29
- name, rule = node
30
- return [] unless name == target_name
31
-
32
- visit(rule)
33
- end
34
-
35
- def visit_predicate(node)
36
- name, args = node
37
- [:predicate, node]
38
- end
39
-
40
- def visit_and(node)
41
- left, right = node
42
- flatten_logical_operation(:and, [visit(left), visit(right)])
43
- end
44
-
45
- def visit_or(node)
46
- left, right = node
47
- flatten_logical_operation(:or, [visit(left), visit(right)])
48
- end
49
-
50
- def visit_xor(node)
51
- left, right = node
52
- flatten_logical_operation(:xor, [visit(left), visit(right)])
53
- end
54
-
55
- def visit_implication(node)
56
- left, right = node
57
- flatten_logical_operation(:implication, [visit(left), visit(right)])
58
- end
59
-
60
- def method_missing(name, *args)
61
- []
62
- end
63
-
64
- def flatten_logical_operation(name, contents)
65
- contents = contents.select(&:any?)
66
-
67
- if contents.length == 0
68
- []
69
- elsif contents.length == 1
70
- contents.first
71
- else
72
- [name, contents]
73
- end
74
- end
75
- end
76
- end
77
- end
@@ -1,73 +0,0 @@
1
- module Formalist
2
- module Validation
3
- class PredicateListCompiler
4
- IGNORED_PREDICATES = [:key?].freeze
5
-
6
- def call(ast)
7
- ast.map { |node| visit(node) }.reduce([], :concat).each_slice(2).to_a
8
- end
9
-
10
- private
11
-
12
- def visit(node)
13
- name, nodes = node
14
- send(:"visit_#{name}", nodes)
15
- end
16
-
17
- def visit_key(node)
18
- name, predicate = node
19
-
20
- visit(predicate)
21
- end
22
-
23
- def visit_val(node)
24
- name, predicate = node
25
-
26
- visit(predicate)
27
- end
28
-
29
- def visit_predicate(node)
30
- name, args = node
31
- return [] if IGNORED_PREDICATES.include?(name)
32
-
33
- [:predicate, node]
34
- end
35
-
36
- def visit_and(node)
37
- left, right = node
38
- flatten_logical_operation(:and, [visit(left), visit(right)])
39
- end
40
-
41
- def visit_or(node)
42
- left, right = node
43
- flatten_logical_operation(:or, [visit(left), visit(right)])
44
- end
45
-
46
- def visit_xor(node)
47
- left, right = node
48
- flatten_logical_operation(:xor, [visit(left), visit(right)])
49
- end
50
-
51
- def visit_implication(node)
52
- left, right = node
53
- flatten_logical_operation(:implication, [visit(left), visit(right)])
54
- end
55
-
56
- def method_missing(name, *args)
57
- []
58
- end
59
-
60
- def flatten_logical_operation(name, contents)
61
- contents = contents.select(&:any?)
62
-
63
- if contents.length == 0
64
- []
65
- elsif contents.length == 1
66
- contents.first
67
- else
68
- [name, contents]
69
- end
70
- end
71
- end
72
- end
73
- end
@@ -1,96 +0,0 @@
1
- module Formalist
2
- module Validation
3
- class ValueRulesCompiler
4
- attr_reader :target_name
5
-
6
- def initialize(target_name)
7
- @target_name = target_name
8
- end
9
-
10
- def call(ast)
11
- ast.map { |node| visit(node) }.reduce([], :concat).each_slice(2).to_a
12
- end
13
-
14
- private
15
-
16
- def visit(node)
17
- name, nodes = node
18
- send(:"visit_#{name}", nodes)
19
- end
20
-
21
- def visit_key(node)
22
- # We can ignore "key" checks - we'll only pick up rules for keys we
23
- # know will exist, since they're attached to fields.
24
- []
25
- end
26
-
27
- def visit_val(node)
28
- name, predicate = node
29
-
30
- # Support names that show as keypaths, e.g. [:reviews, :rating]
31
- name = name.last if name.is_a?(Array)
32
-
33
- return [] unless name == target_name
34
-
35
- # Skip the "val" prefix
36
-
37
- [:val, [name, visit(predicate)]]
38
- end
39
-
40
- # def visit_set(node)
41
- # name, rules = node
42
- # return [] unless name == target_name
43
-
44
- # rules.flatten(1)
45
- # end
46
-
47
- # def visit_each(node)
48
- # name, rule = node
49
- # return [] unless name == target_name
50
-
51
- # visit(rule)
52
- # end
53
-
54
- def visit_predicate(node)
55
- name, args = node
56
- [:predicate, node]
57
- end
58
-
59
- def visit_and(node)
60
- left, right = node
61
- flatten_logical_operation(:and, [visit(left), visit(right)])
62
- end
63
-
64
- def visit_or(node)
65
- left, right = node
66
- flatten_logical_operation(:or, [visit(left), visit(right)])
67
- end
68
-
69
- def visit_xor(node)
70
- left, right = node
71
- flatten_logical_operation(:xor, [visit(left), visit(right)])
72
- end
73
-
74
- def visit_implication(node)
75
- left, right = node
76
- flatten_logical_operation(:implication, [visit(left), visit(right)])
77
- end
78
-
79
- def method_missing(name, *args)
80
- []
81
- end
82
-
83
- def flatten_logical_operation(name, contents)
84
- contents = contents.select(&:any?)
85
-
86
- if contents.length == 0
87
- []
88
- elsif contents.length == 1
89
- contents.first
90
- else
91
- [name, contents]
92
- end
93
- end
94
- end
95
- end
96
- end
@@ -1,55 +0,0 @@
1
- RSpec.describe "Display adapters" do
2
- let(:schema) {
3
- Class.new(Dry::Validation::Schema) do
4
- key(:temperature_unit)
5
- end.new
6
- }
7
-
8
- subject(:form) {
9
- Class.new(Formalist::Form) do
10
- field :temperature_unit, type: "string", display: "select", option_values: [%w[c c], %w[f f]]
11
- end.new(schema)
12
- }
13
-
14
- it "outputs an AST" do
15
- expect(form.build({}).to_ast).to eq [
16
- [:field, [
17
- :temperature_unit,
18
- "string",
19
- "select",
20
- nil,
21
- [],
22
- [],
23
- [
24
- [:option_values, [["c", "c"], ["f", "f"]]]
25
- ]
26
- ]]
27
- ]
28
- end
29
-
30
- it "supports custom disply adapters in a provided container" do
31
- adapter_class = Class.new do
32
- def call(field)
33
- field.to_display_variant("custom")
34
- end
35
- end
36
-
37
- container = Class.new(Formalist::DisplayAdapters) do
38
- register "custom", adapter_class.new
39
- end
40
-
41
- form = Class.new(Formalist::Form) do
42
- configure do |config|
43
- config.display_adapters = container
44
- end
45
-
46
- field :name, type: "string", display: "custom"
47
- field :email, type: "string"
48
- end.new(schema)
49
-
50
- expect(form.build({}).to_ast).to eq [
51
- [:field, [:name, "string", "custom", nil, [], [], []]],
52
- [:field, [:email, "string", "default", nil, [], [], []]],
53
- ]
54
- end
55
- end
@@ -1,86 +0,0 @@
1
- require "dry-validation"
2
- require "pp"
3
-
4
- RSpec.describe "Form validation" do
5
- subject(:schema) {
6
- Class.new(Dry::Validation::Schema) do
7
- key(:title) { |title| title.filled? }
8
- key(:rating) { |rating| rating.gteq?(1) & rating.lteq?(10) }
9
-
10
- key(:reviews) do |reviews|
11
- reviews.filled? & \
12
- reviews.each do |review|
13
- review.key(:summary) { |summary| summary.filled? }
14
- review.key(:rating) { |rating| rating.gteq?(1) & rating.lteq?(10) }
15
- end
16
-
17
- end
18
-
19
- # TODO: uncomment/fix the "meta" related sections (here and below) once
20
- # we have a clarification on dryrb/dry-validation#58.
21
- #
22
- # key(:meta) do |meta|
23
- # meta.key(:pages) { |pages| pages.filled? }
24
- # end
25
- end.new
26
- }
27
-
28
- subject(:form) {
29
- Class.new(Formalist::Form) do
30
- field :title, type: "string"
31
- field :rating, type: "int"
32
-
33
- many :reviews do |review|
34
- review.field :summary, type: "string"
35
- review.field :rating, type: "int"
36
- end
37
-
38
- # attr :meta do |meta|
39
- # meta.field :pages, type: "int"
40
- # end
41
- end.new(schema)
42
- }
43
-
44
- it "includes validation rules and errors in the AST" do
45
- input = {
46
- reviews: [{summary: "Great", rating: 0}, {summary: "", rating: 1}],
47
- meta: {pages: nil}
48
- }
49
-
50
- expect(form.build(input).validate.to_ast).to eq [
51
- [:field, [:title, "string", "default", nil, [[:predicate, [:filled?, []]]], ["title is missing"], []]],
52
- [:field, [:rating, "int", "default", nil, [[:and, [[:predicate, [:gteq?, [1]]], [:predicate, [:lteq?, [10]]]]]], ["rating is missing", "rating must be greater than or equal to 1", "rating must be less than or equal to 10"], []]],
53
- [:many, [:reviews,
54
- [[:predicate, [:filled?, []]]],
55
- [],
56
- [
57
- [:allow_create, true],
58
- [:allow_update, true],
59
- [:allow_destroy, true],
60
- [:allow_reorder, true],
61
- ],
62
- [
63
- [:field, [:summary, "string", "default", nil, [[:predicate, [:filled?, []]]], [], []]],
64
- [:field, [:rating, "int", "default", nil, [[:and, [[:predicate, [:gteq?, [1]]], [:predicate, [:lteq?, [10]]]]]], [], []]],
65
- ],
66
- [
67
- [
68
- [:field, [:summary, "string", "default", "Great", [[:predicate, [:filled?, []]]], [], []]],
69
- [:field, [:rating, "int", "default", 0, [[:and, [[:predicate, [:gteq?, [1]]], [:predicate, [:lteq?, [10]]]]]], ["rating must be greater than or equal to 1"], []]],
70
- ],
71
- [
72
- [:field, [:summary, "string", "default", "", [[:predicate, [:filled?, []]]], ["summary must be filled"], []]],
73
- [:field, [:rating, "int", "default", 1, [[:and, [[:predicate, [:gteq?, [1]]], [:predicate, [:lteq?, [10]]]]]], [], []]],
74
- ]
75
- ],
76
- ]],
77
- # [:attr, [:meta,
78
- # [],
79
- # [],
80
- # [
81
- # [:field, [:pages, "int", "default", nil, [[:predicate, [:filled?, []]]], ["pages must be filled"], []]]
82
- # ],
83
- # ]]
84
- ]
85
- end
86
- end
@@ -1,70 +0,0 @@
1
- RSpec.describe Formalist::OutputCompiler do
2
- subject(:compiler) { Formalist::OutputCompiler.new }
3
-
4
- let(:schema) {
5
- Class.new(Dry::Validation::Schema) do
6
- key(:title, &:str?)
7
- key(:rating, &:int?)
8
-
9
- key(:reviews) do |reviews|
10
- reviews.each do |review|
11
- review.key(:description, &:str?)
12
- review.key(:rating, &:int?)
13
- end
14
- end
15
-
16
- key(:meta) do |meta|
17
- meta.key(:pages, &:int?)
18
- meta.key(:publisher, &:str?)
19
- end
20
- end.new
21
- }
22
-
23
- let(:form) {
24
- Class.new(Formalist::Form) do
25
- field :title, type: "string"
26
- field :rating, type: "int"
27
-
28
- many :reviews do |review|
29
- review.field :description, type: "string"
30
- review.field :rating, type: "int"
31
- end
32
-
33
- attr :meta do |meta|
34
- meta.section "Metadata" do |section|
35
- section.group do |group|
36
- group.field :pages, type: "int"
37
- group.field :publisher, type: "string"
38
- end
39
- end
40
- end
41
- end.new(schema)
42
- }
43
-
44
- let(:input) {
45
- {
46
- title: "Aurora",
47
- rating: "10",
48
- reviews: [
49
- {
50
- description: "Wonderful",
51
- rating: "10",
52
- },
53
- {
54
- description: "Enchanting",
55
- rating: "9",
56
- }
57
- ],
58
- meta: {
59
- pages: "321",
60
- publisher: "Orbit",
61
- },
62
- }
63
- }
64
-
65
- let(:ast) { form.build(input).to_ast }
66
-
67
- it "works" do
68
- expect(compiler.call(ast)).to eq input
69
- end
70
- end