lacerda 1.1.0 → 2.0.1

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: 1c72c775cac5fa6c427773de8b002da68b01cead
4
- data.tar.gz: 1b6adc7418567927bddd5ac87a7af26e859bd335
3
+ metadata.gz: 9e13fe3981ad5e7dc33070f0343f43745d032269
4
+ data.tar.gz: 527ce7ff8b9e0a89a5d4e0d1f8c50a506b9b4cb5
5
5
  SHA512:
6
- metadata.gz: c0d3f4a41eb11d90c348d73f5295830461ee32c31f30cf14e84831acc453f99b0112081707abb4305c7aeda9a766947065e67eab60eeb2404185d116df3a39e6
7
- data.tar.gz: aeace94ffb4d71e354124fd1d0a58b9563e8c55b8ad2baccf6a11559fa924980b9597c24930a335de42f6920c106c8adf189c5fc7f80d4c101b44ae7ecabc5a1
6
+ metadata.gz: 4ac94e6e2c6681138aa1dc83d6beb03d234ca0b8325e4c4aa5deb37967814ced69b38c866a77f98bbaed1051438a30a8ece9d59ae193d98740bc9a6b22ca515c
7
+ data.tar.gz: cc41f55260d38599b36e4403f567c9ba09483452d3890b99fa722f75211b638603631aa653ab9f328e0ea3c5f2e4ab937cc8f3a027ee2e136c8016a4ba512f57
@@ -1,3 +1,10 @@
1
+ # 2.0.1
2
+ - Merge (for real) add enum support to master
3
+
4
+ # 2.0.0
5
+ - [Breaking] Raise parsing errors, instead of silently returning an empty contract
6
+ - Add enum support
7
+
1
8
  # 1.1.0
2
9
  - Update blumquist to show prettier errors
3
10
 
data/README.md CHANGED
@@ -62,10 +62,10 @@ conversion ourselves. These features from the MSON specification are currently s
62
62
  - [x] primitive properties: `string`, `number`, `boolean`, `null`
63
63
  - [x] `object` properties
64
64
  - [x] `array` properties with items of one type
65
- - [ ] `array` properties of mixed types
65
+ - [x] `array` properties of mixed types
66
66
  - [ ] `array` properties of arrays
67
- - [ ] `enum` properties
68
- - [ ] `One of` properties mutually exclusive properties
67
+ - [x] `enum` properties
68
+ - [x] `One of` properties mutually exclusive properties
69
69
  - [x] `Referencing`
70
70
  - [ ] `Mixins`
71
71
  - [ ] Variable property names
@@ -99,8 +99,8 @@ module Lacerda
99
99
  true
100
100
  end
101
101
 
102
- def self.raise_parsing_errors(mson_file, ast_file)
103
- parsing_errors = ast_parsing_errors(ast_file)
102
+ def self.raise_parsing_errors(mson_file, elements)
103
+ parsing_errors = ast_parsing_errors(elements)
104
104
  return if parsing_errors.empty?
105
105
  raise Error, parsing_errors.prepend("The following errors were found in #{mson_file}:").join("\n")
106
106
  end
@@ -122,6 +122,8 @@ module Lacerda
122
122
  # The content of the ast parsing
123
123
  elements = parse_result_contents_from_ast_file(filename)
124
124
 
125
+ raise_parsing_errors(filename, elements)
126
+
125
127
  # We keep the content of the categories only, they could be annotations otherwise
126
128
  result_categories = elements.select do |element|
127
129
  element['element'] == 'category'
@@ -141,12 +143,10 @@ module Lacerda
141
143
  end
142
144
  end
143
145
 
144
- def self.ast_parsing_annotation_messages(filename, type)
145
- annotations = annotations_from_blueprint_ast(filename).select do |annotation|
146
- annotation['meta']['classes'].include?(type)
147
- end
148
- return [] if annotations.empty?
149
- annotations.map do |annotation|
146
+ def self.ast_parsing_annotation_messages(elements, type)
147
+ elements.select do |element|
148
+ element['element'] == 'annotation' && element['meta']['classes'].include?(type)
149
+ end.map do |annotation|
150
150
  "#{type.capitalize} code #{annotation['attributes']['code']}: #{annotation['content']}"
151
151
  end
152
152
  end
@@ -176,10 +176,5 @@ module Lacerda
176
176
  json = JSON.parse(open(filename).read)
177
177
  json&.dig('content') || []
178
178
  end
179
-
180
- private_class_method def self.annotations_from_blueprint_ast(filename)
181
- elements = parse_result_contents_from_ast_file(filename)
182
- elements.select { |element| element['element'] == 'annotation' }
183
- end
184
179
  end
185
180
  end
@@ -1,4 +1,6 @@
1
1
  require 'lacerda/conversion/data_structure'
2
+ require 'lacerda/conversion/data_structure/member'
3
+ require 'lacerda/conversion/data_structure/member/type'
2
4
 
3
5
  module Lacerda
4
6
  module Conversion
@@ -53,112 +53,16 @@ module Lacerda
53
53
  end
54
54
 
55
55
  def add_properties_to_json_schema
