lacerda 1.1.0 → 2.0.1

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