babl-json 0.3.4 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 36cb4d94d13851bb8c5a28ab9d8c4ab5a19fa6de
4
- data.tar.gz: '0298c5489089f88c5ce9a171568e418fb8cb0dc6'
3
+ metadata.gz: 03dff31c23ccad4de2970e0e9f1e8c0ac2df3257
4
+ data.tar.gz: 88bfe61b0d2fcfc20ff71a4e009d6ddf15416e00
5
5
  SHA512:
6
- metadata.gz: a0b1e9ce1e461766292ad33f43aa528d61c8fde897a94a7791a8c4b9ffcfb0f53806fbaea2dad7b981c8d2131552b20f22550aaa1eb01888449a2af08cedec50
7
- data.tar.gz: 8ad1d7732551481f3309dd06797fe11faaa3aec11c8bc222972c4789c721b427c37f22fbedd987e139fc37174f87c3933dea272c7bd9054be7ced88dadaf725c
6
+ metadata.gz: c9c04e5150dd7aeda65aefcb989f4a72eb5aed5b5afd0f18edae0495818e45b6fad658c1d693375ff711329c0c036e0c333d0d5ce99349f100c8a2da0b98f07c
7
+ data.tar.gz: 720f05f879639374ab4a26bc7be49b4d32c6fb7c8534621643d09bb2d3a4f402cedc8af994682268760fcdc2418ac72e6cf7e9ffd9bda8dbf14d4d914683cec7
data/lib/babl/nodes.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'babl/nodes/concat'
2
3
  require 'babl/nodes/create_pin'
3
4
  require 'babl/nodes/dep'
4
5
  require 'babl/nodes/each'
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+ require 'babl/errors'
3
+ require 'babl/utils'
4
+ require 'babl/schema'
5
+
6
+ module Babl
7
+ module Nodes
8
+ class Concat < Utils::Value.new(:nodes)
9
+ def dependencies
10
+ nodes.map(&:dependencies).reduce(Utils::Hash::EMPTY) { |a, b| Babl::Utils::Hash.deep_merge(a, b) }
11
+ end
12
+
13
+ def pinned_dependencies
14
+ nodes.map(&:pinned_dependencies).reduce(Utils::Hash::EMPTY) { |a, b| Babl::Utils::Hash.deep_merge(a, b) }
15
+ end
16
+
17
+ def schema
18
+ nodes.map(&:schema).reduce(Schema::FixedArray::EMPTY) { |a, b| merge_doc(a, b) }
19
+ end
20
+
21
+ def render(ctx)
22
+ out = []
23
+ nodes.each { |node|
24
+ values = node.render(ctx)
25
+ case values
26
+ when ::NilClass then nil
27
+ when ::Array then out.concat(values)
28
+ else raise Errors::RenderingError, "Only arrays can be concatenated\n" + ctx.formatted_stack
29
+ end
30
+ }
31
+ out
32
+ end
33
+
34
+ def optimize
35
+ optimize_empty ||
36
+ optimize_single ||
37
+ optimize_concatenated_arrays ||
38
+ optimize_preconcat_constant ||
39
+ self
40
+ end
41
+
42
+ private
43
+
44
+ def optimize_empty
45
+ Constant.new(Utils::Array::EMPTY, Schema::FixedArray::EMPTY) if nodes.empty?
46
+ end
47
+
48
+ def optimize_single
49
+ return unless nodes.size == 1
50
+ optimized = nodes.first.optimize
51
+ case
52
+ when FixedArray === optimized
53
+ optimized
54
+ when Constant === optimized
55
+ optimized.value.nil? ? FixedArray::EMPTY : optimized
56
+ end
57
+ end
58
+
59
+ def optimize_concatenated_arrays
60
+ optimized_nodes = nodes.map(&:optimize)
61
+ return if optimized_nodes == nodes
62
+ Concat.new(optimized_nodes).optimize
63
+ end
64
+
65
+ def optimize_preconcat_constant
66
+ nodes.each_cons(2).each_with_index do |(obj1, obj2), idx|
67
+ obj1 = constant_to_array(obj1) if Constant === obj1
68
+ obj2 = constant_to_array(obj2) if Constant === obj2
69
+ next unless FixedArray === obj1 && FixedArray === obj2
70
+ new_nodes = nodes.dup
71
+ new_nodes[idx] = FixedArray.new(obj1.nodes + obj2.nodes)
72
+ new_nodes[idx + 1] = nil
73
+ return Concat.new(new_nodes.compact).optimize
74
+ end
75
+ nil
76
+ end
77
+
78
+ def constant_to_array(constant)
79
+ case constant.schema
80
+ when Schema::FixedArray
81
+ FixedArray.new(constant.schema.items.each_with_index.map { |item, index|
82
+ Constant.new(constant.value[index], item)
83
+ })
84
+ when Schema::Primitive::NULL
85
+ FixedArray::EMPTY
86
+ end
87
+ end
88
+
89
+ # Merging documentations from concatenated hashes is lossy, because neither JSON-Schema
90
+ # or our internal representation of arrays is able to model that.
91
+ def merge_doc(doc1, doc2)
92
+ doc1 = Schema::FixedArray::EMPTY if doc1 == Schema::Primitive::NULL
93
+ doc2 = Schema::FixedArray::EMPTY if doc2 == Schema::Primitive::NULL
94
+
95
+ case
96
+ when Schema::Anything === doc1 || Schema::Anything === doc2
97
+ Schema::DynArray.new(Schema::Anything.instance)
98
+ when Schema::FixedArray === doc1 && Schema::FixedArray === doc2
99
+ Schema::FixedArray.new(doc1.items + doc2.items)
100
+ when Schema::AnyOf === doc2
101
+ Schema::AnyOf.canonicalized(doc2.choice_set.map { |c| merge_doc(doc1, c) })
102
+ when Schema::AnyOf === doc1
103
+ Schema::AnyOf.canonicalized(doc1.choice_set.map { |c| merge_doc(c, doc2) })
104
+ when Schema::DynArray === doc1 && Schema::FixedArray === doc2
105
+ Schema::DynArray.new(Schema::AnyOf.canonicalized([doc1.item] + doc2.items))
106
+ when Schema::FixedArray === doc1 && Schema::DynArray === doc2
107
+ Schema::DynArray.new(Schema::AnyOf.canonicalized(doc1.items + [doc2.item]))
108
+ when Schema::DynArray === doc1 && Schema::DynArray === doc2
109
+ Schema::DynArray.new(Schema::AnyOf.canonicalized([doc1.item, doc2.item]))
110
+ else raise Errors::InvalidTemplate, 'Only arrays can be concatenated'
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -6,6 +6,8 @@ require 'babl/nodes/constant'
6
6
  module Babl