56
- possible_members = @data&.first&.dig('content')
57
- return unless possible_members
58
- # In the case that you create a nested data structure when type == 'object',
59
- # the possible_members can be just a Hash, instead of an array
60
- possible_members = [possible_members] if possible_members.is_a?(Hash)
61
- members = possible_members.select { |d| d['element'] == 'member' }
56
+ members = Member.from_data_structure_content(@data&.first&.dig('content'), @scope)
62
57
  # Iterate over each property
63
- members.each do |s|
64
-
65
- # Pluck some things out of the AST
66
- content = s['content']
67
- type_definition = content['value']
68
- type = type_definition['element']
69
- attributes = s.dig('attributes', 'typeAttributes') || []
70
- is_required = attributes.include?('required')
71
-
72
- # Prepare the json schema fragment
73
- spec = {}
74
- name = Lacerda.underscore(content['key']['content'])
75
-
76
- # This is either type: primimtive or a oneOf { $ref: reference_name }
77
- spec.merge!(primitive_or_oneOf(type, is_required))
78
-
79
- # We might have a description
80
- spec['description'] = s.dig('meta', 'description')
81
-
82
- # If it's an array, we need to pluck out the item types
83
- if type == 'array'
84
- nestedTypes = type_definition['content'].map{|vc| vc['element'] }.uniq
85
- spec['items'] = array_items(nestedTypes)
86
-
87
- # If it's an object, we need recursion
88
- elsif type == 'object'
89
- spec['properties'] = {}
90
- # The object has a value that will represent a data structure. the data
91
- # passed to DataStructure normally is an array, but in this case if wouldn't
92
- # So we have to wrap it if it's not an Array.
93
- data = [content['value']] unless content['value'].is_a?(Array)
94
- data_structure = DataStructure.new('tmp', [content['value']], @scope).to_json
95
- spec['properties'].merge!(data_structure['properties'])
96
- end
97
-
58
+ members.each do |member|
98
59
  # Add the specification of this property to the schema
99
- @schema['properties'][name] = spec
100
-
60
+ @schema['properties'][member.name] = member.spec
101
61
  # Mark the property as required
102
- @schema['required'] << name if is_required
103
- end
104
- end
105
-
106
- # returns the type of an array, given its specified type(s). This will be
107
- # either exactly one basic type, or a oneOf in case there are 1+ types
108
- # or exactly 1 non-basic type.
109
- # As there are specied types in the array, `nil` should not be a valid value
110
- # and therefore required should be true.
111
- def array_items(types)
112
- if types.size == 1 && PRIMITIVES.include?(types.first)
113
- primitive(types.first, true)
114
- else
115
- oneOf(types, true)
116
- end
117
- end
118
-
119
- def primitive_or_oneOf(type, is_required)
120
- return { 'type' => 'object' } if type.blank?
121
- if PRIMITIVES.include?(type)
122
- primitive(type, is_required)
123
- else
124
- oneOf([type], is_required)
125
- end
126
- end
127
-
128
- # A basic type is either a primitive type with exactly 1 primitive type
129
- # {'type' => [boolean] }
130
- # a reference
131
- # { '$ref' => "#/definitions/name" }
132
- # or an object if the type in not there
133
- # { 'type' => object }
134
- # Basic types don't care about being required or not.
135
- def basic_type(type)
136
- return { 'type' => 'object' } if type.blank?
137
- if PRIMITIVES.include?(type)
138
- primitive(type, true)
139
- else
140
- reference(type)
62
+ @schema['required'] << member.name if member.required?
141
63
  end
142
64
  end
143
65
 
144
- def oneOf(types, is_required)
145
- types = types.map { |type| basic_type(type) }
146
- types << { 'type' => 'null' } unless is_required
147
- {
148
- 'oneOf' => types.uniq
149
- }
150
- end
151
-
152
- def primitive(type, is_required)
153
- types = [type]
154
- types << 'null' unless is_required
155
- { 'type' => types }
156
- end
157
-
158
- def reference(type)
159
- {'$ref' => "#/definitions/#{self.class.scope(@scope, type)}" }
160
- end
161
-
162
66
  def json_schema_blueprint
163
67
  {
164
68
  "type" => "object",
@@ -166,7 +70,6 @@ module Lacerda
166
70
  "required" => []
167
71
  }
168
72
  end
169
-
170
73
  end
171
74
  end
172
75
  end
