babl-json 0.5.9 → 0.6.0
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/lib/babl.rb +2 -0
- data/lib/babl/builder/chain_builder.rb +17 -17
- data/lib/babl/builder/template_base.rb +9 -5
- data/lib/babl/nodes/concat.rb +5 -2
- 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/merge.rb +6 -2
- data/lib/babl/nodes/nav.rb +8 -7
- data/lib/babl/nodes/object.rb +3 -2
- data/lib/babl/nodes/parent.rb +1 -0
- data/lib/babl/nodes/switch.rb +6 -2
- data/lib/babl/nodes/with.rb +8 -7
- data/lib/babl/operators/array.rb +2 -2
- data/lib/babl/operators/call.rb +1 -2
- data/lib/babl/operators/concat.rb +2 -2
- data/lib/babl/operators/continue.rb +12 -0
- data/lib/babl/operators/dep.rb +1 -1
- data/lib/babl/operators/each.rb +1 -1
- data/lib/babl/operators/enter.rb +40 -7
- data/lib/babl/operators/merge.rb +3 -5
- data/lib/babl/operators/nav.rb +3 -2
- data/lib/babl/operators/object.rb +2 -2
- data/lib/babl/operators/parent.rb +2 -1
- data/lib/babl/operators/partial.rb +2 -0
- data/lib/babl/operators/pin.rb +3 -2
- data/lib/babl/operators/source.rb +1 -0
- data/lib/babl/operators/switch.rb +4 -4
- data/lib/babl/operators/with.rb +4 -4
- data/lib/babl/schema/any_of.rb +11 -2
- data/lib/babl/schema/json_schema.rb +0 -0
- data/lib/babl/schema/object.rb +2 -0
- data/lib/babl/utils/dsl_proxy.rb +1 -1
- data/lib/babl/utils/hash.rb +2 -1
- data/lib/babl/utils/value.rb +1 -0
- data/lib/babl/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1b50e6e799274d6d0286709ee0484a75dea2e09f
|
4
|
+
data.tar.gz: 5b728f4581bea01d71aaf62af160f06b1c0551bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0af0e4fa77e6971d03230617cdf13c211d6587603bd509c531faf9d9e9358b1e88eb982be6df48551ac8033ed190f7d52357988e987ee3bc5c57f609d8528a1
|
7
|
+
data.tar.gz: c88f3b618e4fe2f2dc9181ed07ee6333d7cdfbcd5fff68071cba0a87b9155f9f1e7a13d89cb36cba68b8421773b4330767d1069e290f7522e171b4a9bd9ad532
|
data/lib/babl.rb
CHANGED
@@ -52,6 +52,7 @@ module Babl
|
|
52
52
|
query = File.join(search_path, "{#{partial_name}}{.babl,}")
|
53
53
|
path = Dir[query].first
|
54
54
|
return unless path
|
55
|
+
|
55
56
|
source = File.read(path)
|
56
57
|
[current_template.source(source, path, 0), self]
|
57
58
|
end
|
@@ -76,6 +77,7 @@ module Babl
|
|
76
77
|
def template
|
77
78
|
cached = @cached_template
|
78
79
|
return cached.last if cached && [config.using].flatten == cached.first
|
80
|
+
|
79
81
|
# Calling 'using' is a very inefficient operation, because
|
80
82
|
# it creates a new class. We can avoid that cost most of the
|
81
83
|
# time, assuming 'config.using' does not change often (typically
|
@@ -29,8 +29,8 @@ module Babl
|
|
29
29
|
@scope = block
|
30
30
|
end
|
31
31
|
|
32
|
-
def precompile(node,
|
33
|
-
bind(BoundOperator.new(context)).
|
32
|
+
def precompile(node, context)
|
33
|
+
bind(BoundOperator.new(context, &:itself)).scope.call(node)
|
34
34
|
end
|
35
35
|
|
36
36
|
def bind(bound)
|
@@ -43,38 +43,38 @@ module Babl
|
|
43
43
|
unless [Nodes::InternalValue.instance, Nodes::TerminalValue.instance].include?(node)
|
44
44
|
raise Errors::InvalidTemplate, 'Chaining is not allowed after a terminal operator'
|
45
45
|
end
|
46
|
+
|
46
47
|
yield context
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
50
51
|
# Append an operator to the chain, and return a new Builder object
|
51
|
-
def construct_node
|
52
|
+
def construct_node
|
53
|
+
wrap { |bound|
|
54
|
+
BoundOperator.new(bound.context) { |node| bound.scope[yield(node, bound.context)] }
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def construct_context
|
52
59
|
wrap { |bound|
|
53
|
-
|
54
|
-
|
55
|
-
|
60
|
+
new_context = yield(bound.context)
|
61
|
+
next bound if bound.context.equal?(new_context)
|
62
|
+
|
63
|
+
BoundOperator.new(new_context, &bound.scope)
|
56
64
|
}
|
57
65
|
end
|
58
66
|
|
59
67
|
def wrap
|
60
|
-
|
68
|
+
ChainBuilder.new { |bound| yield bind(bound) }
|
61
69
|
end
|
62
70
|
end
|
63
71
|
|
64
72
|
class BoundOperator
|
65
|
-
attr_reader :context
|
73
|
+
attr_reader :context, :scope
|
66
74
|
|
67
75
|
def initialize(context, &scope)
|
68
76
|
@context = context
|
69
|
-
@scope = scope
|
70
|
-
end
|
71
|
-
|
72
|
-
def precompile(node)
|
73
|
-
@scope[node]
|
74
|
-
end
|
75
|
-
|
76
|
-
def nest(context)
|
77
|
-
self.class.new(context) { |node| @scope[yield node] }
|
77
|
+
@scope = scope
|
78
78
|
end
|
79
79
|
end
|
80
80
|
end
|
@@ -17,7 +17,7 @@ module Babl
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def compile(preloader: Rendering::NoopPreloader, pretty: true, optimize: true, lookup_context: nil)
|
20
|
-
tree = precompile(lookup_context: lookup_context)
|
20
|
+
tree = precompile(Nodes::TerminalValue.instance, lookup_context: lookup_context)
|
21
21
|
tree = tree.optimize if optimize
|
22
22
|
validate(tree)
|
23
23
|
|
@@ -46,12 +46,16 @@ module Babl
|
|
46
46
|
# NOOP
|
47
47
|
end
|
48
48
|
|
49
|
-
def precompile(node
|
50
|
-
builder.precompile(node,
|
49
|
+
def precompile(node, context)
|
50
|
+
builder.precompile(node, context)
|
51
51
|
end
|
52
52
|
|
53
|
-
def construct_node(
|
54
|
-
self.class.new builder.construct_node(
|
53
|
+
def construct_node(&block)
|
54
|
+
self.class.new builder.construct_node(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def construct_context(&block)
|
58
|
+
self.class.new builder.construct_context(&block)
|
55
59
|
end
|
56
60
|
|
57
61
|
def construct_terminal(&block)
|
data/lib/babl/nodes/concat.rb
CHANGED
@@ -7,11 +7,11 @@ module Babl
|
|
7
7
|
module Nodes
|
8
8
|
class Concat < Utils::Value.new(:nodes)
|
9
9
|
memoize def dependencies
|
10
|
-
Babl::Utils::Hash.deep_merge(
|
10
|
+
Babl::Utils::Hash.deep_merge(nodes.map(&:dependencies))
|
11
11
|
end
|
12
12
|
|
13
13
|
memoize def pinned_dependencies
|
14
|
-
Babl::Utils::Hash.deep_merge(
|
14
|
+
Babl::Utils::Hash.deep_merge(nodes.map(&:pinned_dependencies))
|
15
15
|
end
|
16
16
|
|
17
17
|
memoize def schema
|
@@ -47,6 +47,7 @@ module Babl
|
|
47
47
|
|
48
48
|
def optimize_single
|
49
49
|
return unless nodes.size == 1
|
50
|
+
|
50
51
|
optimized = nodes.first.optimize
|
51
52
|
|
52
53
|
case optimized
|
@@ -62,6 +63,7 @@ module Babl
|
|
62
63
|
def optimize_concatenated_arrays
|
63
64
|
optimized_nodes = nodes.map(&:optimize)
|
64
65
|
return if optimized_nodes == nodes
|
66
|
+
|
65
67
|
Concat.new(optimized_nodes).optimize
|
66
68
|
end
|
67
69
|
|
@@ -70,6 +72,7 @@ module Babl
|
|
70
72
|
obj1 = constant_to_array(obj1) if Constant === obj1
|
71
73
|
obj2 = constant_to_array(obj2) if Constant === obj2
|
72
74
|
next unless FixedArray === obj1 && FixedArray === obj2
|
75
|
+
|
73
76
|
new_nodes = nodes.dup
|
74
77
|
new_nodes[idx] = FixedArray.new(obj1.nodes + obj2.nodes)
|
75
78
|
new_nodes[idx + 1] = nil
|
@@ -15,7 +15,7 @@ module Babl
|
|
15
15
|
end
|
16
16
|
|
17
17
|
memoize def dependencies
|
18
|
-
Babl::Utils::Hash.deep_merge(node.dependencies, node.pinned_dependencies[ref] || Utils::Hash::EMPTY)
|
18
|
+
Babl::Utils::Hash.deep_merge([node.dependencies, node.pinned_dependencies[ref] || Utils::Hash::EMPTY])
|
19
19
|
end
|
20
20
|
|
21
21
|
memoize def pinned_dependencies
|
data/lib/babl/nodes/dep.rb
CHANGED
data/lib/babl/nodes/each.rb
CHANGED
@@ -10,10 +10,10 @@ module Babl
|
|
10
10
|
node_deps = node.dependencies
|
11
11
|
child_deps = node_deps.reject { |key, _| key == Parent::PARENT_MARKER }
|
12
12
|
|
13
|
-
Babl::Utils::Hash.deep_merge(
|
13
|
+
Babl::Utils::Hash.deep_merge([
|
14
14
|
node_deps[Parent::PARENT_MARKER] || Utils::Hash::EMPTY,
|
15
15
|
__each__: child_deps
|
16
|
-
)
|
16
|
+
])
|
17
17
|
end
|
18
18
|
|
19
19
|
memoize def schema
|
@@ -13,11 +13,11 @@ module Babl
|
|
13
13
|
end
|
14
14
|
|
15
15
|
memoize def dependencies
|
16
|
-
Babl::Utils::Hash.deep_merge(
|
16
|
+
Babl::Utils::Hash.deep_merge(nodes.map(&:dependencies))
|
17
17
|
end
|
18
18
|
|
19
19
|
memoize def pinned_dependencies
|
20
|
-
Babl::Utils::Hash.deep_merge(
|
20
|
+
Babl::Utils::Hash.deep_merge(nodes.map(&:pinned_dependencies))
|
21
21
|
end
|
22
22
|
|
23
23
|
memoize def optimize
|
data/lib/babl/nodes/goto_pin.rb
CHANGED
data/lib/babl/nodes/merge.rb
CHANGED
@@ -8,11 +8,11 @@ module Babl
|
|
8
8
|
module Nodes
|
9
9
|
class Merge < Utils::Value.new(:nodes)
|
10
10
|
memoize def dependencies
|
11
|
-
Babl::Utils::Hash.deep_merge(
|
11
|
+
Babl::Utils::Hash.deep_merge(nodes.map(&:dependencies))
|
12
12
|
end
|
13
13
|
|
14
14
|
memoize def pinned_dependencies
|
15
|
-
Babl::Utils::Hash.deep_merge(
|
15
|
+
Babl::Utils::Hash.deep_merge(nodes.map(&:pinned_dependencies))
|
16
16
|
end
|
17
17
|
|
18
18
|
memoize def schema
|
@@ -44,6 +44,7 @@ module Babl
|
|
44
44
|
|
45
45
|
def optimize_single
|
46
46
|
return unless nodes.size == 1
|
47
|
+
|
47
48
|
optimized = nodes.first.optimize
|
48
49
|
|
49
50
|
case optimized
|
@@ -59,11 +60,13 @@ module Babl
|
|
59
60
|
def optimize_merged_objects
|
60
61
|
optimized_nodes = nodes.map(&:optimize)
|
61
62
|
return if optimized_nodes == nodes
|
63
|
+
|
62
64
|
Merge.new(optimized_nodes).optimize
|
63
65
|
end
|
64
66
|
|
65
67
|
def optimize_nested_merges
|
66
68
|
return unless nodes.any? { |node| Merge === node }
|
69
|
+
|
67
70
|
Merge.new(nodes.flat_map { |node| Merge === node ? node.nodes : [node] }).optimize
|
68
71
|
end
|
69
72
|
|
@@ -73,6 +76,7 @@ module Babl
|
|
73
76
|
obj2 = constant_to_object(obj2) if Constant === obj2
|
74
77
|
|
75
78
|
next unless Object === obj1 && Object === obj2
|
79
|
+
|
76
80
|
new_nodes = nodes.dup
|
77
81
|
new_nodes[idx] = Object.new(obj1.nodes.merge(obj2.nodes))
|
78
82
|
new_nodes[idx + 1] = nil
|
data/lib/babl/nodes/nav.rb
CHANGED
@@ -10,10 +10,10 @@ module Babl
|
|
10
10
|
node_deps = node.dependencies
|
11
11
|
child_deps = node_deps.reject { |key, _| key == Parent::PARENT_MARKER }
|
12
12
|
|
13
|
-
Babl::Utils::Hash.deep_merge(
|
13
|
+
Babl::Utils::Hash.deep_merge([
|
14
14
|
node_deps[Parent::PARENT_MARKER] || Utils::Hash::EMPTY,
|
15
15
|
property => child_deps
|
16
|
-
)
|
16
|
+
])
|
17
17
|
end
|
18
18
|
|
19
19
|
memoize def schema
|
@@ -29,16 +29,17 @@ module Babl
|
|
29
29
|
return optimized if Constant === optimized || GotoPin === optimized
|
30
30
|
return optimized.node if Parent === optimized
|
31
31
|
return self if optimized.equal?(node)
|
32
|
+
|
32
33
|
Nav.new(property, optimized)
|
33
34
|
end
|
34
35
|
|
35
36
|
def render(frame)
|
36
37
|
value = begin
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
38
|
+
object = frame.object
|
39
|
+
::Hash === object ? object.fetch(property) : object.send(property)
|
40
|
+
rescue StandardError => e
|
41
|
+
raise Errors::RenderingError, "#{e.message}\n" + frame.formatted_stack(property), e.backtrace
|
42
|
+
end
|
42
43
|
|
43
44
|
frame.move_forward(value, property) do |new_frame|
|
44
45
|
node.render(new_frame)
|
data/lib/babl/nodes/object.rb
CHANGED
@@ -9,11 +9,11 @@ module Babl
|
|
9
9
|
EMPTY = new(Utils::Hash::EMPTY)
|
10
10
|
|
11
11
|
memoize def dependencies
|
12
|
-
Babl::Utils::Hash.deep_merge(
|
12
|
+
Babl::Utils::Hash.deep_merge(nodes.values.map(&:dependencies))
|
13
13
|
end
|
14
14
|
|
15
15
|
memoize def pinned_dependencies
|
16
|
-
Babl::Utils::Hash.deep_merge(
|
16
|
+
Babl::Utils::Hash.deep_merge(nodes.values.map(&:pinned_dependencies))
|
17
17
|
end
|
18
18
|
|
19
19
|
memoize def schema
|
@@ -27,6 +27,7 @@ module Babl
|
|
27
27
|
object ||= Object.new(optimized_nodes)
|
28
28
|
|
29
29
|
return object unless optimized_nodes.values.all? { |node| Constant === node }
|
30
|
+
|
30
31
|
Constant.new(optimized_nodes.map { |k, v| [k, v.value] }.to_h.freeze, object.schema)
|
31
32
|
end
|
32
33
|
|
data/lib/babl/nodes/parent.rb
CHANGED
data/lib/babl/nodes/switch.rb
CHANGED
@@ -10,15 +10,16 @@ module Babl
|
|
10
10
|
class Switch < Utils::Value.new(:nodes)
|
11
11
|
def initialize(nodes)
|
12
12
|
raise Errors::InvalidTemplate, 'A least one switch() condition must be taken' if nodes.empty?
|
13
|
+
|
13
14
|
super
|
14
15
|
end
|
15
16
|
|
16
17
|
memoize def dependencies
|
17
|
-
Babl::Utils::Hash.deep_merge(
|
18
|
+
Babl::Utils::Hash.deep_merge(nodes.flatten(1).map(&:dependencies))
|
18
19
|
end
|
19
20
|
|
20
21
|
memoize def pinned_dependencies
|
21
|
-
Babl::Utils::Hash.deep_merge(
|
22
|
+
Babl::Utils::Hash.deep_merge(nodes.flatten(1).map(&:pinned_dependencies))
|
22
23
|
end
|
23
24
|
|
24
25
|
memoize def schema
|
@@ -45,6 +46,7 @@ module Babl
|
|
45
46
|
def optimize_continue_to_switch
|
46
47
|
cond, val = nodes.last
|
47
48
|
return unless Switch === val && Constant === cond && cond.value
|
49
|
+
|
48
50
|
Switch.new(nodes[0...-1] + val.nodes).optimize
|
49
51
|
end
|
50
52
|
|
@@ -52,6 +54,7 @@ module Babl
|
|
52
54
|
conds = Set.new
|
53
55
|
new_nodes = nodes.map { |cond, val|
|
54
56
|
next if conds.include?(cond)
|
57
|
+
|
55
58
|
conds << cond
|
56
59
|
[cond, val]
|
57
60
|
}.compact
|
@@ -61,6 +64,7 @@ module Babl
|
|
61
64
|
def optimize_always_same_outputs
|
62
65
|
return unless nodes.map(&:first).any? { |node| Constant === node && node.value }
|
63
66
|
return unless nodes.map(&:last).uniq.size == 1
|
67
|
+
|
64
68
|
nodes.first.last.optimize
|
65
69
|
end
|
66
70
|
|
data/lib/babl/nodes/with.rb
CHANGED
@@ -10,14 +10,14 @@ module Babl
|
|
10
10
|
end
|
11
11
|
|
12
12
|
memoize def dependencies
|
13
|
-
Babl::Utils::Hash.deep_merge(
|
13
|
+
Babl::Utils::Hash.deep_merge([
|
14
14
|
*nodes.map(&:dependencies),
|
15
15
|
node.dependencies[Parent::PARENT_MARKER] || Utils::Hash::EMPTY
|
16
|
-
)
|
16
|
+
])
|
17
17
|
end
|
18
18
|
|
19
19
|
memoize def pinned_dependencies
|
20
|
-
Babl::Utils::Hash.deep_merge(
|
20
|
+
Babl::Utils::Hash.deep_merge((nodes + [node]).map(&:pinned_dependencies))
|
21
21
|
end
|
22
22
|
|
23
23
|
memoize def optimize
|
@@ -30,15 +30,16 @@ module Babl
|
|
30
30
|
|
31
31
|
if optimized_nodes.all? { |n| Constant === n }
|
32
32
|
value = begin
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
block.call(*optimized_nodes.map(&:value))
|
34
|
+
rescue StandardError => e
|
35
|
+
raise Errors::InvalidTemplate, e.message, e.backtrace
|
36
|
+
end
|
37
37
|
constant_block = Utils::Proc.constant(value)
|
38
38
|
return With.new(optimized, Utils::Array::EMPTY, constant_block)
|
39
39
|
end
|
40
40
|
|
41
41
|
return self if optimized.equal?(node) && optimized_nodes.each_with_index.all? { |n, idx| n.equal?(nodes[idx]) }
|
42
|
+
|
42
43
|
With.new(optimized, optimized_nodes, block)
|
43
44
|
end
|
44
45
|
|
data/lib/babl/operators/array.rb
CHANGED
@@ -7,11 +7,11 @@ module Babl
|
|
7
7
|
module DSL
|
8
8
|
# Produce an fixed-size array, using the provided templates to populate its elements.
|
9
9
|
def array(*templates)
|
10
|
-
templates = templates.map { |t| unscoped.call(t) }
|
10
|
+
templates = templates.map { |t| unscoped.reset_continue.call(t) }
|
11
11
|
|
12
12
|
construct_terminal { |ctx|
|
13
13
|
Nodes::FixedArray.new(templates.map { |t|
|
14
|
-
t.builder.precompile(Nodes::TerminalValue.instance, ctx
|
14
|
+
t.builder.precompile(Nodes::TerminalValue.instance, ctx)
|
15
15
|
})
|
16
16
|
}
|
17
17
|
end
|
data/lib/babl/operators/call.rb
CHANGED
@@ -24,11 +24,10 @@ module Babl
|
|
24
24
|
case arg
|
25
25
|
when Template then self.class.new(builder.wrap { |bound| arg.builder.bind(bound) })
|
26
26
|
when Utils::DslProxy then call(arg.itself)
|
27
|
-
when ::Symbol then nav(arg)
|
28
27
|
when ::Proc then call(&arg)
|
29
28
|
when ::Hash then object(arg)
|
30
29
|
when ::Array then array(*arg)
|
31
|
-
when ::String, ::Numeric, ::NilClass, ::TrueClass, ::FalseClass then static(arg)
|
30
|
+
when ::String, ::Numeric, ::NilClass, ::TrueClass, ::FalseClass, ::Symbol then static(arg)
|
32
31
|
else raise Errors::InvalidTemplate, "call() received invalid argument: #{arg}"
|
33
32
|
end
|
34
33
|
end
|
@@ -8,14 +8,14 @@ module Babl
|
|
8
8
|
# Produce an array by concatening the provided result of the given templates.
|
9
9
|
# (they therefor have to produce arrays, or nil, which is interpreted as an empty array)
|
10
10
|
def concat(*templates)
|
11
|
-
templates = templates.map { |t| unscoped.call(t) }
|
11
|
+
templates = templates.map { |t| unscoped.reset_continue.call(t) }
|
12
12
|
|
13
13
|
construct_terminal { |context|
|
14
14
|
Nodes::Concat.new(
|
15
15
|
templates.map { |t|
|
16
16
|
t.builder.precompile(
|
17
17
|
Nodes::TerminalValue.instance,
|
18
|
-
context
|
18
|
+
context
|
19
19
|
)
|
20
20
|
}
|
21
21
|
)
|
@@ -11,9 +11,21 @@ module Babl
|
|
11
11
|
construct_terminal { |context|
|
12
12
|
node = context[:continue]
|
13
13
|
raise Errors::InvalidTemplate, 'continue() cannot be used outside switch()' unless node
|
14
|
+
|
14
15
|
node
|
15
16
|
}
|
16
17
|
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
# Clear contextual information about parent switch for the rest of the chain
|
22
|
+
def reset_continue
|
23
|
+
construct_context { |context|
|
24
|
+
next context unless context.key?(:continue)
|
25
|
+
|
26
|
+
context.reject { |k, _v| :continue == k }
|
27
|
+
}
|
28
|
+
end
|
17
29
|
end
|
18
30
|
end
|
19
31
|
end
|
data/lib/babl/operators/dep.rb
CHANGED
data/lib/babl/operators/each.rb
CHANGED
@@ -8,7 +8,7 @@ module Babl
|
|
8
8
|
# Construct a JSON array by iterating over the current collection,
|
9
9
|
# using the chained template for rendering each element.
|
10
10
|
def each
|
11
|
-
construct_node
|
11
|
+
construct_node { |node| Nodes::Each.new(node) }.reset_key.reset_continue
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
data/lib/babl/operators/enter.rb
CHANGED
@@ -5,21 +5,54 @@ require 'babl/nodes'
|
|
5
5
|
module Babl
|
6
6
|
module Operators
|
7
7
|
module Enter
|
8
|
+
KEY_QUESTIONIFIER = proc { |context|
|
9
|
+
key = context[:key]
|
10
|
+
|
11
|
+
new_key =
|
12
|
+
case key
|
13
|
+
when ::String then "#{key}?"
|
14
|
+
when ::Symbol then :"#{key}?"
|
15
|
+
else raise Errors::InvalidTemplate, "Key is expected to key a string or a symbol: #{key}"
|
16
|
+
end
|
17
|
+
|
18
|
+
context.merge(key: new_key)
|
19
|
+
}
|
20
|
+
|
8
21
|
module DSL
|
9
|
-
# Navigate to a
|
10
|
-
# is inferred based on the object()
|
22
|
+
# Navigate to a property whose name is inferred based on parent object()'s key
|
11
23
|
def enter
|
12
|
-
construct_node
|
13
|
-
key
|
14
|
-
|
15
|
-
Nodes::Nav.new(key, node)
|
16
|
-
}
|
24
|
+
construct_node { |node, context|
|
25
|
+
raise Errors::InvalidTemplate, 'No key to enter into' unless context.key?(:key)
|
26
|
+
|
27
|
+
Nodes::Nav.new(context[:key], node)
|
28
|
+
}.reset_key.reset_continue
|
29
|
+
end
|
30
|
+
|
31
|
+
# Navigate to a property whose name is inferred based on parent object()'s key + '?'
|
32
|
+
def enter?
|
33
|
+
construct_context(&KEY_QUESTIONIFIER).enter
|
17
34
|
end
|
18
35
|
|
19
36
|
# Simple convenience alias
|
20
37
|
def _
|
21
38
|
enter
|
22
39
|
end
|
40
|
+
|
41
|
+
# Simple convenience alias
|
42
|
+
def _?
|
43
|
+
enter?
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
# Clear contextual information about current property name for the rest of the chain
|
49
|
+
def reset_key
|
50
|
+
construct_context { |context|
|
51
|
+
next context unless context.key?(:key)
|
52
|
+
|
53
|
+
context.reject { |k, _v| :key == k }
|
54
|
+
}
|
55
|
+
end
|
23
56
|
end
|
24
57
|
end
|
25
58
|
end
|
data/lib/babl/operators/merge.rb
CHANGED
@@ -9,15 +9,13 @@ module Babl
|
|
9
9
|
# Merge multiple JSON objects (non-deep)
|
10
10
|
def merge(*templates)
|
11
11
|
return call(Utils::Hash::EMPTY) if templates.empty?
|
12
|
-
|
12
|
+
|
13
|
+
templates = templates.map { |t| unscoped.reset_continue.call(t) }
|
13
14
|
|
14
15
|
construct_terminal { |context|
|
15
16
|
Nodes::Merge.new(
|
16
17
|
templates.map { |t|
|
17
|
-
t.builder.precompile(
|
18
|
-
Nodes::TerminalValue.instance,
|
19
|
-
context.merge(continue: nil)
|
20
|
-
)
|
18
|
+
t.builder.precompile(Nodes::TerminalValue.instance, context)
|
21
19
|
}
|
22
20
|
)
|
23
21
|
}
|
data/lib/babl/operators/nav.rb
CHANGED
@@ -12,10 +12,11 @@ module Babl
|
|
12
12
|
# is disabled for the rest of the chain.
|
13
13
|
def nav(*path, &block)
|
14
14
|
if path.empty?
|
15
|
-
return (block ? with(unscoped, &block) :
|
15
|
+
return (block ? with(unscoped, &block) : reset_key.reset_continue)
|
16
16
|
end
|
17
|
+
|
17
18
|
property = path.first.dup.freeze
|
18
|
-
construct_node
|
19
|
+
construct_node { |node| Nodes::Nav.new(property, node) }
|
19
20
|
.nav(*path[1..-1], &block)
|
20
21
|
end
|
21
22
|
end
|
@@ -18,7 +18,7 @@ module Babl
|
|
18
18
|
templates = args
|
19
19
|
.map { |name| [name.to_sym, unscoped.nav(name)] }.to_h
|
20
20
|
.merge(kwargs)
|
21
|
-
.map { |k, v| [k, unscoped.call(v)] }
|
21
|
+
.map { |k, v| [k, unscoped.reset_continue.call(v)] }
|
22
22
|
|
23
23
|
construct_terminal { |ctx|
|
24
24
|
Nodes::Object.new(templates.map { |key, template|
|
@@ -26,7 +26,7 @@ module Babl
|
|
26
26
|
key.to_sym,
|
27
27
|
template.builder.precompile(
|
28
28
|
Nodes::TerminalValue.instance,
|
29
|
-
ctx.merge(key: key
|
29
|
+
ctx.merge(key: key)
|
30
30
|
)
|
31
31
|
]
|
32
32
|
}.to_h)
|
@@ -7,7 +7,7 @@ module Babl
|
|
7
7
|
module DSL
|
8
8
|
# Navigate to the parent of the current object.
|
9
9
|
def parent
|
10
|
-
construct_node
|
10
|
+
construct_node { |node| Nodes::Parent.new(node) }.reset_key.reset_continue
|
11
11
|
end
|
12
12
|
|
13
13
|
protected
|
@@ -16,6 +16,7 @@ module Babl
|
|
16
16
|
if tree.dependencies.key? Nodes::Parent::PARENT_MARKER
|
17
17
|
raise Errors::InvalidTemplate, 'Out of context parent dependency'
|
18
18
|
end
|
19
|
+
|
19
20
|
super
|
20
21
|
end
|
21
22
|
end
|
@@ -12,8 +12,10 @@ module Babl
|
|
12
12
|
construct_terminal { |ctx|
|
13
13
|
lookup_context = ctx[:lookup_context]
|
14
14
|
raise Errors::InvalidTemplate, 'Cannot use partial without lookup context' unless lookup_context
|
15
|
+
|
15
16
|
template, new_lookup_context = lookup_context.find(current_template, partial_name)
|
16
17
|
raise Errors::InvalidTemplate, "Cannot find partial '#{partial_name}'" unless template
|
18
|
+
|
17
19
|
template.precompile(Nodes::TerminalValue.instance, lookup_context: new_lookup_context)
|
18
20
|
}
|
19
21
|
end
|
data/lib/babl/operators/pin.rb
CHANGED
@@ -15,12 +15,12 @@ module Babl
|
|
15
15
|
|
16
16
|
def named_pin(ref)
|
17
17
|
check_pin_ref(ref)
|
18
|
-
construct_node
|
18
|
+
construct_node { |node| Nodes::CreatePin.new(node, ref) }.reset_continue
|
19
19
|
end
|
20
20
|
|
21
21
|
def goto_pin(ref)
|
22
22
|
check_pin_ref(ref)
|
23
|
-
construct_node
|
23
|
+
construct_node { |node| Nodes::GotoPin.new(node, ref) }.reset_key.reset_continue
|
24
24
|
end
|
25
25
|
|
26
26
|
protected
|
@@ -28,6 +28,7 @@ module Babl
|
|
28
28
|
def validate(tree)
|
29
29
|
name = tree.pinned_dependencies.keys.first
|
30
30
|
raise Errors::InvalidTemplate, "Unresolved pin: #{name}" if name
|
31
|
+
|
31
32
|
super
|
32
33
|
end
|
33
34
|
|
@@ -7,6 +7,7 @@ module Babl
|
|
7
7
|
# Parse BABL source into a Template
|
8
8
|
def source(*args, &block)
|
9
9
|
raise Errors::InvalidTemplate, 'source() expects a block xor a string' unless args.empty? ^ block.nil?
|
10
|
+
|
10
11
|
block ||= proc { instance_eval(*args) }
|
11
12
|
call Utils::DslProxy.eval(unscoped, &block)
|
12
13
|
end
|
@@ -8,13 +8,13 @@ module Babl
|
|
8
8
|
module DSL
|
9
9
|
# Conditional switching between different templates
|
10
10
|
def switch(conds = Utils::Hash::EMPTY)
|
11
|
-
conds = conds.map { |cond, value| [unscoped.call(cond), unscoped.call(value)] }
|
11
|
+
conds = conds.map { |cond, value| [unscoped.reset_continue.call(cond), unscoped.call(value)] }
|
12
12
|
|
13
|
-
construct_node
|
13
|
+
construct_node { |node, context|
|
14
14
|
nodes = conds.map { |cond, value|
|
15
15
|
cond_node = cond.builder.precompile(
|
16
16
|
Nodes::InternalValue.instance,
|
17
|
-
context
|
17
|
+
context
|
18
18
|
)
|
19
19
|
|
20
20
|
value_node = value.builder.precompile(
|
@@ -26,7 +26,7 @@ module Babl
|
|
26
26
|
}
|
27
27
|
|
28
28
|
Nodes::Switch.new(nodes)
|
29
|
-
}
|
29
|
+
}.reset_continue
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
data/lib/babl/operators/with.rb
CHANGED
@@ -7,16 +7,16 @@ module Babl
|
|
7
7
|
module DSL
|
8
8
|
# Produce a value by calling the block, passing it the output value of the templates passed as argument.
|
9
9
|
def with(*templates, &block)
|
10
|
-
templates = templates.map { |t| unscoped.call(t) }
|
10
|
+
templates = templates.map { |t| unscoped.reset_continue.call(t) }
|
11
11
|
|
12
|
-
construct_node
|
12
|
+
construct_node { |node, context|
|
13
13
|
Nodes::With.new(node, templates.map { |t|
|
14
14
|
t.builder.precompile(
|
15
15
|
Nodes::InternalValue.instance,
|
16
|
-
context
|
16
|
+
context
|
17
17
|
)
|
18
18
|
}, block)
|
19
|
-
|
19
|
+
}.reset_key.reset_continue
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
data/lib/babl/schema/any_of.rb
CHANGED
@@ -67,6 +67,7 @@ module Babl
|
|
67
67
|
|
68
68
|
def simplify_integer_is_number
|
69
69
|
return unless choice_set.include?(Typed::INTEGER) && choice_set.include?(Typed::NUMBER)
|
70
|
+
|
70
71
|
AnyOf.canonicalized(choice_set - [Typed::INTEGER])
|
71
72
|
end
|
72
73
|
|
@@ -103,6 +104,7 @@ module Babl
|
|
103
104
|
# AnyOf[true, false] is just boolean
|
104
105
|
def simplify_boolean
|
105
106
|
return unless choice_set.include?(Primitive::TRUE) && choice_set.include?(Primitive::FALSE)
|
107
|
+
|
106
108
|
AnyOf.canonicalized(choice_set - [Primitive::TRUE, Primitive::FALSE] + [Typed::BOOLEAN])
|
107
109
|
end
|
108
110
|
|
@@ -113,10 +115,12 @@ module Babl
|
|
113
115
|
def simplify_typed_and_static
|
114
116
|
choice_set.each do |typed|
|
115
117
|
next unless Typed === typed
|
118
|
+
|
116
119
|
instances = choice_set.select { |instance|
|
117
120
|
Primitive === instance && typed.classes.any? { |clazz| clazz === instance.value }
|
118
121
|
}
|
119
122
|
next if instances.empty?
|
123
|
+
|
120
124
|
return AnyOf.canonicalized(choice_set - instances)
|
121
125
|
end
|
122
126
|
nil
|
@@ -126,8 +130,10 @@ module Babl
|
|
126
130
|
# We can get rid of the former and only keep the DynArray
|
127
131
|
def simplify_empty_array
|
128
132
|
return unless choice_set.include?(FixedArray::EMPTY)
|
133
|
+
|
129
134
|
choice_set.each do |other|
|
130
135
|
next unless DynArray === other
|
136
|
+
|
131
137
|
new_other = DynArray.new(other.item)
|
132
138
|
return AnyOf.canonicalized(choice_set - [other, FixedArray::EMPTY] + [new_other])
|
133
139
|
end
|
@@ -142,6 +148,7 @@ module Babl
|
|
142
148
|
|
143
149
|
choice_set.each do |dyn|
|
144
150
|
next unless DynArray === dyn
|
151
|
+
|
145
152
|
fixed_arrays.each do |fixed|
|
146
153
|
new_dyn = DynArray.new(dyn.item)
|
147
154
|
return AnyOf.canonicalized(choice_set - [fixed, dyn] + [new_dyn]) if dyn.item == fixed.items.first
|
@@ -170,8 +177,8 @@ module Babl
|
|
170
177
|
next if obj1props.any? { |name, p1|
|
171
178
|
p2 = obj2props[name]
|
172
179
|
next name if p2 && Primitive === p2.value &&
|
173
|
-
|
174
|
-
|
180
|
+
Primitive === p1.value &&
|
181
|
+
p1.value.value != p2.value.value
|
175
182
|
}
|
176
183
|
|
177
184
|
new_properties = (obj1props.keys + obj2props.keys).uniq.map { |name|
|
@@ -197,9 +204,11 @@ module Babl
|
|
197
204
|
def simplify_push_down_dyn_array
|
198
205
|
choice_set.each_with_index { |arr1, index1|
|
199
206
|
next unless DynArray === arr1
|
207
|
+
|
200
208
|
choice_set.each_with_index { |arr2, index2|
|
201
209
|
break if index2 >= index1
|
202
210
|
next unless DynArray === arr2
|
211
|
+
|
203
212
|
new_arr = DynArray.new(AnyOf.canonicalized([arr1.item, arr2.item]))
|
204
213
|
return AnyOf.canonicalized(choice_set - [arr1, arr2] + [new_arr])
|
205
214
|
}
|
File without changes
|
data/lib/babl/schema/object.rb
CHANGED
@@ -21,10 +21,12 @@ module Babl
|
|
21
21
|
def json
|
22
22
|
{ type: 'object' }.tap { |out|
|
23
23
|
next if property_set.empty?
|
24
|
+
|
24
25
|
out[:properties] = property_set.map { |property| [property.name, property.value.json] }.to_h
|
25
26
|
out[:additionalProperties] = additional
|
26
27
|
required_properties = property_set.select(&:required)
|
27
28
|
next if required_properties.empty?
|
29
|
+
|
28
30
|
out[:required] = property_set.select(&:required).map(&:name).map(&:to_s)
|
29
31
|
}
|
30
32
|
end
|
data/lib/babl/utils/dsl_proxy.rb
CHANGED
@@ -11,7 +11,7 @@ module Babl
|
|
11
11
|
NON_PROXIED_METHODS = Set[
|
12
12
|
:__send__, :send, :object_id, :__id__, :equal?, :instance_eval, :instance_exec,
|
13
13
|
:respond_to?, :method, :freeze
|
14
|
-
]
|
14
|
+
].freeze
|
15
15
|
|
16
16
|
instance_methods.each do |method|
|
17
17
|
undef_method(method) unless NON_PROXIED_METHODS.include?(method)
|
data/lib/babl/utils/hash.rb
CHANGED
@@ -5,10 +5,11 @@ module Babl
|
|
5
5
|
EMPTY = {}.freeze
|
6
6
|
|
7
7
|
class << self
|
8
|
-
def deep_merge(
|
8
|
+
def deep_merge(hashes)
|
9
9
|
filtered_hashes = hashes.reject(&:empty?)
|
10
10
|
return EMPTY if filtered_hashes.empty?
|
11
11
|
return filtered_hashes.first if filtered_hashes.size == 1
|
12
|
+
|
12
13
|
filtered_hashes.reduce({}) { |out, hash| deep_merge_inplace(out, hash) }
|
13
14
|
end
|
14
15
|
|
data/lib/babl/utils/value.rb
CHANGED
data/lib/babl/version.rb
CHANGED
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.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Frederic Terrazzoni
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-10-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coveralls
|
@@ -187,6 +187,7 @@ files:
|
|
187
187
|
- lib/babl/schema/anything.rb
|
188
188
|
- lib/babl/schema/dyn_array.rb
|
189
189
|
- lib/babl/schema/fixed_array.rb
|
190
|
+
- lib/babl/schema/json_schema.rb
|
190
191
|
- lib/babl/schema/object.rb
|
191
192
|
- lib/babl/schema/primitive.rb
|
192
193
|
- lib/babl/schema/typed.rb
|