babl-json 0.2.1 → 0.2.2
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/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
|