formalist 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
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