babl-json 0.3.4 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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