@@ -0,0 +1,53 @@
1
+ # Represents a DataStructure AST member and
2
+ # helps transforming it into a json schema.
3
+ module Lacerda
4
+ module Conversion
5
+ class DataStructure
6
+ class Member
7
+ attr_reader :name
8
+
9
+ def self.from_data_structure_content(data_structure_content, scope)
10
+ return [] if data_structure_content.nil?
11
+ # In the case that you create a nested data structure when type == 'object',
12
+ # the possible_members can be just a Hash, instead of an array
13
+ if data_structure_content.is_a?(Hash)
14
+ data_structure_content = [data_structure_content]
15
+ end
16
+ members = data_structure_content.select do |d|
17
+ d['element'] == 'member'
18
+ end
19
+ members.map { |member| Member.new(member, scope) }
20
+ end
21
+
22
+ def initialize(member, scope)
23
+ @description = member.dig('meta', 'description')
24
+ @content = member['content']
25
+ @name = Lacerda.underscore(@content['key']['content'])
26
+ @attributes = member.dig('attributes', 'typeAttributes') || []
27
+ @is_required = @attributes.include?('required')
28
+ @type = Type.new(@content['value'], @is_required, @scope)
29
+ @scope = scope
30
+ end
31
+
32
+ def required?
33
+ @is_required
34
+ end
35
+
36
+ def spec
37
+ spec = {}
38
+ # We might have a description
39
+ spec['description'] = @description
40
+ spec.merge!(@type.to_hash)
41
+ # Add the type of the array objects (if it is an array)
42
+ spec['items'] = @type.array_type
43
+ # If it's an object, we need recursion
44
+ if @type.object?
45
+ data_structure = DataStructure.new('tmp', [@content['value']], @scope).to_json
46
+ spec['properties'] = data_structure['properties']
47
+ end
48
+ spec
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,100 @@
1
+ module Lacerda
2
+ module Conversion
3
+ class DataStructure
4
+ class Member
5
+ class Type
6
+ def initialize(type_definition, is_required, scope)
7
+ @type_definition = type_definition
8
+ @scope = scope
9
+ @type_name = type_definition['element']
10
+ @is_required = is_required
11
+ end
12
+
13
+ def object?
14
+ @type_name == 'object'
15
+ end
16
+
17
+ # A type is transformed to json schema either as a primitive:
18
+ # { "type" => ["string"] }
19
+ # { "type" => ["string", "null"] }
20
+ # Or as a oneOf. $ref are always within a oneOf
21
+ # {"oneOf"=>[{"$ref"=>"#/definitions/tag"}, {"type"=>"null"}]}
22
+ def to_hash
23
+ if PRIMITIVES.include?(@type_name)
24
+ primitive(@type_name, required?)
25
+ else
26
+ oneOf([@type_name], required?)
27
+ end
28
+ end
29
+
30
+ # bui
31
+ # As there are specied types in the array, `nil` should not be a valid value
32
+ # and therefore required should be true.
33
+ def array_type
34
+ return unless array?
35
+ if nested_types.size == 1 && PRIMITIVES.include?(nested_types.first)
36
+ primitive(nested_types.first, true)
37
+ else
38
+ oneOf(nested_types, true)
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def array?
45
+ @type_name == 'array'
46
+ end
47
+
48
+ # A basic type is either a primitive type with exactly 1 primitive type
49
+ # {'type' => [boolean] }
50
+ # a reference
51
+ # { '$ref' => "#/definitions/name" }
52
+ # or an object if the type in not there
53
+ # { 'type' => object }
54
+ # Basic types don't care about being required or not.
55
+ def basic_type(type_name, is_required = required?)
56
+ if PRIMITIVES.include?(type_name)
57
+ primitive(type_name, is_required)
58
+ else
59
+ reference(type_name)
60
+ end
61
+ end
62
+
63
+ def oneOf(types, is_required)
64
+ types = types.map { |type_name| basic_type(type_name,is_required) }
65
+ types << { 'type' => 'null' } unless is_required
66
+ {
67
+ 'oneOf' => types.uniq
68
+ }
69
+ end
70
+
71
+ def reference(type_name)
72
+ {'$ref' => "#/definitions/#{Lacerda::Conversion::DataStructure.scope(@scope, type_name)}" }
73
+ end
74
+
75
+ def primitive(type_name, is_required)
76
+ types = [type_name]
77
+ types << 'null' unless is_required
78
+ if type_name == 'enum'
79
+ enum_values = @type_definition['content'].map { |i| i['content'] }
80
+ { 'type' => types, 'enum' => enum_values }
81
+ else
82
+ { 'type' => types }
83
+ end
84
+ end
85
+
86
+ def required?
87
+ @is_required
88
+ end
89
+
90
+ def nested_types
91
+ error_msg = "This DataStructure::Member is a #{@type_name}, not "\
92
+ 'an array, so it cannot have nested types'
93
+ raise error_msg unless array?
94
+ @type_definition['content'].map{|vc| vc['element'] }.uniq
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -1,3 +1,3 @@
1
1
  module Lacerda
2
- VERSION = '1.1.0'
2
+ VERSION = '2.0.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lacerda
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jannis Hermanns
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-18 00:00:00.000000000 Z
11
+ date: 2017-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -252,6 +252,8 @@ files:
252
252
  - lib/lacerda/conversion.rb
253
253
  - lib/lacerda/conversion/apiary_to_json_schema.rb
254
254
  - lib/lacerda/conversion/data_structure.rb
255
+ - lib/lacerda/conversion/data_structure/member.rb
256
+ - lib/lacerda/conversion/data_structure/member/type.rb
255
257
  - lib/lacerda/conversion/error.rb
256
258
  - lib/lacerda/infrastructure.rb
257
259
  - lib/lacerda/object_description.rb