babl-json 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +1 -1
- data/lib/babl/builder/chain_builder.rb +1 -1
- data/lib/babl/errors.rb +3 -3
- data/lib/babl/nodes/create_pin.rb +1 -1
- data/lib/babl/nodes/dep.rb +1 -1
- data/lib/babl/nodes/each.rb +2 -2
- data/lib/babl/nodes/fixed_array.rb +2 -2
- data/lib/babl/nodes/goto_pin.rb +1 -1
- data/lib/babl/nodes/internal_value.rb +1 -1
- data/lib/babl/nodes/merge.rb +9 -13
- data/lib/babl/nodes/nav.rb +1 -1
- data/lib/babl/nodes/object.rb +2 -2
- data/lib/babl/nodes/parent.rb +3 -3
- data/lib/babl/nodes/static.rb +3 -7
- data/lib/babl/nodes/switch.rb +2 -2
- data/lib/babl/nodes/typed.rb +42 -0
- data/lib/babl/nodes/with.rb +1 -1
- data/lib/babl/operators/call.rb +2 -2
- data/lib/babl/operators/continue.rb +1 -1
- data/lib/babl/operators/enter.rb +1 -1
- data/lib/babl/operators/object.rb +1 -1
- data/lib/babl/operators/partial.rb +2 -2
- data/lib/babl/operators/pin.rb +1 -1
- data/lib/babl/operators/static.rb +1 -1
- data/lib/babl/operators/typed.rb +25 -0
- data/lib/babl/rendering/compiled_template.rb +1 -1
- data/lib/babl/schema/any_of.rb +36 -47
- data/lib/babl/schema/dyn_array.rb +2 -2
- data/lib/babl/schema/fixed_array.rb +3 -3
- data/lib/babl/schema/object.rb +8 -8
- data/lib/babl/schema/static.rb +1 -1
- data/lib/babl/schema/typed.rb +16 -0
- data/lib/babl/template.rb +2 -0
- data/lib/babl/version.rb +1 -1
- data/spec/operators/array_spec.rb +1 -1
- data/spec/operators/continue_spec.rb +3 -3
- data/spec/operators/each_spec.rb +1 -1
- data/spec/operators/enter_spec.rb +1 -1
- data/spec/operators/extends_spec.rb +1 -1
- data/spec/operators/merge_spec.rb +2 -2
- data/spec/operators/nav_spec.rb +5 -4
- data/spec/operators/nullable_spec.rb +1 -1
- data/spec/operators/object_spec.rb +1 -1
- data/spec/operators/parent_spec.rb +1 -1
- data/spec/operators/partial_spec.rb +1 -1
- data/spec/operators/pin_spec.rb +1 -1
- data/spec/operators/static_spec.rb +1 -1
- data/spec/operators/switch_spec.rb +3 -3
- data/spec/operators/typed_spec.rb +84 -0
- data/spec/spec_helper/schema_utils.rb +27 -7
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4dec3c593ea00b70b6d191739c4c84b1d78492fa
|
4
|
+
data.tar.gz: f9bcac08981d7ed996443692bd7f8752b402bab2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15ed5dc2e9baf8cb6b94e178e9a8d743c719d73b229d4e33312525f3e743976702bcba8bc7b90069f17e2ddb8b49d4efaccb9a5518e111686847d8adc6c24f3e
|
7
|
+
data.tar.gz: d18b19d2550f3cac80d054474c3d85b60eb1be7f4ddf2e43172477f5b05b3f4a01f535adfe2f322e60dee0d69265b1162757d7886159dbd52474e1395fc7d6e8
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -66,7 +66,7 @@ As of today, the only compatible preloader implementation *has not been released
|
|
66
66
|
|
67
67
|
### Automatic documentation
|
68
68
|
|
69
|
-
The structure of the JSON produced by a BABL template can be
|
69
|
+
The structure of the JSON produced by a BABL template can be documented using [JSON-Schema](http://json-schema.org/).
|
70
70
|
|
71
71
|
### Rails integration
|
72
72
|
|
@@ -41,7 +41,7 @@ module Babl
|
|
41
41
|
def construct_terminal
|
42
42
|
construct_node do |node, context|
|
43
43
|
unless [Nodes::InternalValue.instance, Nodes::TerminalValue.instance].include?(node)
|
44
|
-
raise Errors::
|
44
|
+
raise Errors::InvalidTemplate, 'Chaining is not allowed after a terminal operator'
|
45
45
|
end
|
46
46
|
yield context
|
47
47
|
end
|
data/lib/babl/errors.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Babl
|
2
2
|
module Errors
|
3
|
-
class
|
4
|
-
class
|
5
|
-
class RenderingError <
|
3
|
+
class Base < StandardError; end
|
4
|
+
class InvalidTemplate < Base; end
|
5
|
+
class RenderingError < Base; end
|
6
6
|
end
|
7
7
|
end
|
data/lib/babl/nodes/dep.rb
CHANGED
data/lib/babl/nodes/each.rb
CHANGED
@@ -4,13 +4,13 @@ require 'values'
|
|
4
4
|
|
5
5
|
module Babl
|
6
6
|
module Nodes
|
7
|
-
class Each < Value.new(:node)
|
7
|
+
class Each < ::Value.new(:node)
|
8
8
|
def dependencies
|
9
9
|
{ __each__: node.dependencies }
|
10
10
|
end
|
11
11
|
|
12
12
|
def schema
|
13
|
-
Schema::DynArray.new(node.schema
|
13
|
+
Schema::DynArray.new(node.schema)
|
14
14
|
end
|
15
15
|
|
16
16
|
def pinned_dependencies
|
@@ -4,9 +4,9 @@ require 'values'
|
|
4
4
|
|
5
5
|
module Babl
|
6
6
|
module Nodes
|
7
|
-
class FixedArray < Value.new(:nodes)
|
7
|
+
class FixedArray < ::Value.new(:nodes)
|
8
8
|
def schema
|
9
|
-
Schema::FixedArray.new(nodes.map(&:schema)
|
9
|
+
Schema::FixedArray.new(nodes.map(&:schema))
|
10
10
|
end
|
11
11
|
|
12
12
|
def dependencies
|
data/lib/babl/nodes/goto_pin.rb
CHANGED
data/lib/babl/nodes/merge.rb
CHANGED
@@ -4,11 +4,7 @@ require 'values'
|
|
4
4
|
|
5
5
|
module Babl
|
6
6
|
module Nodes
|
7
|
-
class Merge < Value.new(:nodes)
|
8
|
-
def initialize(nodes)
|
9
|
-
super
|
10
|
-
end
|
11
|
-
|
7
|
+
class Merge < ::Value.new(:nodes)
|
12
8
|
def dependencies
|
13
9
|
nodes.map(&:dependencies).reduce({}) { |a, b| Babl::Utils::Hash.deep_merge(a, b) }
|
14
10
|
end
|
@@ -45,7 +41,7 @@ module Babl
|
|
45
41
|
when Schema::Anything === doc1 && Schema::Object === doc2
|
46
42
|
merge_object(Schema::Object::EMPTY_WITH_ADDITIONAL, doc2)
|
47
43
|
else
|
48
|
-
raise Errors::
|
44
|
+
raise Errors::InvalidTemplate, 'Only objects can be merged'
|
49
45
|
end
|
50
46
|
end
|
51
47
|
|
@@ -53,11 +49,11 @@ module Babl
|
|
53
49
|
# on left, right or both sides.
|
54
50
|
def merge_extended(doc1, doc2)
|
55
51
|
# Ensure doc1 & doc2 are both Schema::AnyOf
|
56
|
-
|
57
|
-
|
52
|
+
choices1 = Schema::AnyOf === doc1 ? doc1.choices : [doc1]
|
53
|
+
choices2 = Schema::AnyOf === doc2 ? doc2.choices : [doc2]
|
58
54
|
|
59
55
|
# Generate all possible combinations
|
60
|
-
all_docs =
|
56
|
+
all_docs = choices1.product(choices2)
|
61
57
|
.map { |choice1, choice2| merge_doc(choice1, choice2) }
|
62
58
|
|
63
59
|
# Analyze each property accross all combination to
|
@@ -68,13 +64,13 @@ module Babl
|
|
68
64
|
.map do |name, properties|
|
69
65
|
Schema::Object::Property.new(
|
70
66
|
name,
|
71
|
-
Schema::AnyOf.
|
67
|
+
Schema::AnyOf.canonical(properties.map(&:value)),
|
72
68
|
properties.size == all_docs.size && properties.all?(&:required)
|
73
69
|
)
|
74
70
|
end
|
75
71
|
|
76
72
|
# Generate the final Schema::Object
|
77
|
-
Schema::Object.new(final_properties, all_docs.any?(&:additional)
|
73
|
+
Schema::Object.new(final_properties, all_docs.any?(&:additional))
|
78
74
|
end
|
79
75
|
|
80
76
|
# Merge two Schema::Object
|
@@ -86,14 +82,14 @@ module Babl
|
|
86
82
|
doc2.properties
|
87
83
|
).each_with_object({}) { |property, acc| acc[property.name] = property }.values
|
88
84
|
|
89
|
-
Schema::Object.new
|
85
|
+
Schema::Object.new(properties, additional)
|
90
86
|
end
|
91
87
|
|
92
88
|
# Rewrite a property to allow Schema::Anything as value
|
93
89
|
def allow_anything(property)
|
94
90
|
Schema::Object::Property.new(
|
95
91
|
property.name,
|
96
|
-
Schema::AnyOf.
|
92
|
+
Schema::AnyOf.canonical([property.value, Schema::Anything.instance]),
|
97
93
|
property.required
|
98
94
|
)
|
99
95
|
end
|
data/lib/babl/nodes/nav.rb
CHANGED
data/lib/babl/nodes/object.rb
CHANGED
@@ -4,7 +4,7 @@ require 'values'
|
|
4
4
|
|
5
5
|
module Babl
|
6
6
|
module Nodes
|
7
|
-
class Object < Value.new(:nodes)
|
7
|
+
class Object < ::Value.new(:nodes)
|
8
8
|
def dependencies
|
9
9
|
nodes.values.map(&:dependencies).reduce({}) { |a, b| Babl::Utils::Hash.deep_merge(a, b) }
|
10
10
|
end
|
@@ -15,7 +15,7 @@ module Babl
|
|
15
15
|
|
16
16
|
def schema
|
17
17
|
properties = nodes.map { |k, v| Schema::Object::Property.new(k, v.schema, true) }
|
18
|
-
Schema::Object.new(properties, false
|
18
|
+
Schema::Object.new(properties, false)
|
19
19
|
end
|
20
20
|
|
21
21
|
def render(ctx)
|
data/lib/babl/nodes/parent.rb
CHANGED
@@ -4,10 +4,10 @@ require 'values'
|
|
4
4
|
|
5
5
|
module Babl
|
6
6
|
module Nodes
|
7
|
-
class Parent < Value.new(:node)
|
7
|
+
class Parent < ::Value.new(:node)
|
8
8
|
PARENT_MARKER = Utils::Ref.new
|
9
9
|
|
10
|
-
class Resolver < Value.new(:node)
|
10
|
+
class Resolver < ::Value.new(:node)
|
11
11
|
def dependencies
|
12
12
|
backpropagate_dependencies(node.dependencies)
|
13
13
|
end
|
@@ -27,7 +27,7 @@ module Babl
|
|
27
27
|
private
|
28
28
|
|
29
29
|
def backpropagate_dependencies(deps)
|
30
|
-
raise Errors::
|
30
|
+
raise Errors::InvalidTemplate, 'Out of context parent dependency' if deps.key? PARENT_MARKER
|
31
31
|
new_deps = backpropagate_dependencies_one_level(deps)
|
32
32
|
deps == new_deps ? new_deps : backpropagate_dependencies(new_deps)
|
33
33
|
end
|
data/lib/babl/nodes/static.rb
CHANGED
@@ -4,17 +4,13 @@ require 'values'
|
|
4
4
|
|
5
5
|
module Babl
|
6
6
|
module Nodes
|
7
|
-
class Static < Value.new(:
|
8
|
-
def initialize(value)
|
9
|
-
super(value)
|
10
|
-
end
|
11
|
-
|
7
|
+
class Static < ::Value.new(:value)
|
12
8
|
def schema
|
13
|
-
Schema::Static.new(
|
9
|
+
Schema::Static.new(value)
|
14
10
|
end
|
15
11
|
|
16
12
|
def render(_ctx)
|
17
|
-
|
13
|
+
value
|
18
14
|
end
|
19
15
|
|
20
16
|
def dependencies
|
data/lib/babl/nodes/switch.rb
CHANGED
@@ -5,7 +5,7 @@ require 'values'
|
|
5
5
|
|
6
6
|
module Babl
|
7
7
|
module Nodes
|
8
|
-
class Switch < Value.new(:nodes)
|
8
|
+
class Switch < ::Value.new(:nodes)
|
9
9
|
def dependencies
|
10
10
|
(nodes.values + nodes.keys).map(&:dependencies)
|
11
11
|
.reduce({}) { |a, b| Babl::Utils::Hash.deep_merge(a, b) }
|
@@ -17,7 +17,7 @@ module Babl
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def schema
|
20
|
-
Schema::AnyOf.
|
20
|
+
Schema::AnyOf.canonical(nodes.values.map(&:schema))
|
21
21
|
end
|
22
22
|
|
23
23
|
def render(ctx)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'values'
|
2
|
+
require 'babl/schema/typed'
|
3
|
+
|
4
|
+
module Babl
|
5
|
+
module Nodes
|
6
|
+
class Typed < ::Value.new(:types, :schema, :node)
|
7
|
+
BOOLEAN = method(:new).curry(3).call([TrueClass, FalseClass], Schema::Typed::BOOLEAN)
|
8
|
+
INTEGER = method(:new).curry(3).call([Integer], Schema::Typed::INTEGER)
|
9
|
+
NUMBER = method(:new).curry(3).call([Numeric], Schema::Typed::NUMBER)
|
10
|
+
STRING = method(:new).curry(3).call([String], Schema::Typed::STRING)
|
11
|
+
|
12
|
+
def dependencies
|
13
|
+
node.dependencies
|
14
|
+
end
|
15
|
+
|
16
|
+
def pinned_dependencies
|
17
|
+
node.pinned_dependencies
|
18
|
+
end
|
19
|
+
|
20
|
+
def render(ctx)
|
21
|
+
value = node.render(ctx)
|
22
|
+
unless types.any? { |type| type === value }
|
23
|
+
raise Errors::RenderingError, "Expected type '#{schema.type}': #{value}\n#{ctx.formatted_stack}"
|
24
|
+
end
|
25
|
+
value
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def initialize(*)
|
31
|
+
super
|
32
|
+
check_type_compatibility
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_type_compatibility
|
36
|
+
return if schema == node.schema
|
37
|
+
return if node.schema == Schema::Anything.instance
|
38
|
+
raise Errors::InvalidTemplate, "Type cannot be '#{schema.type}' in this context"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/babl/nodes/with.rb
CHANGED
data/lib/babl/operators/call.rb
CHANGED
@@ -7,7 +7,7 @@ module Babl
|
|
7
7
|
# Interpret whatever is passed to this method as BABL template. It is idempotent.
|
8
8
|
def call(*args, &block)
|
9
9
|
return with(*args, &block) unless block.nil?
|
10
|
-
raise Errors::
|
10
|
+
raise Errors::InvalidTemplate, 'call() expects exactly 1 argument (unless block)' unless args.size == 1
|
11
11
|
|
12
12
|
arg = args.first
|
13
13
|
|
@@ -18,7 +18,7 @@ module Babl
|
|
18
18
|
when ::Hash then object(**arg.map { |k, v| [:"#{k}", v] }.to_h)
|
19
19
|
when ::Array then array(*arg)
|
20
20
|
when ::String, ::Numeric, ::NilClass, ::TrueClass, ::FalseClass then static(arg)
|
21
|
-
else raise Errors::
|
21
|
+
else raise Errors::InvalidTemplate, "call() received invalid argument: #{arg}"
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -9,7 +9,7 @@ module Babl
|
|
9
9
|
def continue
|
10
10
|
construct_terminal { |context|
|
11
11
|
node = context[:continue]
|
12
|
-
raise Errors::
|
12
|
+
raise Errors::InvalidTemplate, 'continue() cannot be used outside switch()' unless node
|
13
13
|
node
|
14
14
|
}
|
15
15
|
end
|
data/lib/babl/operators/enter.rb
CHANGED
@@ -9,7 +9,7 @@ module Babl
|
|
9
9
|
def enter
|
10
10
|
construct_node(key: nil, continue: nil) { |node, context|
|
11
11
|
key = context[:key]
|
12
|
-
raise Errors::
|
12
|
+
raise Errors::InvalidTemplate, "No key to enter into" unless key
|
13
13
|
Nodes::Nav.new(key, node)
|
14
14
|
}
|
15
15
|
end
|
@@ -9,7 +9,7 @@ module Babl
|
|
9
9
|
# Create a JSON object node with static structure
|
10
10
|
def object(*attrs, **nested)
|
11
11
|
(attrs.map(&:to_sym) + nested.keys).group_by(&:itself).values.each do |keys|
|
12
|
-
raise Errors::
|
12
|
+
raise Errors::InvalidTemplate, "Duplicate key in object(): #{keys.first}" if keys.size > 1
|
13
13
|
end
|
14
14
|
|
15
15
|
construct_terminal { |ctx|
|
@@ -7,10 +7,10 @@ module Babl
|
|
7
7
|
# Load a partial template given its name
|
8
8
|
# A 'lookup_context' must be defined
|
9
9
|
def partial(partial_name)
|
10
|
-
raise Errors::
|
10
|
+
raise Errors::InvalidTemplate, "Cannot use partial without lookup context" unless lookup_context
|
11
11
|
|
12
12
|
path, source, partial_lookup_context = lookup_context.find(partial_name)
|
13
|
-
raise Errors::
|
13
|
+
raise Errors::InvalidTemplate, "Cannot find partial '#{partial_name}'" unless path
|
14
14
|
|
15
15
|
with_lookup_context(partial_lookup_context)
|
16
16
|
.source(source, path, 0)
|
data/lib/babl/operators/pin.rb
CHANGED
@@ -25,7 +25,7 @@ module Babl
|
|
25
25
|
# Override TemplateBase#precompile to ensure that all pin dependencies are satisfied.
|
26
26
|
def precompile
|
27
27
|
super.tap do |node|
|
28
|
-
raise Errors::
|
28
|
+
raise Errors::InvalidTemplate, 'Unresolved pin' unless node.pinned_dependencies.empty?
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'babl/nodes/typed'
|
2
|
+
|
3
|
+
module Babl
|
4
|
+
module Operators
|
5
|
+
module Typed
|
6
|
+
module DSL
|
7
|
+
def integer
|
8
|
+
construct_node(continue: nil) { |node| Nodes::Typed::INTEGER.call(node) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def number
|
12
|
+
construct_node(continue: nil) { |node| Nodes::Typed::NUMBER.call(node) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def string
|
16
|
+
construct_node(continue: nil) { |node| Nodes::Typed::STRING.call(node) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def boolean
|
20
|
+
construct_node(continue: nil) { |node| Nodes::Typed::BOOLEAN.call(node) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -4,7 +4,7 @@ require 'values'
|
|
4
4
|
|
5
5
|
module Babl
|
6
6
|
module Rendering
|
7
|
-
class CompiledTemplate < Value.new(:node, :dependencies, :preloader, :pretty, :json_schema)
|
7
|
+
class CompiledTemplate < ::Value.new(:node, :dependencies, :preloader, :pretty, :json_schema)
|
8
8
|
def json(root)
|
9
9
|
data = render(root)
|
10
10
|
::Oj.dump(data, indent: pretty ? 4 : 0, mode: :strict)
|
data/lib/babl/schema/any_of.rb
CHANGED
@@ -5,7 +5,7 @@ require 'babl/schema/object'
|
|
5
5
|
|
6
6
|
module Babl
|
7
7
|
module Schema
|
8
|
-
class AnyOf < Value.new(:choice_set)
|
8
|
+
class AnyOf < ::Value.new(:choice_set)
|
9
9
|
attr_reader :choices
|
10
10
|
|
11
11
|
def initialize(choices)
|
@@ -30,6 +30,10 @@ module Babl
|
|
30
30
|
self
|
31
31
|
end
|
32
32
|
|
33
|
+
def self.canonical(choices)
|
34
|
+
new(choices).simplify
|
35
|
+
end
|
36
|
+
|
33
37
|
private
|
34
38
|
|
35
39
|
# We can completely get rid of the AnyOf element of there is only one possible schema.
|
@@ -37,52 +41,34 @@ module Babl
|
|
37
41
|
choices.size == 1 ? choices.first : nil
|
38
42
|
end
|
39
43
|
|
40
|
-
# Try to merge
|
41
|
-
# (Object, DynArray and FixedArray).
|
44
|
+
# Try to merge null with another Anything
|
42
45
|
def simplify_nullability
|
43
|
-
if
|
44
|
-
others = choices - [Static::NULL]
|
45
|
-
others.each do |other|
|
46
|
-
new_other =
|
47
|
-
case other
|
48
|
-
when Object then Object.new(other.properties, other.additional, true)
|
49
|
-
when DynArray then DynArray.new(other.item, true)
|
50
|
-
when FixedArray then FixedArray.new(other.items, true)
|
51
|
-
when Anything then other
|
52
|
-
end
|
53
|
-
return AnyOf.new(others - [other] + [new_other]).simplify if new_other
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
nil
|
46
|
+
AnyOf.canonical(choices - [Static::NULL]) if ([Static::NULL, Anything.instance] - choices).empty?
|
58
47
|
end
|
59
48
|
|
60
49
|
# An always empty FixedArray is just a special case of a DynArray
|
61
50
|
# We can get rid of the former and only keep the DynArray
|
62
51
|
def simplify_empty_array
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
return AnyOf.new(others - [other] + [new_other]).simplify
|
70
|
-
end
|
52
|
+
return unless choices.include?(FixedArray::EMPTY)
|
53
|
+
others = choices - [FixedArray::EMPTY]
|
54
|
+
others.each do |other|
|
55
|
+
next unless DynArray === other
|
56
|
+
new_other = DynArray.new(other.item)
|
57
|
+
return AnyOf.canonical(others - [other] + [new_other])
|
71
58
|
end
|
72
|
-
|
73
59
|
nil
|
74
60
|
end
|
75
61
|
|
76
62
|
# If the static array is an instance of another dyn array, then the fixed array can be
|
77
63
|
# removed.
|
78
64
|
def simplify_dyn_and_fixed_array
|
79
|
-
|
80
|
-
fixeds = choices.select { |s| FixedArray === s && s.items.uniq.size == 1 }
|
65
|
+
fixed_arrays = choices.select { |s| FixedArray === s && s.items.uniq.size == 1 }
|
81
66
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
67
|
+
choices.each do |dyn|
|
68
|
+
next unless DynArray === dyn
|
69
|
+
fixed_arrays.each do |fixed|
|
70
|
+
new_dyn = DynArray.new(dyn.item)
|
71
|
+
return AnyOf.canonical(choices - [fixed, dyn] + [new_dyn]) if dyn.item == fixed.items.first
|
86
72
|
end
|
87
73
|
end
|
88
74
|
|
@@ -92,13 +78,10 @@ module Babl
|
|
92
78
|
# If two objects have exactly the same structure, with the exception of only one property
|
93
79
|
# having a different type, then AnyOf can be pushed down to this property.
|
94
80
|
def simplify_many_objects_only_one_difference
|
95
|
-
return unless choices.all? { |s| Object === s }
|
96
|
-
|
97
81
|
choices.each_with_index { |obj1, index1|
|
98
82
|
choices.each_with_index { |obj2, index2|
|
99
83
|
next if index2 <= index1
|
100
|
-
next unless Object === obj1 && Object === obj2
|
101
|
-
next unless obj1.nullable == obj2.nullable && obj1.additional == obj2.additional
|
84
|
+
next unless Object === obj1 && Object === obj2 && obj1.additional == obj2.additional
|
102
85
|
next unless obj1.properties.map { |p| [p.name, p.required] }.to_set ==
|
103
86
|
obj2.properties.map { |p| [p.name, p.required] }.to_set
|
104
87
|
|
@@ -108,19 +91,18 @@ module Babl
|
|
108
91
|
next unless diff1.size == 1 && diff2.size == 1 && diff1.first.name == diff2.first.name
|
109
92
|
|
110
93
|
merged = Object.new(
|
111
|
-
obj1.properties.map { |
|
112
|
-
next
|
94
|
+
obj1.properties.map { |property|
|
95
|
+
next property unless property == diff1.first
|
113
96
|
Object::Property.new(
|
114
|
-
|
115
|
-
AnyOf.
|
116
|
-
|
97
|
+
property.name,
|
98
|
+
AnyOf.canonical([diff1.first.value, diff2.first.value]),
|
99
|
+
property.required
|
117
100
|
)
|
118
101
|
},
|
119
|
-
obj1.additional
|
120
|
-
obj1.nullable
|
102
|
+
obj1.additional
|
121
103
|
)
|
122
104
|
|
123
|
-
return AnyOf.
|
105
|
+
return AnyOf.canonical(choices - [obj1, obj2] + [merged])
|
124
106
|
}
|
125
107
|
}
|
126
108
|
|
@@ -129,8 +111,15 @@ module Babl
|
|
129
111
|
|
130
112
|
# Push down the AnyOf to the item if all outputs are of type DynArray
|
131
113
|
def simplify_push_down_dyn_array
|
132
|
-
|
133
|
-
|
114
|
+
choices.each_with_index { |arr1, index1|
|
115
|
+
choices.each_with_index { |arr2, index2|
|
116
|
+
next if index2 <= index1
|
117
|
+
next unless DynArray === arr1 && DynArray === arr2
|
118
|
+
new_arr = DynArray.new(AnyOf.canonical([arr1.item, arr2.item]))
|
119
|
+
return AnyOf.canonical(choices - [arr1, arr2] + [new_arr])
|
120
|
+
}
|
121
|
+
}
|
122
|
+
nil
|
134
123
|
end
|
135
124
|
end
|
136
125
|
end
|
@@ -2,9 +2,9 @@ require 'values'
|
|
2
2
|
|
3
3
|
module Babl
|
4
4
|
module Schema
|
5
|
-
class DynArray < Value.new(:item
|
5
|
+
class DynArray < ::Value.new(:item)
|
6
6
|
def json
|
7
|
-
{ type:
|
7
|
+
{ type: 'array', items: item.json }
|
8
8
|
end
|
9
9
|
end
|
10
10
|
end
|
@@ -2,11 +2,11 @@ require 'values'
|
|
2
2
|
|
3
3
|
module Babl
|
4
4
|
module Schema
|
5
|
-
class FixedArray < Value.new(:items
|
6
|
-
EMPTY = new([]
|
5
|
+
class FixedArray < ::Value.new(:items)
|
6
|
+
EMPTY = new([])
|
7
7
|
|
8
8
|
def json
|
9
|
-
{ type:
|
9
|
+
{ type: 'array', items: items.map(&:json) }
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
data/lib/babl/schema/object.rb
CHANGED
@@ -3,25 +3,25 @@ require 'set'
|
|
3
3
|
|
4
4
|
module Babl
|
5
5
|
module Schema
|
6
|
-
class Object < Value.new(:property_set, :additional
|
6
|
+
class Object < ::Value.new(:property_set, :additional)
|
7
7
|
attr_reader :properties
|
8
8
|
|
9
|
-
def initialize(properties, additional
|
9
|
+
def initialize(properties, additional)
|
10
10
|
@properties = properties
|
11
|
-
super(properties.to_set, additional
|
11
|
+
super(properties.to_set, additional)
|
12
12
|
end
|
13
13
|
|
14
|
-
|
14
|
+
EMPTY = new([], false)
|
15
|
+
EMPTY_WITH_ADDITIONAL = new([], true)
|
16
|
+
|
17
|
+
class Property < ::Value.new(:name, :value, :required)
|
15
18
|
def initialize(name, value, required)
|
16
19
|
super(name, value, required)
|
17
20
|
end
|
18
21
|
end
|
19
22
|
|
20
|
-
EMPTY = new([], false, false)
|
21
|
-
EMPTY_WITH_ADDITIONAL = new([], true, false)
|
22
|
-
|
23
23
|
def json
|
24
|
-
{ type:
|
24
|
+
{ type: 'object' }.tap { |out|
|
25
25
|
next if properties.empty?
|
26
26
|
out[:properties] = properties.map { |property| [property.name, property.value.json] }.to_h
|
27
27
|
out[:additionalProperties] = additional
|
data/lib/babl/schema/static.rb
CHANGED
data/lib/babl/schema/typed.rb
CHANGED
data/lib/babl/template.rb
CHANGED
@@ -17,6 +17,7 @@ require 'babl/operators/pin'
|
|
17
17
|
require 'babl/operators/source'
|
18
18
|
require 'babl/operators/static'
|
19
19
|
require 'babl/operators/switch'
|
20
|
+
require 'babl/operators/typed'
|
20
21
|
require 'babl/operators/with'
|
21
22
|
|
22
23
|
require 'babl/builder/template_base'
|
@@ -42,6 +43,7 @@ module Babl
|
|
42
43
|
include Operators::Source::DSL
|
43
44
|
include Operators::Static::DSL
|
44
45
|
include Operators::Switch::DSL
|
46
|
+
include Operators::Typed::DSL
|
45
47
|
include Operators::With::DSL
|
46
48
|
end
|
47
49
|
end
|
data/lib/babl/version.rb
CHANGED
@@ -31,7 +31,7 @@ describe Babl::Operators::Array do
|
|
31
31
|
let(:object) { 1 }
|
32
32
|
|
33
33
|
it { expect(json).to eq([1]) }
|
34
|
-
it { expect(schema).to eq s_fixed_array(s_anything
|
34
|
+
it { expect(schema).to eq s_any_of(s_null, s_fixed_array(s_anything)) }
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -7,19 +7,19 @@ describe Babl::Operators::Continue do
|
|
7
7
|
context 'navigation before continue' do
|
8
8
|
template { nav(:abc).switch(false => 1, default => nav(:lol).continue).object(val: nav(:ok)) }
|
9
9
|
|
10
|
-
it { expect { compiled }.to raise_error Babl::Errors::
|
10
|
+
it { expect { compiled }.to raise_error Babl::Errors::InvalidTemplate }
|
11
11
|
end
|
12
12
|
|
13
13
|
context 'continue without switch' do
|
14
14
|
template { continue }
|
15
15
|
|
16
|
-
it { expect { compiled }.to raise_error Babl::Errors::
|
16
|
+
it { expect { compiled }.to raise_error Babl::Errors::InvalidTemplate }
|
17
17
|
end
|
18
18
|
|
19
19
|
context 'continue in sub-object' do
|
20
20
|
template { object(a: switch(default => object(x: continue))) }
|
21
21
|
|
22
|
-
it { expect { compiled }.to raise_error Babl::Errors::
|
22
|
+
it { expect { compiled }.to raise_error Babl::Errors::InvalidTemplate }
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
data/spec/operators/each_spec.rb
CHANGED
@@ -36,7 +36,7 @@ describe Babl::Operators::Each do
|
|
36
36
|
let(:object) { [1, nil] }
|
37
37
|
|
38
38
|
it { expect(json).to eq [{}, nil] }
|
39
|
-
it { expect(schema).to eq s_dyn_array(s_object
|
39
|
+
it { expect(schema).to eq s_any_of(s_dyn_array(s_any_of(s_object, s_null)), s_null) }
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -7,7 +7,7 @@ describe Babl::Operators::Enter do
|
|
7
7
|
context 'invalid usage' do
|
8
8
|
template { enter }
|
9
9
|
|
10
|
-
it { expect { compiled }.to raise_error Babl::Errors::
|
10
|
+
it { expect { compiled }.to raise_error Babl::Errors::InvalidTemplate }
|
11
11
|
end
|
12
12
|
|
13
13
|
context 'valid usage' do
|
@@ -25,6 +25,6 @@ describe Babl::Operators::Extends do
|
|
25
25
|
|
26
26
|
context 'extend a non-object and try to add properties' do
|
27
27
|
template { each.extends('string_partial', test: 1) }
|
28
|
-
it { expect { schema }.to raise_error Babl::Errors::
|
28
|
+
it { expect { schema }.to raise_error Babl::Errors::InvalidTemplate }
|
29
29
|
end
|
30
30
|
end
|
@@ -66,13 +66,13 @@ describe Babl::Operators::Merge do
|
|
66
66
|
)
|
67
67
|
}
|
68
68
|
|
69
|
-
it { expect { schema }.to raise_error Babl::Errors::
|
69
|
+
it { expect { schema }.to raise_error Babl::Errors::InvalidTemplate }
|
70
70
|
end
|
71
71
|
|
72
72
|
context 'merge only one static value' do
|
73
73
|
template { merge(42) }
|
74
74
|
|
75
|
-
it { expect { schema }.to raise_error Babl::Errors::
|
75
|
+
it { expect { schema }.to raise_error Babl::Errors::InvalidTemplate }
|
76
76
|
end
|
77
77
|
|
78
78
|
context 'merge only one dynamic value' do
|
data/spec/operators/nav_spec.rb
CHANGED
@@ -7,12 +7,13 @@ describe Babl::Operators::Nav do
|
|
7
7
|
template { nav(:a) }
|
8
8
|
|
9
9
|
context 'hash navigation' do
|
10
|
-
let(:object) { { a: 42 } }
|
11
|
-
it { expect(json).to eq(42) }
|
10
|
+
let(:object) { { a: '42' } }
|
11
|
+
it { expect(json).to eq('42') }
|
12
12
|
it { expect(dependencies).to eq(a: {}) }
|
13
13
|
|
14
|
-
context '
|
14
|
+
context 'method navigation propagate dependency chain' do
|
15
15
|
template { nav(:a).nav(:to_i) }
|
16
|
+
it { expect(json).to eq(42) }
|
16
17
|
it { expect(dependencies).to eq(a: { to_i: {} }) }
|
17
18
|
end
|
18
19
|
end
|
@@ -57,7 +58,7 @@ describe Babl::Operators::Nav do
|
|
57
58
|
|
58
59
|
context '#nav should stop key propagation for #enter' do
|
59
60
|
template { object(a: nav._) }
|
60
|
-
it { expect { compiled }.to raise_error Babl::Errors::
|
61
|
+
it { expect { compiled }.to raise_error Babl::Errors::InvalidTemplate }
|
61
62
|
end
|
62
63
|
|
63
64
|
context 'nav to array of complex objects' do
|
@@ -19,7 +19,7 @@ describe Babl::Operators::Nullable do
|
|
19
19
|
expect(schema).to eq(
|
20
20
|
s_object(
|
21
21
|
s_property(:nullprop, s_anything),
|
22
|
-
s_property(:notnullprop, s_object(s_property(:abc, s_anything),
|
22
|
+
s_property(:notnullprop, s_any_of(s_object(s_property(:abc, s_anything)), s_null))
|
23
23
|
)
|
24
24
|
)
|
25
25
|
}
|
@@ -24,7 +24,7 @@ describe Babl::Operators::Object do
|
|
24
24
|
context 'misused (chaining after object)' do
|
25
25
|
template { object(:a).object(:b) }
|
26
26
|
|
27
|
-
it { expect { compiled }.to raise_error Babl::Errors::
|
27
|
+
it { expect { compiled }.to raise_error Babl::Errors::InvalidTemplate }
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -25,7 +25,7 @@ describe Babl::Operators::Parent do
|
|
25
25
|
context 'invalid usage' do
|
26
26
|
template { parent }
|
27
27
|
|
28
|
-
it { expect { compiled }.to raise_error Babl::Errors::
|
28
|
+
it { expect { compiled }.to raise_error Babl::Errors::InvalidTemplate }
|
29
29
|
end
|
30
30
|
|
31
31
|
context 'deeply nested parent chain' do
|
@@ -25,7 +25,7 @@ describe Babl::Operators::Partial do
|
|
25
25
|
context 'missing partial' do
|
26
26
|
template { partial('i_do_not_exist') }
|
27
27
|
|
28
|
-
it { expect { compiled }.to raise_error Babl::Errors::
|
28
|
+
it { expect { compiled }.to raise_error Babl::Errors::InvalidTemplate }
|
29
29
|
end
|
30
30
|
|
31
31
|
context 'found partial' do
|
data/spec/operators/pin_spec.rb
CHANGED
@@ -60,7 +60,7 @@ describe Babl::Operators::Pin do
|
|
60
60
|
)
|
61
61
|
}
|
62
62
|
|
63
|
-
it { expect { compiled }.to raise_error Babl::Errors::
|
63
|
+
it { expect { compiled }.to raise_error Babl::Errors::InvalidTemplate }
|
64
64
|
end
|
65
65
|
|
66
66
|
context 'when pinning is mixed with a "with" context' do
|
@@ -21,7 +21,7 @@ describe Babl::Operators::Static do
|
|
21
21
|
context 'invalid' do
|
22
22
|
template { static(test: Object.new) }
|
23
23
|
|
24
|
-
it { expect { compiled }.to raise_error Babl::Errors::
|
24
|
+
it { expect { compiled }.to raise_error Babl::Errors::InvalidTemplate }
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -76,7 +76,7 @@ describe Babl::Operators::Switch do
|
|
76
76
|
context 'switch between fixed array and a dyn array producing identical output' do
|
77
77
|
template { switch(1 => nullable.each.static(1), 2 => [1]) }
|
78
78
|
|
79
|
-
it { expect(schema).to eq s_dyn_array(s_static(1)
|
79
|
+
it { expect(schema).to eq s_any_of(s_null, s_dyn_array(s_static(1))) }
|
80
80
|
end
|
81
81
|
|
82
82
|
context 'switch between similar objects having only one different property' do
|
@@ -108,9 +108,9 @@ describe Babl::Operators::Switch do
|
|
108
108
|
end
|
109
109
|
|
110
110
|
context 'switch between two possible dyn arrays' do
|
111
|
-
template { switch(1 => each.static('a'), 2 => each.static('b')) }
|
111
|
+
template { switch(1 => each.static('a'), 2 => each.static('b'), 3 => nullable.each.static('c')) }
|
112
112
|
|
113
|
-
it { expect(schema).to eq s_dyn_array(s_any_of(s_static('a'), s_static('b'))) }
|
113
|
+
it { expect(schema).to eq s_any_of(s_null, s_dyn_array(s_any_of(s_static('a'), s_static('c'), s_static('b')))) }
|
114
114
|
end
|
115
115
|
|
116
116
|
context 'with dependencies' do
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Babl::Operators::Typed do
|
4
|
+
extend SpecHelper::OperatorTesting
|
5
|
+
|
6
|
+
describe '#integer' do
|
7
|
+
template { [integer] }
|
8
|
+
|
9
|
+
it { expect(schema).to eq s_fixed_array(s_integer) }
|
10
|
+
|
11
|
+
context do
|
12
|
+
let(:object) { 12 }
|
13
|
+
it { expect(json).to eq [12] }
|
14
|
+
end
|
15
|
+
|
16
|
+
context do
|
17
|
+
let(:object) { 12.5 }
|
18
|
+
it { expect { json }.to raise_error Babl::Errors::RenderingError }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#number' do
|
23
|
+
template { [number] }
|
24
|
+
|
25
|
+
it { expect(schema).to eq s_fixed_array(s_number) }
|
26
|
+
|
27
|
+
context do
|
28
|
+
let(:object) { 12 }
|
29
|
+
it { expect(json).to eq [12] }
|
30
|
+
end
|
31
|
+
|
32
|
+
context do
|
33
|
+
let(:object) { 12.5 }
|
34
|
+
it { expect(json).to eq [12.5] }
|
35
|
+
end
|
36
|
+
|
37
|
+
context do
|
38
|
+
let(:object) { '12' }
|
39
|
+
it { expect { json }.to raise_error Babl::Errors::RenderingError }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#string' do
|
44
|
+
template { [string] }
|
45
|
+
|
46
|
+
it { expect(schema).to eq s_fixed_array(s_string) }
|
47
|
+
|
48
|
+
context do
|
49
|
+
let(:object) { [12] }
|
50
|
+
it { expect { json }.to raise_error Babl::Errors::RenderingError }
|
51
|
+
end
|
52
|
+
|
53
|
+
context do
|
54
|
+
let(:object) { '12' }
|
55
|
+
it { expect(json).to eq ['12'] }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#boolean' do
|
60
|
+
template { [boolean] }
|
61
|
+
|
62
|
+
it { expect(schema).to eq s_fixed_array(s_boolean) }
|
63
|
+
|
64
|
+
context do
|
65
|
+
let(:object) { true }
|
66
|
+
it { expect(json).to eq [true] }
|
67
|
+
end
|
68
|
+
|
69
|
+
context do
|
70
|
+
let(:object) { false }
|
71
|
+
it { expect(json).to eq [false] }
|
72
|
+
end
|
73
|
+
|
74
|
+
context do
|
75
|
+
let(:object) { 'true' }
|
76
|
+
it { expect { json }.to raise_error Babl::Errors::RenderingError }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'obviously invalid template' do
|
81
|
+
template { integer.string }
|
82
|
+
it { expect { schema }.to raise_error Babl::Errors::InvalidTemplate }
|
83
|
+
end
|
84
|
+
end
|
@@ -3,23 +3,43 @@ require 'babl'
|
|
3
3
|
module SpecHelper
|
4
4
|
module SchemaUtils
|
5
5
|
def s_any_of(*args)
|
6
|
-
Babl::Schema::AnyOf.
|
6
|
+
Babl::Schema::AnyOf.canonical(args)
|
7
7
|
end
|
8
8
|
|
9
9
|
def s_anything
|
10
10
|
Babl::Schema::Anything.instance
|
11
11
|
end
|
12
12
|
|
13
|
-
def s_dyn_array(schema
|
14
|
-
Babl::Schema::DynArray.new(schema
|
13
|
+
def s_dyn_array(schema)
|
14
|
+
Babl::Schema::DynArray.new(schema)
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
Babl::Schema::
|
17
|
+
def s_null
|
18
|
+
Babl::Schema::Static::NULL
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
Babl::Schema::
|
21
|
+
def s_integer
|
22
|
+
Babl::Schema::Typed::INTEGER
|
23
|
+
end
|
24
|
+
|
25
|
+
def s_string
|
26
|
+
Babl::Schema::Typed::STRING
|
27
|
+
end
|
28
|
+
|
29
|
+
def s_boolean
|
30
|
+
Babl::Schema::Typed::BOOLEAN
|
31
|
+
end
|
32
|
+
|
33
|
+
def s_number
|
34
|
+
Babl::Schema::Typed::NUMBER
|
35
|
+
end
|
36
|
+
|
37
|
+
def s_fixed_array(*schemas)
|
38
|
+
Babl::Schema::FixedArray.new(schemas)
|
39
|
+
end
|
40
|
+
|
41
|
+
def s_object(*properties, additional: false)
|
42
|
+
Babl::Schema::Object.new(properties, additional)
|
23
43
|
end
|
24
44
|
|
25
45
|
def s_static(value)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: babl-json
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Frederic Terrazzoni
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07-
|
11
|
+
date: 2017-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -127,6 +127,7 @@ files:
|
|
127
127
|
- lib/babl/nodes/static.rb
|
128
128
|
- lib/babl/nodes/switch.rb
|
129
129
|
- lib/babl/nodes/terminal_value.rb
|
130
|
+
- lib/babl/nodes/typed.rb
|
130
131
|
- lib/babl/nodes/with.rb
|
131
132
|
- lib/babl/operators/array.rb
|
132
133
|
- lib/babl/operators/call.rb
|
@@ -147,6 +148,7 @@ files:
|
|
147
148
|
- lib/babl/operators/source.rb
|
148
149
|
- lib/babl/operators/static.rb
|
149
150
|
- lib/babl/operators/switch.rb
|
151
|
+
- lib/babl/operators/typed.rb
|
150
152
|
- lib/babl/operators/with.rb
|
151
153
|
- lib/babl/railtie.rb
|
152
154
|
- lib/babl/rendering/compiled_template.rb
|
@@ -182,6 +184,7 @@ files:
|
|
182
184
|
- spec/operators/source_spec.rb
|
183
185
|
- spec/operators/static_spec.rb
|
184
186
|
- spec/operators/switch_spec.rb
|
187
|
+
- spec/operators/typed_spec.rb
|
185
188
|
- spec/operators/with_spec.rb
|
186
189
|
- spec/spec_helper.rb
|
187
190
|
- spec/spec_helper/operator_testing.rb
|
@@ -230,6 +233,7 @@ test_files:
|
|
230
233
|
- spec/operators/source_spec.rb
|
231
234
|
- spec/operators/static_spec.rb
|
232
235
|
- spec/operators/switch_spec.rb
|
236
|
+
- spec/operators/typed_spec.rb
|
233
237
|
- spec/operators/with_spec.rb
|
234
238
|
- spec/spec_helper.rb
|
235
239
|
- spec/spec_helper/operator_testing.rb
|