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.
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