7
7
  module Nodes
8
8
  class FixedArray < Utils::Value.new(:nodes)
9
+ EMPTY = new(Utils::Array::EMPTY)
10
+
9
11
  def schema
10
12
  Schema::FixedArray.new(nodes.map(&:schema))
11
13
  end
@@ -29,8 +29,8 @@ module Babl
29
29
  def optimize
30
30
  optimize_empty ||
31
31
  optimize_single ||
32
- optimize_merged_objects ||
33
32
  optimize_nested_merges ||
33
+ optimize_merged_objects ||
34
34
  optimize_premergeable_objects ||
35
35
  self
36
36
  end
@@ -45,14 +45,17 @@ module Babl
45
45
  return unless nodes.size == 1
46
46
  optimized = nodes.first.optimize
47
47
  case
48
- when Object === optimized then optimized
49
- when Constant === optimized && optimized.value.nil? then Object::EMPTY
48
+ when Object === optimized
49
+ optimized
50
+ when Constant === optimized
51
+ optimized.value.nil? ? Object::EMPTY : optimized
50
52
  end
51
53
  end
52
54
 
53
55
  def optimize_merged_objects
54
56
  optimized_nodes = nodes.map(&:optimize)
55
- optimized_nodes == nodes ? nil : Merge.new(optimized_nodes).optimize
57
+ return if optimized_nodes == nodes
58
+ Merge.new(optimized_nodes).optimize
56
59
  end
57
60
 
58
61
  def optimize_nested_merges
@@ -80,6 +83,8 @@ module Babl
80
83
  Object.new(constant.schema.property_set.map { |property|
81
84
  [property.name, Constant.new(constant.value[property.name], property.value)]
82
85
  }.to_h)
86
+ when Schema::Primitive::NULL
87
+ Object::EMPTY
83
88
  end
84
89
  end
85
90
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'babl/operators/array'
3
3
  require 'babl/operators/call'
4
+ require 'babl/operators/concat'
4
5
  require 'babl/operators/continue'
5
6
  require 'babl/operators/default'
