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.
- checksums.yaml +4 -4
- data/Gemfile +7 -3
- data/Gemfile.lock +36 -38
- data/README.md +8 -4
- data/Rakefile +0 -7
- data/lib/formalist/element/attributes.rb +54 -0
- data/lib/formalist/element/class_interface.rb +133 -0
- data/lib/formalist/element/definition.rb +55 -0
- data/lib/formalist/element/permitted_children.rb +46 -0
- data/lib/formalist/element.rb +51 -0
- data/lib/formalist/elements/attr.rb +74 -0
- data/lib/formalist/elements/compound_field.rb +49 -0
- data/lib/formalist/elements/field.rb +73 -0
- data/lib/formalist/elements/group.rb +50 -0
- data/lib/formalist/elements/many.rb +125 -0
- data/lib/formalist/elements/section.rb +58 -0
- data/lib/formalist/elements/standard/check_box.rb +20 -0
- data/lib/formalist/elements/standard/date_field.rb +12 -0
- data/lib/formalist/elements/standard/date_time_field.rb +12 -0
- data/lib/formalist/elements/standard/hidden_field.rb +11 -0
- data/lib/formalist/elements/standard/multi_selection_field.rb +16 -0
- data/lib/formalist/elements/standard/number_field.rb +17 -0
- data/lib/formalist/elements/standard/radio_buttons.rb +13 -0
- data/lib/formalist/elements/standard/select_box.rb +13 -0
- data/lib/formalist/elements/standard/selection_field.rb +16 -0
- data/lib/formalist/elements/standard/text_area.rb +15 -0
- data/lib/formalist/elements/standard/text_field.rb +14 -0
- data/lib/formalist/elements/standard.rb +11 -0
- data/lib/formalist/elements.rb +20 -0
- data/lib/formalist/form/definition_context.rb +58 -4
- data/lib/formalist/form/result.rb +5 -27
- data/lib/formalist/form.rb +15 -35
- data/lib/formalist/types.rb +30 -0
- data/lib/formalist/version.rb +1 -1
- data/lib/formalist.rb +0 -20
- data/spec/examples.txt +8 -7
- data/spec/integration/dependency_injection_spec.rb +54 -0
- data/spec/integration/form_spec.rb +86 -13
- data/spec/spec_helper.rb +12 -5
- data/spec/support/constants.rb +11 -0
- data/spec/unit/elements/standard/check_box_spec.rb +33 -0
- metadata +36 -63
- data/lib/formalist/definition_compiler.rb +0 -61
- data/lib/formalist/display_adapters/default.rb +0 -9
- data/lib/formalist/display_adapters/radio.rb +0 -19
- data/lib/formalist/display_adapters/select.rb +0 -19
- data/lib/formalist/display_adapters/textarea.rb +0 -14
- data/lib/formalist/display_adapters.rb +0 -16
- data/lib/formalist/form/definition/attr.rb +0 -20
- data/lib/formalist/form/definition/component.rb +0 -31
- data/lib/formalist/form/definition/field.rb +0 -29
- data/lib/formalist/form/definition/group.rb +0 -31
- data/lib/formalist/form/definition/many.rb +0 -41
- data/lib/formalist/form/definition/section.rb +0 -23
- data/lib/formalist/form/definition.rb +0 -37
- data/lib/formalist/form/result/attr.rb +0 -82
- data/lib/formalist/form/result/component.rb +0 -51
- data/lib/formalist/form/result/field.rb +0 -77
- data/lib/formalist/form/result/group.rb +0 -51
- data/lib/formalist/form/result/many.rb +0 -123
- data/lib/formalist/form/result/section.rb +0 -54
- data/lib/formalist/form/validated_result.rb +0 -35
- data/lib/formalist/output_compiler.rb +0 -43
- data/lib/formalist/validation/collection_rules_compiler.rb +0 -77
- data/lib/formalist/validation/predicate_list_compiler.rb +0 -73
- data/lib/formalist/validation/value_rules_compiler.rb +0 -96
- data/spec/integration/display_adapters_spec.rb +0 -55
- data/spec/integration/validation_spec.rb +0 -86
- 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
|