babl-json 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/CHANGELOG.md +6 -0
- data/README.md +1 -3
- data/babl.gemspec +3 -1
- data/lib/babl.rb +4 -6
- data/lib/babl/builder/chain_builder.rb +6 -2
- data/lib/babl/builder/template_base.rb +16 -3
- data/lib/babl/errors.rb +7 -0
- data/lib/babl/nodes/create_pin.rb +24 -0
- data/lib/babl/nodes/dep.rb +38 -0
- data/lib/babl/nodes/each.rb +29 -0
- data/lib/babl/nodes/fixed_array.rb +25 -0
- data/lib/babl/nodes/goto_pin.rb +24 -0
- data/lib/babl/{rendering/internal_value_node.rb → nodes/internal_value.rb} +6 -5
- data/lib/babl/nodes/merge.rb +102 -0
- data/lib/babl/nodes/nav.rb +33 -0
- data/lib/babl/nodes/object.rb +26 -0
- data/lib/babl/nodes/parent.rb +64 -0
- data/lib/babl/nodes/static.rb +34 -0
- data/lib/babl/nodes/switch.rb +29 -0
- data/lib/babl/nodes/terminal_value.rb +76 -0
- data/lib/babl/nodes/with.rb +28 -0
- data/lib/babl/operators/array.rb +5 -28
- data/lib/babl/operators/call.rb +4 -2
- data/lib/babl/operators/continue.rb +19 -0
- data/lib/babl/operators/default.rb +13 -0
- data/lib/babl/operators/dep.rb +3 -36
- data/lib/babl/operators/each.rb +3 -33
- data/lib/babl/operators/enter.rb +4 -2
- data/lib/babl/operators/extends.rb +4 -1
- data/lib/babl/operators/merge.rb +7 -30
- data/lib/babl/operators/nav.rb +4 -36
- data/lib/babl/operators/object.rb +7 -29
- data/lib/babl/operators/parent.rb +4 -73
- data/lib/babl/operators/partial.rb +4 -2
- data/lib/babl/operators/pin.rb +14 -58
- data/lib/babl/operators/static.rb +11 -30
- data/lib/babl/operators/switch.rb +8 -51
- data/lib/babl/operators/with.rb +5 -34
- data/lib/babl/railtie.rb +2 -2
- data/lib/babl/rendering/compiled_template.rb +5 -13
- data/lib/babl/rendering/context.rb +13 -7
- data/lib/babl/schema/any_of.rb +137 -0
- data/lib/babl/schema/anything.rb +13 -0
- data/lib/babl/schema/dyn_array.rb +11 -0
- data/lib/babl/schema/fixed_array.rb +13 -0
- data/lib/babl/schema/object.rb +35 -0
- data/lib/babl/schema/static.rb +14 -0
- data/lib/babl/schema/typed.rb +0 -0
- data/lib/babl/template.rb +4 -9
- data/lib/babl/utils/ref.rb +6 -0
- data/lib/babl/version.rb +1 -1
- data/spec/operators/array_spec.rb +31 -7
- data/spec/operators/call_spec.rb +16 -14
- data/spec/operators/continue_spec.rb +25 -0
- data/spec/operators/default_spec.rb +15 -0
- data/spec/operators/dep_spec.rb +4 -8
- data/spec/operators/each_spec.rb +24 -5
- data/spec/operators/enter_spec.rb +9 -7
- data/spec/operators/extends_spec.rb +19 -5
- data/spec/operators/merge_spec.rb +105 -12
- data/spec/operators/nav_spec.rb +22 -10
- data/spec/operators/null_spec.rb +5 -4
- data/spec/operators/nullable_spec.rb +13 -13
- data/spec/operators/object_spec.rb +17 -6
- data/spec/operators/parent_spec.rb +18 -22
- data/spec/operators/partial_spec.rb +8 -6
- data/spec/operators/pin_spec.rb +100 -61
- data/spec/operators/source_spec.rb +10 -6
- data/spec/operators/static_spec.rb +17 -9
- data/spec/operators/switch_spec.rb +85 -45
- data/spec/operators/with_spec.rb +13 -15
- data/spec/spec_helper.rb +2 -31
- data/spec/spec_helper/operator_testing.rb +46 -0
- data/spec/spec_helper/schema_utils.rb +33 -0
- metadata +63 -4
- data/lib/babl/rendering/terminal_value_node.rb +0 -54
data/lib/babl/operators/enter.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'babl/errors'
|
2
|
+
|
1
3
|
module Babl
|
2
4
|
module Operators
|
3
5
|
module Enter
|
@@ -7,8 +9,8 @@ module Babl
|
|
7
9
|
def enter
|
8
10
|
construct_node(key: nil, continue: nil) { |node, context|
|
9
11
|
key = context[:key]
|
10
|
-
raise InvalidTemplateError, "No key to enter into" unless key
|
11
|
-
Nav
|
12
|
+
raise Errors::InvalidTemplateError, "No key to enter into" unless key
|
13
|
+
Nodes::Nav.new(key, node)
|
12
14
|
}
|
13
15
|
end
|
14
16
|
|
@@ -3,7 +3,10 @@ module Babl
|
|
3
3
|
module Extends
|
4
4
|
module DSL
|
5
5
|
def extends(partial_path, *args)
|
6
|
-
source {
|
6
|
+
source {
|
7
|
+
partial_template = partial(partial_path)
|
8
|
+
args.empty? ? partial_template : merge(partial_template, *args)
|
9
|
+
}
|
7
10
|
end
|
8
11
|
end
|
9
12
|
end
|
data/lib/babl/operators/merge.rb
CHANGED
@@ -1,14 +1,19 @@
|
|
1
|
+
require 'babl/nodes/merge'
|
2
|
+
require 'babl/nodes/terminal_value'
|
3
|
+
|
1
4
|
module Babl
|
2
5
|
module Operators
|
3
6
|
module Merge
|
4
7
|
module DSL
|
5
8
|
# Merge multiple JSON objects (non-deep)
|
6
9
|
def merge(*templates)
|
10
|
+
return call({}) if templates.empty?
|
11
|
+
|
7
12
|
construct_terminal { |context|
|
8
|
-
|
13
|
+
Nodes::Merge.new(
|
9
14
|
templates.map { |t|
|
10
15
|
unscoped.call(t).builder.precompile(
|
11
|
-
|
16
|
+
Nodes::TerminalValue.instance,
|
12
17
|
context.merge(continue: nil)
|
13
18
|
)
|
14
19
|
}
|
@@ -16,34 +21,6 @@ module Babl
|
|
16
21
|
}
|
17
22
|
end
|
18
23
|
end
|
19
|
-
|
20
|
-
class MergeNode
|
21
|
-
def initialize(nodes)
|
22
|
-
@nodes = nodes
|
23
|
-
end
|
24
|
-
|
25
|
-
def dependencies
|
26
|
-
nodes.map(&:dependencies).reduce({}) { |a, b| Babl::Utils::Hash.deep_merge(a, b) }
|
27
|
-
end
|
28
|
-
|
29
|
-
def pinned_dependencies
|
30
|
-
nodes.map(&:pinned_dependencies).reduce({}) { |a, b| Babl::Utils::Hash.deep_merge(a, b) }
|
31
|
-
end
|
32
|
-
|
33
|
-
def documentation
|
34
|
-
nodes.map(&:documentation).each_with_index.map { |doc, idx|
|
35
|
-
[:"Merge #{idx + 1}", doc]
|
36
|
-
}.to_h
|
37
|
-
end
|
38
|
-
|
39
|
-
def render(ctx)
|
40
|
-
nodes.map { |node| node.render(ctx) }.compact.reduce({}, :merge)
|
41
|
-
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
attr_reader :nodes
|
46
|
-
end
|
47
24
|
end
|
48
25
|
end
|
49
26
|
end
|
data/lib/babl/operators/nav.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'babl/nodes/nav'
|
2
|
+
|
1
3
|
module Babl
|
2
4
|
module Operators
|
3
5
|
module Nav
|
@@ -12,42 +14,8 @@ module Babl
|
|
12
14
|
return (block ? with(unscoped, &block) : construct_node(key: nil, continue: nil) { |node| node })
|
13
15
|
end
|
14
16
|
|
15
|
-
construct_node(key: nil, continue: nil) { |node|
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
class NavNode
|
20
|
-
def initialize(through, node)
|
21
|
-
@through = through
|
22
|
-
@node = node
|
23
|
-
end
|
24
|
-
|
25
|
-
def dependencies
|
26
|
-
{ through => node.dependencies }
|
27
|
-
end
|
28
|
-
|
29
|
-
def documentation
|
30
|
-
node.documentation
|
31
|
-
end
|
32
|
-
|
33
|
-
def pinned_dependencies
|
34
|
-
node.pinned_dependencies
|
35
|
-
end
|
36
|
-
|
37
|
-
def render(ctx)
|
38
|
-
node.render(ctx.move_forward_block(through) { navigate(ctx.object) })
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
attr_reader :through, :node
|
44
|
-
|
45
|
-
def navigate(object)
|
46
|
-
if object.is_a?(Hash)
|
47
|
-
object.fetch(through)
|
48
|
-
else
|
49
|
-
object.send(through)
|
50
|
-
end
|
17
|
+
construct_node(key: nil, continue: nil) { |node| Nodes::Nav.new(path.first, node) }
|
18
|
+
.nav(*path[1..-1], &block)
|
51
19
|
end
|
52
20
|
end
|
53
21
|
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'babl/nodes/object'
|
2
|
+
require 'babl/nodes/terminal_value'
|
3
|
+
require 'babl/errors'
|
4
|
+
|
1
5
|
module Babl
|
2
6
|
module Operators
|
3
7
|
module Object
|
@@ -5,7 +9,7 @@ module Babl
|
|
5
9
|
# Create a JSON object node with static structure
|
6
10
|
def object(*attrs, **nested)
|
7
11
|
(attrs.map(&:to_sym) + nested.keys).group_by(&:itself).values.each do |keys|
|
8
|
-
raise ::
|
12
|
+
raise Errors::InvalidTemplateError, "Duplicate key in object(): #{keys.first}" if keys.size > 1
|
9
13
|
end
|
10
14
|
|
11
15
|
construct_terminal { |ctx|
|
@@ -14,42 +18,16 @@ module Babl
|
|
14
18
|
.merge(nested)
|
15
19
|
.map { |k, v|
|
16
20
|
[k, unscoped.call(v).builder.precompile(
|
17
|
-
|
21
|
+
Nodes::TerminalValue.instance,
|
18
22
|
ctx.merge(key: k, continue: nil)
|
19
23
|
)]
|
20
24
|
}
|
21
25
|
.to_h
|
22
26
|
|
23
|
-
|
27
|
+
Nodes::Object.new(nodes)
|
24
28
|
}
|
25
29
|
end
|
26
30
|
end
|
27
|
-
|
28
|
-
class ObjectNode
|
29
|
-
def initialize(nodes)
|
30
|
-
@nodes = nodes
|
31
|
-
end
|
32
|
-
|
33
|
-
def dependencies
|
34
|
-
nodes.values.map(&:dependencies).reduce({}) { |a, b| Babl::Utils::Hash.deep_merge(a, b) }
|
35
|
-
end
|
36
|
-
|
37
|
-
def pinned_dependencies
|
38
|
-
nodes.values.map(&:pinned_dependencies).reduce({}) { |a, b| Babl::Utils::Hash.deep_merge(a, b) }
|
39
|
-
end
|
40
|
-
|
41
|
-
def documentation
|
42
|
-
nodes.map { |k, v| [k, v.documentation] }.to_h
|
43
|
-
end
|
44
|
-
|
45
|
-
def render(ctx)
|
46
|
-
nodes.map { |k, v| [k, v.render(ctx)] }.to_h
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
attr_reader :nodes
|
52
|
-
end
|
53
31
|
end
|
54
32
|
end
|
55
33
|
end
|
@@ -1,89 +1,20 @@
|
|
1
|
+
require 'babl/nodes/parent'
|
2
|
+
|
1
3
|
module Babl
|
2
4
|
module Operators
|
3
5
|
module Parent
|
4
|
-
PARENT = ::Object.new
|
5
|
-
|
6
6
|
module DSL
|
7
7
|
# Navigate to the parent of the current object.
|
8
8
|
def parent
|
9
|
-
construct_node(key: nil, continue: nil) { |node|
|
9
|
+
construct_node(key: nil, continue: nil) { |node| Nodes::Parent.new(node) }
|
10
10
|
end
|
11
11
|
|
12
12
|
protected
|
13
13
|
|
14
14
|
# Override TemplateBase#precompile to add parent dependencies verification
|
15
15
|
def precompile
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class ParentResolverNode
|
21
|
-
def initialize(node)
|
22
|
-
@node = node
|
23
|
-
end
|
24
|
-
|
25
|
-
def dependencies
|
26
|
-
backpropagate_dependencies(node.dependencies)
|
27
|
-
end
|
28
|
-
|
29
|
-
def documentation
|
30
|
-
node.documentation
|
31
|
-
end
|
32
|
-
|
33
|
-
def pinned_dependencies
|
34
|
-
node.pinned_dependencies
|
35
|
-
end
|
36
|
-
|
37
|
-
def render(ctx)
|
38
|
-
node.render(ctx)
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
attr_reader :node
|
44
|
-
|
45
|
-
def backpropagate_dependencies(deps)
|
46
|
-
raise InvalidTemplateError, 'Out of context parent dependency' if deps.key? PARENT
|
47
|
-
new_deps = backpropagate_dependencies_one_level(deps)
|
48
|
-
deps == new_deps ? new_deps : backpropagate_dependencies(new_deps)
|
16
|
+
Nodes::Parent::Resolver.new(super)
|
49
17
|
end
|
50
|
-
|
51
|
-
def backpropagate_dependencies_one_level(deps)
|
52
|
-
deps.reduce({}) do |out, (k, v)|
|
53
|
-
next out if k == PARENT
|
54
|
-
|
55
|
-
Babl::Utils::Hash.deep_merge(
|
56
|
-
Babl::Utils::Hash.deep_merge(out, k => backpropagate_dependencies_one_level(v)),
|
57
|
-
v[PARENT] || {}
|
58
|
-
)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
class ParentNode
|
64
|
-
def initialize(node)
|
65
|
-
@node = node
|
66
|
-
end
|
67
|
-
|
68
|
-
def documentation
|
69
|
-
node.documentation
|
70
|
-
end
|
71
|
-
|
72
|
-
def pinned_dependencies
|
73
|
-
node.pinned_dependencies
|
74
|
-
end
|
75
|
-
|
76
|
-
def dependencies
|
77
|
-
{ PARENT => node.dependencies }
|
78
|
-
end
|
79
|
-
|
80
|
-
def render(ctx)
|
81
|
-
node.render(ctx.move_backward)
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
attr_reader :node
|
87
18
|
end
|
88
19
|
end
|
89
20
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'babl/errors'
|
2
|
+
|
1
3
|
module Babl
|
2
4
|
module Operators
|
3
5
|
module Partial
|
@@ -5,10 +7,10 @@ module Babl
|
|
5
7
|
# Load a partial template given its name
|
6
8
|
# A 'lookup_context' must be defined
|
7
9
|
def partial(partial_name)
|
8
|
-
raise InvalidTemplateError, "Cannot use partial without lookup context" unless lookup_context
|
10
|
+
raise Errors::InvalidTemplateError, "Cannot use partial without lookup context" unless lookup_context
|
9
11
|
|
10
12
|
path, source, partial_lookup_context = lookup_context.find(partial_name)
|
11
|
-
raise InvalidTemplateError, "Cannot find partial '#{partial_name}'" unless path
|
13
|
+
raise Errors::InvalidTemplateError, "Cannot find partial '#{partial_name}'" unless path
|
12
14
|
|
13
15
|
with_lookup_context(partial_lookup_context)
|
14
16
|
.source(source, path, 0)
|
data/lib/babl/operators/pin.rb
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
require 'babl/nodes/create_pin'
|
2
|
+
require 'babl/nodes/goto_pin'
|
3
|
+
require 'babl/utils/ref'
|
4
|
+
require 'babl/errors'
|
5
|
+
|
1
6
|
module Babl
|
2
7
|
module Operators
|
3
8
|
module Pin
|
@@ -5,9 +10,14 @@ module Babl
|
|
5
10
|
# Create a pin
|
6
11
|
def pin(navigation = nil, &block)
|
7
12
|
return pin { |p| block[p.call(navigation)] } if navigation
|
8
|
-
ref = ::
|
9
|
-
|
10
|
-
construct_node(continue: nil) { |node|
|
13
|
+
ref = Utils::Ref.new
|
14
|
+
|
15
|
+
referenced_scope = unscoped.construct_node(key: nil, continue: nil) { |node|
|
16
|
+
Nodes::GotoPin.new(node, ref)
|
17
|
+
}
|
18
|
+
|
19
|
+
construct_node(continue: nil) { |node| Nodes::CreatePin.new(node, ref) }
|
20
|
+
.call(block[referenced_scope])
|
11
21
|
end
|
12
22
|
|
13
23
|
protected
|
@@ -15,64 +25,10 @@ module Babl
|
|
15
25
|
# Override TemplateBase#precompile to ensure that all pin dependencies are satisfied.
|
16
26
|
def precompile
|
17
27
|
super.tap do |node|
|
18
|
-
raise
|
28
|
+
raise Errors::InvalidTemplateError, 'Unresolved pin' unless node.pinned_dependencies.empty?
|
19
29
|
end
|
20
30
|
end
|
21
31
|
end
|
22
|
-
|
23
|
-
class CreatePinNode
|
24
|
-
def initialize(node, ref)
|
25
|
-
@node = node
|
26
|
-
@ref = ref
|
27
|
-
end
|
28
|
-
|
29
|
-
def render(ctx)
|
30
|
-
node.render(ctx.create_pin(ref))
|
31
|
-
end
|
32
|
-
|
33
|
-
def documentation
|
34
|
-
node.documentation
|
35
|
-
end
|
36
|
-
|
37
|
-
def dependencies
|
38
|
-
Babl::Utils::Hash.deep_merge(node.dependencies, node.pinned_dependencies[ref] || {})
|
39
|
-
end
|
40
|
-
|
41
|
-
def pinned_dependencies
|
42
|
-
node.pinned_dependencies.reject { |k, _v| k == ref }
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
attr_reader :node, :ref
|
48
|
-
end
|
49
|
-
|
50
|
-
class GotoPinNode
|
51
|
-
def initialize(node, ref)
|
52
|
-
@node = node
|
53
|
-
@ref = ref
|
54
|
-
end
|
55
|
-
|
56
|
-
def dependencies
|
57
|
-
{}
|
58
|
-
end
|
59
|
-
|
60
|
-
def pinned_dependencies
|
61
|
-
Babl::Utils::Hash.deep_merge(node.pinned_dependencies, ref => node.dependencies)
|
62
|
-
end
|
63
|
-
|
64
|
-
def documentation
|
65
|
-
node.documentation
|
66
|
-
end
|
67
|
-
|
68
|
-
def render(ctx)
|
69
|
-
node.render(ctx.goto_pin(ref))
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
attr_reader :node, :ref
|
75
|
-
end
|
76
32
|
end
|
77
33
|
end
|
78
34
|
end
|
@@ -1,39 +1,20 @@
|
|
1
|
+
require 'babl/nodes/static'
|
2
|
+
require 'babl/nodes/terminal_value'
|
3
|
+
require 'babl/errors'
|
4
|
+
|
1
5
|
module Babl
|
2
6
|
module Operators
|
3
7
|
module Static
|
4
8
|
module DSL
|
5
9
|
# Create a static JSON value
|
6
|
-
def static(
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
@serialized_value = Rendering::TerminalValueNode.instance.render_object(value)
|
14
|
-
rescue Babl::RenderingError => exception
|
15
|
-
raise Babl::InvalidTemplateError, exception.message
|
16
|
-
end
|
17
|
-
|
18
|
-
def documentation
|
19
|
-
serialized_value
|
20
|
-
end
|
21
|
-
|
22
|
-
def render(_ctx)
|
23
|
-
serialized_value
|
24
|
-
end
|
25
|
-
|
26
|
-
def dependencies
|
27
|
-
{}
|
10
|
+
def static(val)
|
11
|
+
case val
|
12
|
+
when String, Numeric, NilClass, TrueClass, FalseClass then construct_terminal { Nodes::Static.new(val) }
|
13
|
+
else call(Nodes::TerminalValue.instance.render_object(val))
|
14
|
+
end
|
15
|
+
rescue Errors::RenderingError => exception
|
16
|
+
raise Errors::InvalidTemplateError, exception.message
|
28
17
|
end
|
29
|
-
|
30
|
-
def pinned_dependencies
|
31
|
-
{}
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
attr_reader :serialized_value
|
37
18
|
end
|
38
19
|
end
|
39
20
|
end
|