6
7
  require 'babl/operators/dep'
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+ require 'babl/nodes'
3
+
4
+ module Babl
5
+ module Operators
6
+ module Concat
7
+ module DSL
8
+ # Produce an array by concatening the provided result of the given templates.
9
+ # (they therefor have to produce arrays, or nil, which is interpreted as an empty array)
10
+ def concat(*templates)
11
+ templates = templates.map { |t| unscoped.call(t) }
12
+
13
+ construct_terminal { |context|
14
+ Nodes::Concat.new(
15
+ templates.map { |t|
16
+ t.builder.precompile(
17
+ Nodes::TerminalValue.instance,
18
+ context.merge(continue: nil)
19
+ )
20
+ }
21
+ )
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -26,6 +26,8 @@ module Babl
26
26
  simplify_push_down_dyn_array ||
27
27
  simplify_dyn_and_fixed_array ||
28
28
  simplify_merge_objects ||
29
+ simplify_integer_is_number ||
30
+ simplify_many_fixed_arrays ||
29
31
  self
30
32
  end
31
33
 
@@ -35,6 +37,31 @@ module Babl
35
37
 
36
38
  private
37
39
 
40
+ def simplify_integer_is_number
41
+ return unless choice_set.include?(Typed::INTEGER) && choice_set.include?(Typed::NUMBER)
42
+ AnyOf.canonicalized(choice_set - [Typed::INTEGER])
43
+ end
44
+
45
+ # AnyOf[FixedArray(Item1, Item2), FixedArray(Item3, Item4)] can be summarized
46
+ # by DynArray(AnyOf(Item1, Item2, Item3, Item4)). It is a lossy transformation
47
+ # but it will help reducing the number of permutations when the operator concat() is used.
48
+ def simplify_many_fixed_arrays
49
+ choice_set.each_with_index { |obj1, index1|
50
+ next unless FixedArray === obj1
51
+
52
+ choice_set.each_with_index { |obj2, index2|
53
+ break if index2 >= index1
54
+ next unless FixedArray === obj2
55
+
56
+ return AnyOf.canonicalized(choice_set - [obj1, obj2] + [
57
+ DynArray.new(AnyOf.new(obj1.items + obj2.items))
58
+ ])
59
+ }
60
+ }
61
+
62
+ nil
63
+ end
64
+
38
65
  # We can completely get rid of the AnyOf element of there is only one possible schema.
39
66
  def simplify_single
40
67
  choice_set.size == 1 ? choice_set.first : nil
@@ -96,7 +123,7 @@ module Babl
96
123
  nil
97
124
  end
98
125
 
99
- # Merge all objects together. This is the only lossy simplification, but it will greatly reduce the size
126
+ # Merge all objects together. This is a lossy simplification, but it will greatly reduce the size
100
127
  # of the generated schema. On top of that, when the JSON-Schema is translated into Typescript, it produces
101
128
  # a much more workable type definition (union of anonymous object types is not practical to use)
102
129
  def simplify_merge_objects
@@ -7,7 +7,11 @@ module Babl
7
7
  EMPTY = new([])
8
8
 
9
9
  def json
10
- { type: 'array', items: items.map(&:json) }
10
+ if items.empty?
11
+ { enum: [[]] }
12
+ else
13
+ { type: 'array', items: items.map(&:json), additionalItems: false }
14
+ end
11
15
  end
12
16
  end
13
17
  end
data/lib/babl/template.rb CHANGED
@@ -6,6 +6,7 @@ module Babl
6
6
  class Template < Babl::Builder::TemplateBase
7
7
  include Operators::Array::DSL
8
8
  include Operators::Call::DSL
9
+ include Operators::Concat::DSL
9
10
  include Operators::Continue::DSL
10
11
  include Operators::Default::DSL
11
12
  include Operators::Dep::DSL
data/lib/babl/utils.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'babl/utils/array'
2
3
  require 'babl/utils/hash'
3
4
  require 'babl/utils/ref'
4
5
  require 'babl/utils/value'
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ module Babl
3
+ module Utils
4
+ class Array
5
+ EMPTY = [].freeze
6
+ end
7
+ end
8
+ end
data/lib/babl/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Babl
3
- VERSION = '0.3.4'
3
+ VERSION = '0.4.0'
4
4
  end
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.3.4
4
+ version: 0.4.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: 2017-10-01 00:00:00.000000000 Z
11
+ date: 2017-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -135,6 +135,7 @@ files:
135
135
  - lib/babl/builder/template_base.rb
136
136
  - lib/babl/errors.rb
137
137
  - lib/babl/nodes.rb
138
+ - lib/babl/nodes/concat.rb
138
139
  - lib/babl/nodes/constant.rb
139
140
  - lib/babl/nodes/create_pin.rb
140
141
  - lib/babl/nodes/dep.rb
@@ -154,6 +155,7 @@ files:
154
155
  - lib/babl/operators.rb
155
156
  - lib/babl/operators/array.rb
156
157
  - lib/babl/operators/call.rb
158
+ - lib/babl/operators/concat.rb
157
159
  - lib/babl/operators/continue.rb
158
160
  - lib/babl/operators/default.rb
159
161
  - lib/babl/operators/dep.rb
@@ -189,6 +191,7 @@ files:
189
191
  - lib/babl/schema/typed.rb
190
192
  - lib/babl/template.rb
191
193
  - lib/babl/utils.rb
194
+ - lib/babl/utils/array.rb
192
195
  - lib/babl/utils/dsl_proxy.rb
193
196
  - lib/babl/utils/hash.rb
194
197
  - lib/babl/utils/ref.rb