babl-json 0.1.4 → 0.2.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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +6 -0
  4. data/README.md +1 -3
  5. data/babl.gemspec +3 -1
  6. data/lib/babl.rb +4 -6
  7. data/lib/babl/builder/chain_builder.rb +6 -2
  8. data/lib/babl/builder/template_base.rb +16 -3
  9. data/lib/babl/errors.rb +7 -0
  10. data/lib/babl/nodes/create_pin.rb +24 -0
  11. data/lib/babl/nodes/dep.rb +38 -0
  12. data/lib/babl/nodes/each.rb +29 -0
  13. data/lib/babl/nodes/fixed_array.rb +25 -0
  14. data/lib/babl/nodes/goto_pin.rb +24 -0
  15. data/lib/babl/{rendering/internal_value_node.rb → nodes/internal_value.rb} +6 -5
  16. data/lib/babl/nodes/merge.rb +102 -0
  17. data/lib/babl/nodes/nav.rb +33 -0
  18. data/lib/babl/nodes/object.rb +26 -0
  19. data/lib/babl/nodes/parent.rb +64 -0
  20. data/lib/babl/nodes/static.rb +34 -0
  21. data/lib/babl/nodes/switch.rb +29 -0
  22. data/lib/babl/nodes/terminal_value.rb +76 -0
  23. data/lib/babl/nodes/with.rb +28 -0
  24. data/lib/babl/operators/array.rb +5 -28
  25. data/lib/babl/operators/call.rb +4 -2
  26. data/lib/babl/operators/continue.rb +19 -0
  27. data/lib/babl/operators/default.rb +13 -0
  28. data/lib/babl/operators/dep.rb +3 -36
  29. data/lib/babl/operators/each.rb +3 -33
  30. data/lib/babl/operators/enter.rb +4 -2
  31. data/lib/babl/operators/extends.rb +4 -1
  32. data/lib/babl/operators/merge.rb +7 -30
  33. data/lib/babl/operators/nav.rb +4 -36
  34. data/lib/babl/operators/object.rb +7 -29
  35. data/lib/babl/operators/parent.rb +4 -73
  36. data/lib/babl/operators/partial.rb +4 -2
  37. data/lib/babl/operators/pin.rb +14 -58
  38. data/lib/babl/operators/static.rb +11 -30
  39. data/lib/babl/operators/switch.rb +8 -51
  40. data/lib/babl/operators/with.rb +5 -34
  41. data/lib/babl/railtie.rb +2 -2
  42. data/lib/babl/rendering/compiled_template.rb +5 -13
  43. data/lib/babl/rendering/context.rb +13 -7
  44. data/lib/babl/schema/any_of.rb +137 -0
  45. data/lib/babl/schema/anything.rb +13 -0
  46. data/lib/babl/schema/dyn_array.rb +11 -0
  47. data/lib/babl/schema/fixed_array.rb +13 -0
  48. data/lib/babl/schema/object.rb +35 -0
  49. data/lib/babl/schema/static.rb +14 -0
  50. data/lib/babl/schema/typed.rb +0 -0
  51. data/lib/babl/template.rb +4 -9
  52. data/lib/babl/utils/ref.rb +6 -0
  53. data/lib/babl/version.rb +1 -1
  54. data/spec/operators/array_spec.rb +31 -7
  55. data/spec/operators/call_spec.rb +16 -14
  56. data/spec/operators/continue_spec.rb +25 -0
  57. data/spec/operators/default_spec.rb +15 -0
  58. data/spec/operators/dep_spec.rb +4 -8
  59. data/spec/operators/each_spec.rb +24 -5
  60. data/spec/operators/enter_spec.rb +9 -7
  61. data/spec/operators/extends_spec.rb +19 -5
  62. data/spec/operators/merge_spec.rb +105 -12
  63. data/spec/operators/nav_spec.rb +22 -10
  64. data/spec/operators/null_spec.rb +5 -4
  65. data/spec/operators/nullable_spec.rb +13 -13
  66. data/spec/operators/object_spec.rb +17 -6
  67. data/spec/operators/parent_spec.rb +18 -22
  68. data/spec/operators/partial_spec.rb +8 -6
  69. data/spec/operators/pin_spec.rb +100 -61
  70. data/spec/operators/source_spec.rb +10 -6
  71. data/spec/operators/static_spec.rb +17 -9
  72. data/spec/operators/switch_spec.rb +85 -45
  73. data/spec/operators/with_spec.rb +13 -15
  74. data/spec/spec_helper.rb +2 -31
  75. data/spec/spec_helper/operator_testing.rb +46 -0
  76. data/spec/spec_helper/schema_utils.rb +33 -0
  77. metadata +63 -4
  78. data/lib/babl/rendering/terminal_value_node.rb +0 -54
@@ -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::NavNode.new(key, node)
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 { merge(partial(partial_path), *args) }
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
@@ -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
- MergeNode.new(
13
+ Nodes::Merge.new(
9
14
  templates.map { |t|
10
15
  unscoped.call(t).builder.precompile(
11
- Rendering::TerminalValueNode.instance,
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
@@ -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| NavNode.new(path.first, node) }.nav(*path[1..-1], &block)
16
- end
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 ::Babl::InvalidTemplateError, "Duplicate key in object(): #{keys.first}" if keys.size > 1
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
- Rendering::TerminalValueNode.instance,
21
+ Nodes::TerminalValue.instance,
18
22
  ctx.merge(key: k, continue: nil)
19
23
  )]
20
24
  }
21
25
  .to_h
22
26
 
23
- ObjectNode.new(nodes)
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| ParentNode.new(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
- ParentResolverNode.new(super)
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)
@@ -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 = ::Object.new
9
- referenced_scope = unscoped.construct_node(key: nil, continue: nil) { |node| GotoPinNode.new(node, ref) }
10
- construct_node(continue: nil) { |node| CreatePinNode.new(node, ref) }.call(block[referenced_scope])
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 Babl::InvalidTemplateError, 'Unresolved pin' unless node.pinned_dependencies.empty?
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(value)
7
- construct_terminal { StaticNode.new(value) }
8
- end
9
- end
10
-
11
- class StaticNode
12
- def initialize(value)
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