matter_compiler 0.4.0 → 0.5.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 +4 -4
- data/Gemfile.lock +1 -1
- data/features/compose.feature +80 -86
- data/lib/matter_compiler/blueprint.rb +174 -146
- data/lib/matter_compiler/composer.rb +5 -0
- data/lib/matter_compiler/version.rb +1 -1
- data/test/action_test.rb +2 -7
- data/test/blueprint_test.rb +2 -2
- data/test/headers_test.rb +11 -8
- data/test/metadata_test.rb +5 -4
- data/test/parameters_test.rb +23 -16
- data/test/payload_test.rb +1 -1
- data/test/resource_test.rb +2 -7
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9153d21f8315633c3bc9d88554c1ce4e78c32787
|
4
|
+
data.tar.gz: ad37793f494ab8a5ed3e0a853d363fedde362f4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b031392eeeba9b4cb9bc0aca86259cf96d48aa7ad1d278c48c507bb7e96fcac9e1cc48631f9ec48c62c883886e6d0ce389fc58a36d048f9e4898ec75000501c2
|
7
|
+
data.tar.gz: 2ebb07a49858b3a01c7f74746c2f41498cdb4c0834a14836fac9302440b00f951d1bd0d33aa3810d264e0cd55dec9d6d904b38c93afb3918675b8baefc71e125
|
data/Gemfile.lock
CHANGED
data/features/compose.feature
CHANGED
@@ -61,11 +61,11 @@
|
|
61
61
|
|
62
62
|
And a file named "ast.yaml" with:
|
63
63
|
"""
|
64
|
-
_version:
|
64
|
+
_version: 2.0
|
65
65
|
metadata:
|
66
|
-
|
67
|
-
|
68
|
-
name: My API
|
66
|
+
- name: "FORMAT"
|
67
|
+
value: "1A"
|
68
|
+
name: "My API"
|
69
69
|
description: "Description of *My API*.\n\n"
|
70
70
|
resourceGroups:
|
71
71
|
- name:
|
@@ -73,78 +73,73 @@
|
|
73
73
|
resources:
|
74
74
|
- name:
|
75
75
|
description:
|
76
|
-
uriTemplate: /
|
76
|
+
uriTemplate: "/"
|
77
77
|
model:
|
78
78
|
parameters:
|
79
|
-
headers:
|
80
79
|
actions:
|
81
80
|
- name:
|
82
81
|
description:
|
83
|
-
method: GET
|
82
|
+
method: "GET"
|
84
83
|
parameters:
|
85
|
-
headers:
|
86
84
|
examples:
|
87
85
|
- name:
|
88
86
|
description:
|
89
87
|
requests:
|
90
88
|
responses:
|
91
|
-
- name: 200
|
89
|
+
- name: "200"
|
92
90
|
description:
|
93
91
|
headers:
|
94
|
-
|
95
|
-
|
92
|
+
- name: "Content-Type"
|
93
|
+
value: "text/plain"
|
96
94
|
body: "Hello World!\n"
|
97
95
|
schema:
|
98
|
-
- name: Red
|
96
|
+
- name: "Red"
|
99
97
|
description: "Description of *Red* Group.\n\n"
|
100
98
|
resources:
|
101
|
-
- name: My Resource
|
99
|
+
- name: "My Resource"
|
102
100
|
description: "Description of *My Resource*\n\n"
|
103
101
|
uriTemplate: "/myresource/{id}"
|
104
102
|
model:
|
105
|
-
name: My Resource
|
103
|
+
name: "My Resource"
|
106
104
|
description:
|
107
105
|
headers:
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
106
|
+
- name: "Content-Type"
|
107
|
+
value: "application/json"
|
108
|
+
- name: "X-Header"
|
109
|
+
value: "1"
|
112
110
|
body: "{ \"message\": \"Hello World\" }\n"
|
113
111
|
schema: "{ \"$schema\": \"http://json-schema.org/draft-03/schema\" }\n"
|
114
112
|
parameters:
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
headers:
|
113
|
+
- name: "id"
|
114
|
+
description: "Parameter `id` description."
|
115
|
+
type: "number"
|
116
|
+
required: false
|
117
|
+
default: "42"
|
118
|
+
example: "1000"
|
119
|
+
values:
|
123
120
|
actions:
|
124
|
-
- name: Retrieve My Resource
|
121
|
+
- name: "Retrieve My Resource"
|
125
122
|
description:
|
126
|
-
method: GET
|
123
|
+
method: "GET"
|
127
124
|
parameters:
|
128
|
-
headers:
|
129
125
|
examples:
|
130
126
|
- name:
|
131
127
|
description:
|
132
128
|
requests:
|
133
129
|
responses:
|
134
|
-
- name: 200
|
130
|
+
- name: "200"
|
135
131
|
description:
|
136
132
|
headers:
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
133
|
+
- name: "Content-Type"
|
134
|
+
value: "application/json"
|
135
|
+
- name: "X-Header"
|
136
|
+
value: "1"
|
141
137
|
body: "{ \"message\": \"Hello World\" }\n"
|
142
138
|
schema: "{ \"$schema\": \"http://json-schema.org/draft-03/schema\" }\n"
|
143
|
-
- name: Create My Resource
|
139
|
+
- name: "Create My Resource"
|
144
140
|
description:
|
145
|
-
method: POST
|
141
|
+
method: "POST"
|
146
142
|
parameters:
|
147
|
-
headers:
|
148
143
|
examples:
|
149
144
|
- name:
|
150
145
|
description:
|
@@ -152,28 +147,28 @@
|
|
152
147
|
- name:
|
153
148
|
description:
|
154
149
|
headers:
|
155
|
-
|
156
|
-
|
150
|
+
- name: "Content-Type"
|
151
|
+
value: "text/plain"
|
157
152
|
body: "Ni Hao!\n"
|
158
153
|
schema:
|
159
154
|
responses:
|
160
|
-
- name: 204
|
155
|
+
- name: "204"
|
161
156
|
description:
|
162
157
|
headers:
|
163
158
|
body:
|
164
159
|
schema:
|
165
|
-
|
166
160
|
"""
|
167
161
|
|
168
162
|
And a file named "ast.json" with:
|
169
163
|
"""
|
170
164
|
{
|
171
|
-
"_version": "
|
172
|
-
"metadata":
|
173
|
-
|
165
|
+
"_version": "2.0",
|
166
|
+
"metadata": [
|
167
|
+
{
|
168
|
+
"name": "FORMAT",
|
174
169
|
"value": "1A"
|
175
170
|
}
|
176
|
-
|
171
|
+
],
|
177
172
|
"name": "My API",
|
178
173
|
"description": "Description of *My API*.\n\n",
|
179
174
|
"resourceGroups": [
|
@@ -186,15 +181,13 @@
|
|
186
181
|
"description": "",
|
187
182
|
"uriTemplate": "/",
|
188
183
|
"model": {},
|
189
|
-
"parameters":
|
190
|
-
"headers": {},
|
184
|
+
"parameters": [],
|
191
185
|
"actions": [
|
192
186
|
{
|
193
187
|
"name": "",
|
194
188
|
"description": "",
|
195
189
|
"method": "GET",
|
196
|
-
"parameters":
|
197
|
-
"headers": {},
|
190
|
+
"parameters": [],
|
198
191
|
"examples": [
|
199
192
|
{
|
200
193
|
"name": "",
|
@@ -204,11 +197,12 @@
|
|
204
197
|
{
|
205
198
|
"name": "200",
|
206
199
|
"description": "",
|
207
|
-
"headers":
|
208
|
-
|
200
|
+
"headers": [
|
201
|
+
{
|
202
|
+
"name": "Content-Type",
|
209
203
|
"value": "text/plain"
|
210
204
|
}
|
211
|
-
|
205
|
+
],
|
212
206
|
"body": "Hello World!\n",
|
213
207
|
"schema": ""
|
214
208
|
}
|
@@ -229,37 +223,38 @@
|
|
229
223
|
"description": "Description of *My Resource*\n\n",
|
230
224
|
"uriTemplate": "/myresource/{id}",
|
231
225
|
"model": {
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
"X-Header": {
|
239
|
-
"value": "1"
|
240
|
-
}
|
226
|
+
"name": "My Resource",
|
227
|
+
"description": "",
|
228
|
+
"headers": [
|
229
|
+
{
|
230
|
+
"name": "Content-Type",
|
231
|
+
"value": "application/json"
|
241
232
|
},
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
233
|
+
{
|
234
|
+
"name": "X-Header",
|
235
|
+
"value": "1"
|
236
|
+
}
|
237
|
+
],
|
238
|
+
"body": "{ \"message\": \"Hello World\" }\n",
|
239
|
+
"schema": "{ \"$schema\": \"http://json-schema.org/draft-03/schema\" }\n"
|
240
|
+
},
|
241
|
+
"parameters": [
|
242
|
+
{
|
243
|
+
"name": "id",
|
244
|
+
"description": "Parameter `id` description.",
|
248
245
|
"type": "number",
|
249
246
|
"required": false,
|
250
247
|
"default": "42",
|
251
248
|
"example": "1000",
|
252
249
|
"values": []
|
253
250
|
}
|
254
|
-
|
255
|
-
"headers": {},
|
251
|
+
],
|
256
252
|
"actions": [
|
257
253
|
{
|
258
254
|
"name": "Retrieve My Resource",
|
259
255
|
"description": "",
|
260
256
|
"method": "GET",
|
261
|
-
"parameters":
|
262
|
-
"headers": {},
|
257
|
+
"parameters": [],
|
263
258
|
"examples": [
|
264
259
|
{
|
265
260
|
"name": "",
|
@@ -269,14 +264,16 @@
|
|
269
264
|
{
|
270
265
|
"name": "200",
|
271
266
|
"description": "",
|
272
|
-
"headers":
|
273
|
-
|
267
|
+
"headers": [
|
268
|
+
{
|
269
|
+
"name": "Content-Type",
|
274
270
|
"value": "application/json"
|
275
271
|
},
|
276
|
-
|
272
|
+
{
|
273
|
+
"name": "X-Header",
|
277
274
|
"value": "1"
|
278
275
|
}
|
279
|
-
|
276
|
+
],
|
280
277
|
"body": "{ \"message\": \"Hello World\" }\n",
|
281
278
|
"schema": "{ \"$schema\": \"http://json-schema.org/draft-03/schema\" }\n"
|
282
279
|
}
|
@@ -288,8 +285,7 @@
|
|
288
285
|
"name": "Create My Resource",
|
289
286
|
"description": "",
|
290
287
|
"method": "POST",
|
291
|
-
"parameters":
|
292
|
-
"headers": {},
|
288
|
+
"parameters": [],
|
293
289
|
"examples": [
|
294
290
|
{
|
295
291
|
"name": "",
|
@@ -298,11 +294,12 @@
|
|
298
294
|
{
|
299
295
|
"name": "",
|
300
296
|
"description": "",
|
301
|
-
"headers":
|
302
|
-
|
297
|
+
"headers": [
|
298
|
+
{
|
299
|
+
"name": "Content-Type",
|
303
300
|
"value": "text/plain"
|
304
301
|
}
|
305
|
-
|
302
|
+
],
|
306
303
|
"body": "Ni Hao!\n",
|
307
304
|
"schema": ""
|
308
305
|
}
|
@@ -311,7 +308,7 @@
|
|
311
308
|
{
|
312
309
|
"name": "204",
|
313
310
|
"description": "",
|
314
|
-
"headers":
|
311
|
+
"headers": [],
|
315
312
|
"body": "",
|
316
313
|
"schema": ""
|
317
314
|
}
|
@@ -325,7 +322,6 @@
|
|
325
322
|
}
|
326
323
|
]
|
327
324
|
}
|
328
|
-
|
329
325
|
"""
|
330
326
|
|
331
327
|
Scenario: Compose blueprint from an YAML stdin input
|
@@ -364,7 +360,7 @@
|
|
364
360
|
Scenario: Explicitly set API Blueprint Format
|
365
361
|
Given a file named "no-format.yaml" with:
|
366
362
|
"""
|
367
|
-
_version:
|
363
|
+
_version: 2.0
|
368
364
|
metadata:
|
369
365
|
name: My API
|
370
366
|
description:
|
@@ -381,7 +377,7 @@
|
|
381
377
|
Scenario: Attempt to compose a resource without URI template
|
382
378
|
Given a file named "no-uri-template.yaml" with:
|
383
379
|
"""
|
384
|
-
_version:
|
380
|
+
_version: 2.0
|
385
381
|
metadata:
|
386
382
|
name: My API
|
387
383
|
description:
|
@@ -409,8 +405,6 @@
|
|
409
405
|
- name: 200
|
410
406
|
description:
|
411
407
|
headers:
|
412
|
-
Content-Type:
|
413
|
-
value: text/plain
|
414
408
|
body: "Hello World!\n"
|
415
409
|
schema:
|
416
410
|
"""
|
@@ -1,40 +1,58 @@
|
|
1
|
+
require 'object.rb'
|
2
|
+
|
3
|
+
# The classes in this module should be 1:1 with the Snow Crash AST
|
4
|
+
# counterparts (https://github.com/apiaryio/snowcrash/blob/master/src/Blueprint.h).
|
1
5
|
module MatterCompiler
|
2
6
|
|
3
|
-
# The classes in this document should be 1:1 with relevant Snow Crash
|
4
|
-
# counterparts (https://github.com/apiaryio/snowcrash/blob/master/src/Blueprint.h)
|
5
|
-
# until Matter Compiler becomes a wrapper for Snow Crash.
|
6
|
-
|
7
|
-
#
|
8
7
|
# Blueprint AST node
|
8
|
+
# Base class for API Blueprint AST nodes in Matter Compiler.
|
9
9
|
#
|
10
|
+
# @abstract
|
10
11
|
class BlueprintNode
|
12
|
+
|
11
13
|
ONE_INDENTATION_LEVEL = " "
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
+
# Initialize the node
|
16
|
+
#
|
17
|
+
# @param ast [Array, Hash, nil] a hash or array to initialize the node with or nil
|
18
|
+
def initialize(ast = nil)
|
19
|
+
load_ast!(ast) if ast
|
15
20
|
end
|
16
21
|
|
17
|
-
# Load AST
|
18
|
-
|
22
|
+
# Load AST object content into node
|
23
|
+
#
|
24
|
+
# @param ast [Array, Hash] an ast object to load
|
25
|
+
def load_ast!(ast)
|
19
26
|
end
|
20
27
|
|
21
|
-
# Serialize
|
22
|
-
#
|
28
|
+
# Serialize node to a Markdown string buffer
|
29
|
+
#
|
30
|
+
# @param level [Integer, 0] requested indentation level
|
31
|
+
# @return [String, nil] content of the node serialized into Markdown or nil
|
23
32
|
def serialize(level = 0)
|
24
33
|
end
|
25
34
|
end
|
26
35
|
|
36
|
+
# Blueprint AST node with name and description associated
|
37
|
+
#
|
38
|
+
# @attr name [String] name of the node
|
39
|
+
# @attr description [String] description of the node
|
27
40
|
#
|
28
|
-
#
|
29
|
-
#
|
41
|
+
# @abstract
|
30
42
|
class NamedBlueprintNode < BlueprintNode
|
31
|
-
|
32
|
-
|
33
|
-
|
43
|
+
|
44
|
+
attr_accessor :name
|
45
|
+
attr_accessor :description
|
46
|
+
|
47
|
+
def load_ast!(ast)
|
48
|
+
@name = ast[:name]
|
49
|
+
@description = ast[:description]
|
34
50
|
end
|
35
51
|
|
36
|
-
# Ensure
|
37
|
-
#
|
52
|
+
# Ensure the input string buffer ends with two newlines.
|
53
|
+
#
|
54
|
+
# @param buffer [String] a buffer to check
|
55
|
+
# If the buffer does not ends with two newlines the newlines are added.
|
38
56
|
def ensure_description_newlines(buffer)
|
39
57
|
return if description.blank?
|
40
58
|
|
@@ -46,27 +64,28 @@ module MatterCompiler
|
|
46
64
|
end
|
47
65
|
end
|
48
66
|
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
67
|
+
# Blueprint AST node for key-value collections
|
68
|
+
#
|
69
|
+
# @abstract
|
70
|
+
# @attr collection [Array<Hash>] array of key value hashes
|
52
71
|
class KeyValueCollection < BlueprintNode
|
72
|
+
|
53
73
|
attr_accessor :collection
|
54
74
|
|
55
|
-
def
|
56
|
-
return if
|
57
|
-
|
75
|
+
def load_ast!(ast)
|
76
|
+
return if ast.empty?
|
58
77
|
@collection = Array.new
|
59
|
-
|
60
|
-
@collection << Hash[
|
78
|
+
ast.each do |item|
|
79
|
+
@collection << Hash[item[:name].to_sym, item[:value]]
|
61
80
|
end
|
62
81
|
end
|
63
82
|
|
64
|
-
# Serialize key value
|
65
|
-
#
|
83
|
+
# Serialize key value collection node to a Markdown string buffer
|
84
|
+
#
|
85
|
+
# @param ignore_keys [Array<String>] array of keys that should be ignored (skipped) during the serialization
|
66
86
|
def serialize(level = 0, ignore_keys = nil)
|
67
87
|
buffer = ""
|
68
88
|
@collection.each do |hash|
|
69
|
-
# Skip ignored keys
|
70
89
|
unless ignore_keys && ignore_keys.include?(hash.keys.first)
|
71
90
|
level.times { buffer << ONE_INDENTATION_LEVEL }
|
72
91
|
buffer << "#{hash.keys.first}: #{hash.values.first}\n"
|
@@ -77,70 +96,73 @@ module MatterCompiler
|
|
77
96
|
buffer
|
78
97
|
end
|
79
98
|
|
80
|
-
#
|
81
|
-
|
99
|
+
# Filter collection keys
|
100
|
+
#
|
101
|
+
# @returns [Array<Hash>] collection without ignored keys
|
102
|
+
def filter_collection(ignore_keys)
|
82
103
|
return @collection if ignore_keys.blank?
|
83
104
|
@collection.select { |kv_item| !ignore_keys.include?(kv_item.keys.first) }
|
84
105
|
end
|
85
106
|
end
|
86
107
|
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
108
|
+
# Metadata collection Blueprint AST node
|
109
|
+
# represents 'metadata section'
|
90
110
|
class Metadata < KeyValueCollection
|
91
111
|
end
|
92
112
|
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
113
|
+
# Headers collection Blueprint AST node
|
114
|
+
# represents 'headers section'
|
96
115
|
class Headers < KeyValueCollection
|
97
116
|
|
117
|
+
# HTTP 'Content-Type' header
|
98
118
|
CONTENT_TYPE_HEADER_KEY = :'Content-Type'
|
99
119
|
|
100
120
|
def serialize(level = 0, ignore_keys = nil)
|
101
|
-
return "" if @collection.blank? ||
|
121
|
+
return "" if @collection.blank? || filter_collection(ignore_keys).blank?
|
102
122
|
|
103
123
|
buffer = ""
|
104
124
|
level.times { buffer << ONE_INDENTATION_LEVEL }
|
105
125
|
buffer << "+ Headers\n\n"
|
106
|
-
|
107
126
|
buffer << super(level + 2, ignore_keys)
|
108
127
|
end
|
109
128
|
|
110
|
-
#
|
129
|
+
# @return [String] the value of 'Content-type' header if present or nil
|
111
130
|
def content_type
|
112
131
|
content_type_header = @collection.detect { |header| header.has_key?(CONTENT_TYPE_HEADER_KEY) }
|
113
132
|
return (content_type_header.nil?) ? nil : content_type_header[CONTENT_TYPE_HEADER_KEY]
|
114
133
|
end
|
115
134
|
end;
|
116
135
|
|
136
|
+
# URI parameter Blueprint AST node
|
137
|
+
# represents one 'parameters section' parameter
|
117
138
|
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
|
121
|
-
|
122
|
-
|
139
|
+
# @attr type [String] an arbitrary type of the parameter or nil
|
140
|
+
# @attr use [Symbol] parameter necessity flag, `:required` or `:optional`
|
141
|
+
# @attr default_value [String] default value of the parameter or nil
|
142
|
+
# This is a value used when the parameter is ommited in the request.
|
143
|
+
# @attr example_value [String] example value of the parameter or nil
|
144
|
+
# @attr values [Array<String>] an enumeration of possible parameter values
|
145
|
+
class Parameter < NamedBlueprintNode
|
146
|
+
|
123
147
|
attr_accessor :type
|
124
148
|
attr_accessor :use
|
125
149
|
attr_accessor :default_value
|
126
150
|
attr_accessor :example_value
|
127
151
|
attr_accessor :values
|
128
152
|
|
129
|
-
def
|
130
|
-
super(
|
131
|
-
@name = name.to_s if name
|
132
|
-
end
|
153
|
+
def load_ast!(ast)
|
154
|
+
super(ast)
|
133
155
|
|
134
|
-
|
135
|
-
@
|
136
|
-
@
|
137
|
-
@
|
138
|
-
@default_value = hash[:default] if hash[:default]
|
139
|
-
@example_value = hash[:example] if hash[:example]
|
156
|
+
@type = ast[:type] if ast[:type]
|
157
|
+
@use = (ast[:required] && ast[:required] == true) ? :required : :optional
|
158
|
+
@default_value = ast[:default] if ast[:default]
|
159
|
+
@example_value = ast[:example] if ast[:example]
|
140
160
|
|
141
|
-
unless
|
161
|
+
unless ast[:values].blank?
|
142
162
|
@values = Array.new
|
143
|
-
|
163
|
+
ast[:values].each do |item|
|
164
|
+
@values << item[:value]
|
165
|
+
end
|
144
166
|
end
|
145
167
|
end
|
146
168
|
|
@@ -209,18 +231,20 @@ module MatterCompiler
|
|
209
231
|
end
|
210
232
|
end
|
211
233
|
|
234
|
+
# Collection of URI parameters Blueprint AST node
|
235
|
+
# represents 'parameters section'
|
212
236
|
#
|
213
|
-
#
|
214
|
-
#
|
237
|
+
# @attr collection [Array<Parameter>] an array of URI parameters
|
215
238
|
class Parameters < BlueprintNode
|
239
|
+
|
216
240
|
attr_accessor :collection
|
217
241
|
|
218
|
-
def
|
219
|
-
return if
|
242
|
+
def load_ast!(ast)
|
243
|
+
return if ast.empty?
|
220
244
|
|
221
245
|
@collection = Array.new
|
222
|
-
|
223
|
-
@collection << Parameter.new(
|
246
|
+
ast.each do |item|
|
247
|
+
@collection << Parameter.new(item)
|
224
248
|
end
|
225
249
|
end
|
226
250
|
|
@@ -237,23 +261,27 @@ module MatterCompiler
|
|
237
261
|
end
|
238
262
|
end
|
239
263
|
|
264
|
+
# HTTP message payload Blueprint AST node
|
265
|
+
# base class for 'payload sections'
|
240
266
|
#
|
241
|
-
#
|
242
|
-
#
|
267
|
+
# @abstract
|
268
|
+
# @attr parameters [Array] ignored
|
269
|
+
# @attr headers [Array<Headers>] array of HTTP header fields of the message or nil
|
270
|
+
# @attr body [String] HTTP-message body or nil
|
271
|
+
# @attr schema [String] HTTP-message body validation schema or nil
|
243
272
|
class Payload < NamedBlueprintNode
|
244
|
-
|
245
|
-
attr_accessor :description
|
273
|
+
|
246
274
|
attr_accessor :parameters
|
247
275
|
attr_accessor :headers
|
248
276
|
attr_accessor :body
|
249
277
|
attr_accessor :schema
|
250
278
|
|
251
|
-
def
|
252
|
-
super(
|
279
|
+
def load_ast!(ast)
|
280
|
+
super(ast)
|
253
281
|
|
254
|
-
@headers = Headers.new(
|
255
|
-
@body =
|
256
|
-
@schema =
|
282
|
+
@headers = Headers.new(ast[:headers]) unless ast[:headers].blank?
|
283
|
+
@body = ast[:body] unless ast[:body].blank?
|
284
|
+
@schema = ast[:schema] unless ast[:schema].blank?
|
257
285
|
end
|
258
286
|
|
259
287
|
def serialize
|
@@ -271,7 +299,7 @@ module MatterCompiler
|
|
271
299
|
end
|
272
300
|
|
273
301
|
unless @body.blank?
|
274
|
-
abbreviated_synax = (headers.blank? || headers.
|
302
|
+
abbreviated_synax = (headers.blank? || headers.filter_collection([Headers::CONTENT_TYPE_HEADER_KEY]).blank?) \
|
275
303
|
& description.blank? \
|
276
304
|
& schema.blank?
|
277
305
|
asset_indent_level = 2
|
@@ -311,10 +339,12 @@ module MatterCompiler
|
|
311
339
|
buffer
|
312
340
|
end
|
313
341
|
|
314
|
-
# Serialize
|
315
|
-
#
|
316
|
-
#
|
317
|
-
|
342
|
+
# Serialize payaload's definition (lead-in)
|
343
|
+
#
|
344
|
+
# @param section [String] section type keyword
|
345
|
+
# @param ignore_name [Boolean] object to ignore section name in serialization, false otherwise
|
346
|
+
# @return [String] buffer with serialized section definition
|
347
|
+
def serialize_definition(section, ignore_name = false)
|
318
348
|
buffer = ""
|
319
349
|
buffer << "+ #{section}"
|
320
350
|
buffer << " #{@name}" unless ignore_name || @name.blank?
|
@@ -327,56 +357,53 @@ module MatterCompiler
|
|
327
357
|
end
|
328
358
|
end
|
329
359
|
|
330
|
-
#
|
331
|
-
#
|
332
|
-
#
|
360
|
+
# Model Payload Blueprint AST node
|
361
|
+
# represents 'model section'
|
333
362
|
class Model < Payload
|
334
363
|
def serialize
|
335
|
-
buffer =
|
364
|
+
buffer = serialize_definition("Model", true)
|
336
365
|
buffer << super
|
337
366
|
end
|
338
367
|
end
|
339
368
|
|
340
|
-
#
|
341
|
-
#
|
342
|
-
#
|
369
|
+
# Request Payload Blueprint AST node
|
370
|
+
# represents 'request section'
|
343
371
|
class Request < Payload
|
344
372
|
def serialize
|
345
|
-
buffer =
|
373
|
+
buffer = serialize_definition("Request")
|
346
374
|
buffer << super
|
347
375
|
end
|
348
376
|
end
|
349
377
|
|
350
|
-
#
|
351
378
|
# Response Payload
|
352
|
-
#
|
379
|
+
# represents 'response section'
|
353
380
|
class Response < Payload;
|
354
381
|
def serialize
|
355
|
-
buffer =
|
382
|
+
buffer = serialize_definition("Response")
|
356
383
|
buffer << super
|
357
384
|
end
|
358
385
|
end
|
359
386
|
|
387
|
+
# Transaction example Blueprint AST node
|
360
388
|
#
|
361
|
-
#
|
362
|
-
#
|
389
|
+
# @attr requests [Array<Request>] example request payloads
|
390
|
+
# @attr response [Array<Response>] example response payloads
|
363
391
|
class TransactionExample < NamedBlueprintNode
|
364
|
-
|
365
|
-
attr_accessor :description
|
392
|
+
|
366
393
|
attr_accessor :requests
|
367
394
|
attr_accessor :responses
|
368
395
|
|
369
|
-
def
|
370
|
-
super(
|
396
|
+
def load_ast!(ast)
|
397
|
+
super(ast)
|
371
398
|
|
372
|
-
unless
|
399
|
+
unless ast[:requests].blank?
|
373
400
|
@requests = Array.new
|
374
|
-
|
401
|
+
ast[:requests].each { |request_ast| @requests << Request.new(request_ast) }
|
375
402
|
end
|
376
403
|
|
377
|
-
unless
|
404
|
+
unless ast[:responses].blank?
|
378
405
|
@responses = Array.new
|
379
|
-
|
406
|
+
ast[:responses].each { |response_ast| @responses << Response.new(response_ast) }
|
380
407
|
end
|
381
408
|
end
|
382
409
|
|
@@ -388,27 +415,27 @@ module MatterCompiler
|
|
388
415
|
end
|
389
416
|
end
|
390
417
|
|
418
|
+
# Action Blueprint AST node
|
419
|
+
# represetns 'action sction'
|
391
420
|
#
|
392
|
-
#
|
393
|
-
#
|
421
|
+
# @attr method [String] HTTP request method or nil
|
422
|
+
# @attr parameters [Parameters] action-specific URI parameters or nil
|
423
|
+
# @attr examples [Array<TransactionExample>] action transaction examples
|
394
424
|
class Action < NamedBlueprintNode
|
425
|
+
|
395
426
|
attr_accessor :method
|
396
|
-
attr_accessor :name
|
397
|
-
attr_accessor :description
|
398
427
|
attr_accessor :parameters
|
399
|
-
attr_accessor :headers
|
400
428
|
attr_accessor :examples
|
401
429
|
|
402
|
-
def
|
403
|
-
super(
|
430
|
+
def load_ast!(ast)
|
431
|
+
super(ast)
|
404
432
|
|
405
|
-
@method =
|
406
|
-
@parameters = Parameters.new(
|
407
|
-
@headers = Headers.new(hash[:headers]) unless hash[:headers].blank?
|
433
|
+
@method = ast[:method]
|
434
|
+
@parameters = Parameters.new(ast[:parameters]) unless ast[:parameters].blank?
|
408
435
|
|
409
|
-
unless
|
436
|
+
unless ast[:examples].blank?
|
410
437
|
@examples = Array.new
|
411
|
-
|
438
|
+
ast[:examples].each { |example_ast| @examples << TransactionExample.new(example_ast) }
|
412
439
|
end
|
413
440
|
end
|
414
441
|
|
@@ -424,42 +451,42 @@ module MatterCompiler
|
|
424
451
|
ensure_description_newlines(buffer)
|
425
452
|
|
426
453
|
buffer << @parameters.serialize unless @parameters.nil?
|
427
|
-
buffer << @headers.serialize unless @headers.nil?
|
428
454
|
|
429
455
|
@examples.each { |example| buffer << example.serialize } unless @examples.nil?
|
430
456
|
buffer
|
431
457
|
end
|
432
458
|
end
|
433
459
|
|
460
|
+
# Resource Blueprint AST node
|
461
|
+
# represents 'resource section'
|
434
462
|
#
|
435
|
-
#
|
436
|
-
#
|
463
|
+
# @attr uri_template [String] RFC 6570 URI template
|
464
|
+
# @attr model [Model] model payload for the resource or nil
|
465
|
+
# @attr parameters [Parameters] action-specific URI parameters or nil
|
466
|
+
# @attr actions [Array<Action>] array of resource actions or nil
|
437
467
|
class Resource < NamedBlueprintNode
|
468
|
+
|
438
469
|
attr_accessor :uri_template
|
439
|
-
attr_accessor :name
|
440
|
-
attr_accessor :description
|
441
470
|
attr_accessor :model
|
442
471
|
attr_accessor :parameters
|
443
|
-
attr_accessor :headers
|
444
472
|
attr_accessor :actions
|
445
473
|
|
446
|
-
def
|
447
|
-
super(
|
474
|
+
def load_ast!(ast)
|
475
|
+
super(ast)
|
448
476
|
|
449
|
-
if
|
477
|
+
if ast[:uriTemplate].blank? || ast[:uriTemplate][0] != '/'
|
450
478
|
failure_message = "Invalid input: A resource is missing URI template"
|
451
|
-
failure_message << " ('#{
|
479
|
+
failure_message << " ('#{ast[:name]}' resource)" unless ast[:name].blank?
|
452
480
|
abort(failure_message);
|
453
481
|
end
|
454
482
|
|
455
|
-
@uri_template =
|
456
|
-
@model = Model.new(
|
457
|
-
@parameters = Parameters.new(
|
458
|
-
@headers = Headers.new(hash[:headers]) unless hash[:headers].blank?
|
483
|
+
@uri_template = ast[:uriTemplate]
|
484
|
+
@model = Model.new(ast[:model]) unless ast[:model].blank?
|
485
|
+
@parameters = Parameters.new(ast[:parameters]) unless ast[:parameters].blank?
|
459
486
|
|
460
|
-
unless
|
487
|
+
unless ast[:actions].blank?
|
461
488
|
@actions = Array.new
|
462
|
-
|
489
|
+
ast[:actions].each { |action_ast| @actions << Action.new(action_ast) }
|
463
490
|
end
|
464
491
|
end
|
465
492
|
|
@@ -476,27 +503,26 @@ module MatterCompiler
|
|
476
503
|
|
477
504
|
buffer << @model.serialize unless @model.nil?
|
478
505
|
buffer << @parameters.serialize unless @parameters.nil?
|
479
|
-
buffer << @headers.serialize unless @headers.nil?
|
480
506
|
|
481
507
|
@actions.each { |action| buffer << action.serialize } unless @actions.nil?
|
482
508
|
buffer
|
483
509
|
end
|
484
510
|
end
|
485
511
|
|
512
|
+
# Resource group Blueprint AST node
|
513
|
+
# represents 'resource group section'
|
486
514
|
#
|
487
|
-
# Resource
|
488
|
-
#
|
515
|
+
# @attr resources [Array<Resource>] array of resources in the group
|
489
516
|
class ResourceGroup < NamedBlueprintNode
|
490
|
-
|
491
|
-
attr_accessor :description
|
517
|
+
|
492
518
|
attr_accessor :resources
|
493
519
|
|
494
|
-
def
|
495
|
-
super(
|
520
|
+
def load_ast!(ast)
|
521
|
+
super(ast)
|
496
522
|
|
497
|
-
unless
|
523
|
+
unless ast[:resources].blank?
|
498
524
|
@resources = Array.new
|
499
|
-
|
525
|
+
ast[:resources].each { |resource_ast| @resources << Resource.new(resource_ast) }
|
500
526
|
end
|
501
527
|
end
|
502
528
|
|
@@ -511,30 +537,32 @@ module MatterCompiler
|
|
511
537
|
end
|
512
538
|
end
|
513
539
|
|
540
|
+
|
541
|
+
# Top-level Blueprint AST node
|
542
|
+
# represents 'blueprint section'
|
514
543
|
#
|
515
|
-
#
|
516
|
-
#
|
544
|
+
# @attr metadata [Metadata] tool-specific metadata collection or nil
|
545
|
+
# @attr resource_groups [Array<ResourceGroup>] array of blueprint resource groups
|
517
546
|
class Blueprint < NamedBlueprintNode
|
547
|
+
|
518
548
|
attr_accessor :metadata
|
519
|
-
attr_accessor :name
|
520
|
-
attr_accessor :description
|
521
549
|
attr_accessor :resource_groups
|
522
550
|
|
523
551
|
VERSION_KEY = :_version
|
524
|
-
SUPPORTED_VERSIONS = ["
|
552
|
+
SUPPORTED_VERSIONS = ["2.0"]
|
525
553
|
|
526
|
-
def
|
527
|
-
super(
|
554
|
+
def load_ast!(ast)
|
555
|
+
super(ast)
|
528
556
|
|
529
557
|
# Load Metadata
|
530
|
-
unless
|
531
|
-
@metadata = Metadata.new(
|
558
|
+
unless ast[:metadata].blank?
|
559
|
+
@metadata = Metadata.new(ast[:metadata])
|
532
560
|
end
|
533
561
|
|
534
562
|
# Load Resource Groups
|
535
|
-
unless
|
563
|
+
unless ast[:resourceGroups].blank?
|
536
564
|
@resource_groups = Array.new
|
537
|
-
|
565
|
+
ast[:resourceGroups].each { |group_ast| @resource_groups << ResourceGroup.new(group_ast) }
|
538
566
|
end
|
539
567
|
end
|
540
568
|
|
@@ -74,6 +74,11 @@ module MatterCompiler
|
|
74
74
|
|
75
75
|
# Check version of the AST
|
76
76
|
unless Blueprint::SUPPORTED_VERSIONS.include?(ast_hash[Blueprint::VERSION_KEY].to_s)
|
77
|
+
|
78
|
+
if ast_hash[Blueprint::VERSION_KEY].to_s == "1.0"
|
79
|
+
puts "Use matter_compiler v0.4.0 to process AST media types prior AST v2.0"
|
80
|
+
end
|
81
|
+
|
77
82
|
abort("Invalid input: Unsupported AST version: '#{ast_hash[Blueprint::VERSION_KEY]}'\n")
|
78
83
|
end
|
79
84
|
|
data/test/action_test.rb
CHANGED
@@ -10,7 +10,6 @@ class ActionTest < Minitest::Test
|
|
10
10
|
:description => "Dolor sit amet\n\n",
|
11
11
|
:method => "GET",
|
12
12
|
:parameters => ParametersTest::AST_HASH,
|
13
|
-
:headers => HeadersTest::AST_HASH,
|
14
13
|
:examples => [TransactionExampleTest::AST_HASH]
|
15
14
|
}
|
16
15
|
|
@@ -18,7 +17,7 @@ class ActionTest < Minitest::Test
|
|
18
17
|
%Q{### Into Action [GET]
|
19
18
|
Dolor sit amet
|
20
19
|
|
21
|
-
#{ParametersTest::BLUEPRINT}#{
|
20
|
+
#{ParametersTest::BLUEPRINT}#{TransactionExampleTest::BLUEPRINT}}
|
22
21
|
|
23
22
|
def test_from_ast_hash
|
24
23
|
action = MatterCompiler::Action.new(ActionTest::AST_HASH)
|
@@ -28,11 +27,7 @@ Dolor sit amet
|
|
28
27
|
|
29
28
|
assert_instance_of MatterCompiler::Parameters, action.parameters
|
30
29
|
assert_instance_of Array, action.parameters.collection
|
31
|
-
assert_equal ParametersTest::AST_HASH.
|
32
|
-
|
33
|
-
assert_instance_of MatterCompiler::Headers, action.headers
|
34
|
-
assert_instance_of Array, action.headers.collection
|
35
|
-
assert_equal HeadersTest::AST_HASH.keys.length, action.headers.collection.length
|
30
|
+
assert_equal ParametersTest::AST_HASH.length, action.parameters.collection.length
|
36
31
|
|
37
32
|
assert_instance_of Array, action.examples
|
38
33
|
assert_equal ActionTest::AST_HASH[:examples].length, action.examples.length
|
data/test/blueprint_test.rb
CHANGED
@@ -30,8 +30,8 @@ Lorem Ipsum
|
|
30
30
|
|
31
31
|
assert_instance_of MatterCompiler::Metadata, blueprint.metadata
|
32
32
|
assert_instance_of Array, blueprint.metadata.collection
|
33
|
-
assert_equal BlueprintTest::AST_HASH[:metadata].
|
34
|
-
assert_equal BlueprintTest::AST_HASH[:metadata]
|
33
|
+
assert_equal BlueprintTest::AST_HASH[:metadata].length, blueprint.metadata.collection.length
|
34
|
+
assert_equal BlueprintTest::AST_HASH[:metadata][0][:name], blueprint.metadata.collection[0].keys[0].to_s
|
35
35
|
end
|
36
36
|
|
37
37
|
def test_serialize
|
data/test/headers_test.rb
CHANGED
@@ -2,20 +2,23 @@ require 'minitest/autorun'
|
|
2
2
|
require 'matter_compiler/blueprint'
|
3
3
|
|
4
4
|
class HeadersTest < Minitest::Test
|
5
|
-
AST_HASH =
|
6
|
-
|
5
|
+
AST_HASH = [
|
6
|
+
{
|
7
|
+
:name => "X-Header",
|
7
8
|
:value => "1"
|
8
9
|
},
|
9
|
-
|
10
|
+
{
|
11
|
+
:name => "Content-Type",
|
10
12
|
:value => "text/plain"
|
11
13
|
}
|
12
|
-
|
14
|
+
]
|
13
15
|
|
14
|
-
AST_HASH_CONTENT_ONLY =
|
15
|
-
|
16
|
+
AST_HASH_CONTENT_ONLY = [
|
17
|
+
{
|
18
|
+
:name => "Content-Type",
|
16
19
|
:value => "text/plain"
|
17
|
-
}
|
18
|
-
|
20
|
+
}
|
21
|
+
]
|
19
22
|
|
20
23
|
BLUEPRINT = \
|
21
24
|
%Q{+ Headers
|
data/test/metadata_test.rb
CHANGED
@@ -2,11 +2,12 @@ require 'minitest/autorun'
|
|
2
2
|
require 'matter_compiler/blueprint'
|
3
3
|
|
4
4
|
class MetadataTest < Minitest::Test
|
5
|
-
AST_HASH =
|
6
|
-
|
7
|
-
:
|
5
|
+
AST_HASH = [
|
6
|
+
{
|
7
|
+
:name => "FORMAT",
|
8
|
+
:value => "1A"
|
8
9
|
}
|
9
|
-
|
10
|
+
]
|
10
11
|
|
11
12
|
BLUEPRINT = \
|
12
13
|
%Q{FORMAT: 1A
|
data/test/parameters_test.rb
CHANGED
@@ -2,19 +2,25 @@ require 'minitest/autorun'
|
|
2
2
|
require 'matter_compiler/blueprint'
|
3
3
|
|
4
4
|
class ParametersTest < Minitest::Test
|
5
|
-
AST_HASH =
|
6
|
-
|
5
|
+
AST_HASH = [
|
6
|
+
{
|
7
|
+
:name => "id",
|
7
8
|
:description => "Lorem\nIpsum\n",
|
8
9
|
:type => "number",
|
9
10
|
:required => false,
|
10
11
|
:default => "42",
|
11
12
|
:example => "1000",
|
12
|
-
:values => [
|
13
|
+
:values => [
|
14
|
+
{ :value => "42" },
|
15
|
+
{ :value => "1000"},
|
16
|
+
{ :value => "1AM4646"}
|
17
|
+
]
|
13
18
|
}
|
14
|
-
|
19
|
+
]
|
15
20
|
|
16
|
-
AST_HASH_MANY =
|
17
|
-
|
21
|
+
AST_HASH_MANY = [
|
22
|
+
{
|
23
|
+
:name => "id",
|
18
24
|
:description => "Lorem",
|
19
25
|
:type => nil,
|
20
26
|
:required => true,
|
@@ -22,7 +28,8 @@ class ParametersTest < Minitest::Test
|
|
22
28
|
:example => nil,
|
23
29
|
:values => nil
|
24
30
|
},
|
25
|
-
|
31
|
+
{
|
32
|
+
:name => "search",
|
26
33
|
:description => "Ipsum",
|
27
34
|
:type => nil,
|
28
35
|
:required => true,
|
@@ -30,7 +37,7 @@ class ParametersTest < Minitest::Test
|
|
30
37
|
:example => nil,
|
31
38
|
:values => nil
|
32
39
|
}
|
33
|
-
|
40
|
+
]
|
34
41
|
|
35
42
|
BLUEPRINT = \
|
36
43
|
%Q{+ Parameters
|
@@ -61,17 +68,17 @@ class ParametersTest < Minitest::Test
|
|
61
68
|
|
62
69
|
parameter = parameters.collection[0]
|
63
70
|
assert_equal :id.to_s, parameter.name
|
64
|
-
assert_equal ParametersTest::AST_HASH[
|
65
|
-
assert_equal ParametersTest::AST_HASH[
|
71
|
+
assert_equal ParametersTest::AST_HASH[0][:description], parameter.description
|
72
|
+
assert_equal ParametersTest::AST_HASH[0][:type], parameter.type
|
66
73
|
assert_equal :optional, parameter.use
|
67
|
-
assert_equal ParametersTest::AST_HASH[
|
68
|
-
assert_equal ParametersTest::AST_HASH[
|
74
|
+
assert_equal ParametersTest::AST_HASH[0][:default], parameter.default_value
|
75
|
+
assert_equal ParametersTest::AST_HASH[0][:example], parameter.example_value
|
69
76
|
|
70
77
|
assert_instance_of Array, parameter.values
|
71
|
-
assert_equal ParametersTest::AST_HASH[
|
72
|
-
assert_equal ParametersTest::AST_HASH[
|
73
|
-
assert_equal ParametersTest::AST_HASH[
|
74
|
-
assert_equal ParametersTest::AST_HASH[
|
78
|
+
assert_equal ParametersTest::AST_HASH[0][:values].length, parameter.values.length
|
79
|
+
assert_equal ParametersTest::AST_HASH[0][:values][0][:value], parameter.values[0]
|
80
|
+
assert_equal ParametersTest::AST_HASH[0][:values][1][:value], parameter.values[1]
|
81
|
+
assert_equal ParametersTest::AST_HASH[0][:values][2][:value], parameter.values[2]
|
75
82
|
end
|
76
83
|
|
77
84
|
def test_serialize
|
data/test/payload_test.rb
CHANGED
@@ -144,7 +144,7 @@ class ResponseTest < Minitest::Test
|
|
144
144
|
|
145
145
|
assert_instance_of MatterCompiler::Headers, response.headers
|
146
146
|
assert_instance_of Array, response.headers.collection
|
147
|
-
assert_equal HeadersTest::AST_HASH.
|
147
|
+
assert_equal HeadersTest::AST_HASH.length, response.headers.collection.length
|
148
148
|
|
149
149
|
assert_equal ResponseTest::AST_HASH[:body], response.body
|
150
150
|
assert_equal ResponseTest::AST_HASH[:schema], response.schema
|
data/test/resource_test.rb
CHANGED
@@ -12,7 +12,6 @@ class ResourceTest < Minitest::Test
|
|
12
12
|
:uriTemplate => "/my-resource/{id}",
|
13
13
|
:model => ModelTest::AST_HASH,
|
14
14
|
:parameters => ParametersTest::AST_HASH,
|
15
|
-
:headers => HeadersTest::AST_HASH,
|
16
15
|
:actions => [ActionTest::AST_HASH]
|
17
16
|
}
|
18
17
|
|
@@ -20,7 +19,7 @@ class ResourceTest < Minitest::Test
|
|
20
19
|
%Q{## My Resource [/my-resource/{id}]
|
21
20
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
22
21
|
|
23
|
-
#{ModelTest::BLUEPRINT}#{ParametersTest::BLUEPRINT}#{
|
22
|
+
#{ModelTest::BLUEPRINT}#{ParametersTest::BLUEPRINT}#{ActionTest::BLUEPRINT}}
|
24
23
|
|
25
24
|
def test_from_ast_hash
|
26
25
|
resource = MatterCompiler::Resource.new(ResourceTest::AST_HASH)
|
@@ -33,11 +32,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
|
33
32
|
|
34
33
|
assert_instance_of MatterCompiler::Parameters, resource.parameters
|
35
34
|
assert_instance_of Array, resource.parameters.collection
|
36
|
-
assert_equal ParametersTest::AST_HASH.
|
37
|
-
|
38
|
-
assert_instance_of MatterCompiler::Headers, resource.headers
|
39
|
-
assert_instance_of Array, resource.headers.collection
|
40
|
-
assert_equal HeadersTest::AST_HASH.keys.length, resource.headers.collection.length
|
35
|
+
assert_equal ParametersTest::AST_HASH.length, resource.parameters.collection.length
|
41
36
|
|
42
37
|
assert_instance_of Array, resource.actions
|
43
38
|
assert_equal ResourceTest::AST_HASH[:actions].length, resource.actions.length
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: matter_compiler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zdenek Nemec
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|