matter_compiler 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ Then /^the output should contain the content file "(.*)"$/ do |filename|
2
+ expected = nil
3
+ in_current_dir do
4
+ expected = File.read(filename)
5
+ end
6
+
7
+ assert_partial_output(expected, all_output)
8
+ end
@@ -0,0 +1 @@
1
+ require 'aruba/cucumber'
@@ -0,0 +1,522 @@
1
+ module MatterCompiler
2
+
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
+ # Blueprint AST node
9
+ #
10
+ class BlueprintNode
11
+ ONE_INDENTATION_LEVEL = " "
12
+
13
+ def initialize(hash = nil)
14
+ load_ast_hash!(hash) if hash
15
+ end
16
+
17
+ # Load AST has into block
18
+ def load_ast_hash!(hash)
19
+ end
20
+
21
+ # Serialize block to a Markdown string
22
+ # \param level ... optional requested indentation level
23
+ def serialize(level = 0)
24
+ end
25
+ end
26
+
27
+ #
28
+ # Named Blueprint AST node
29
+ #
30
+ class NamedBlueprintNode < BlueprintNode
31
+ def load_ast_hash!(hash)
32
+ @name = hash[:name]
33
+ @description = hash[:description]
34
+ end
35
+ end
36
+
37
+ #
38
+ # Key-value collection
39
+ #
40
+ class KeyValueCollection < BlueprintNode
41
+ attr_accessor :collection
42
+
43
+ def load_ast_hash!(hash)
44
+ return if hash.empty?
45
+
46
+ @collection = Array.new
47
+ hash.each do |key, value_hash|
48
+ @collection << Hash[key, value_hash[:value]]
49
+ end
50
+ end
51
+
52
+ # Serialize key value pairs
53
+ # \param ignore_keys ... optional array of keys to NOT be serialized
54
+ def serialize(level = 0, ignore_keys = nil)
55
+ buffer = ""
56
+ @collection.each do |hash|
57
+ # Skip ignored keys
58
+ unless ignore_keys && ignore_keys.include?(hash.keys.first)
59
+ level.times { buffer << ONE_INDENTATION_LEVEL }
60
+ buffer << "#{hash.keys.first}: #{hash.values.first}\n"
61
+ end
62
+ end
63
+
64
+ buffer << "\n" unless buffer.empty?
65
+ buffer
66
+ end
67
+
68
+ # Returns collection without ignored keys
69
+ def ignore(ignore_keys)
70
+ return @collection if ignore_keys.blank?
71
+ @collection.select { |kv_item| !ignore_keys.include?(kv_item.keys.first) }
72
+ end
73
+ end
74
+
75
+ #
76
+ # Collection of metadata
77
+ #
78
+ class Metadata < KeyValueCollection
79
+ end
80
+
81
+ #
82
+ # Collection of headers
83
+ #
84
+ class Headers < KeyValueCollection
85
+
86
+ CONTENT_TYPE_HEADER_KEY = :'Content-Type'
87
+
88
+ def serialize(level = 0, ignore_keys = nil)
89
+ return "" if @collection.blank? || ignore(ignore_keys).blank?
90
+
91
+ buffer = ""
92
+ level.times { buffer << ONE_INDENTATION_LEVEL }
93
+ buffer << "+ Headers\n\n"
94
+
95
+ buffer << super(level + 2, ignore_keys)
96
+ end
97
+
98
+ # Returns the value of Content-Type header, if any.
99
+ def content_type
100
+ content_type_header = @collection.detect { |header| header.has_key?(CONTENT_TYPE_HEADER_KEY) }
101
+ return (content_type_header.nil?) ? nil : content_type_header[CONTENT_TYPE_HEADER_KEY]
102
+ end
103
+ end;
104
+
105
+ #
106
+ # One URI parameter
107
+ #
108
+ class Parameter < BlueprintNode
109
+ attr_accessor :name
110
+ attr_accessor :description
111
+ attr_accessor :type
112
+ attr_accessor :use
113
+ attr_accessor :default_value
114
+ attr_accessor :example_value
115
+ attr_accessor :values
116
+
117
+ def initialize(name = nil, hash = nil)
118
+ super(hash)
119
+ @name = name.to_s if name
120
+ end
121
+
122
+ def load_ast_hash!(hash)
123
+ @description = hash[:description]
124
+ @type = hash[:type] if hash[:type]
125
+ @use = (hash[:required] && hash[:required] == true) ? :required : :optional
126
+ @default_value = hash[:default] if hash[:default]
127
+ @example_value = hash[:example] if hash[:example]
128
+
129
+ unless hash[:values].blank?
130
+ @values = Array.new
131
+ hash[:values].each { |value| @values << value }
132
+ end
133
+ end
134
+
135
+ def serialize
136
+ # Parameter name
137
+ buffer = "#{ONE_INDENTATION_LEVEL}+ #{@name}"
138
+
139
+ # Default value
140
+ buffer << " = `#{@default_value}`" if @default_value
141
+
142
+ # Attributes
143
+ unless @type.blank? && @example_value.blank? && @use == :required
144
+ attribute_buffer = ""
145
+
146
+ buffer << " ("
147
+
148
+ # Type
149
+ attribute_buffer << @type unless @type.blank?
150
+
151
+ # Use
152
+ if (@use == :optional)
153
+ attribute_buffer << ", " unless attribute_buffer.empty?
154
+ attribute_buffer << "optional"
155
+ end
156
+
157
+ # Example value
158
+ unless (@example_value.blank?)
159
+ attribute_buffer << ", " unless attribute_buffer.empty?
160
+ attribute_buffer << "`#{@example_value}`"
161
+ end
162
+
163
+ buffer << attribute_buffer
164
+ buffer << ")"
165
+ end
166
+
167
+ # Description
168
+ if @description.blank?
169
+ buffer << "\n"
170
+ else
171
+ if @description.lines.count == 1
172
+ # One line description
173
+ buffer << " ... #{@description}"
174
+ buffer << "\n" if @description[-1, 1] != "\n" # Additional newline needed if no provided
175
+ else
176
+ # Multi-line description
177
+ buffer << "\n\n"
178
+ @description.each_line do |line|
179
+ 2.times { buffer << ONE_INDENTATION_LEVEL }
180
+ buffer << "#{line}"
181
+ end
182
+ end
183
+ end
184
+
185
+ # Value
186
+ unless @values.blank?
187
+ buffer << "\n"
188
+ 2.times { buffer << ONE_INDENTATION_LEVEL }
189
+ buffer << "+ Values\n"
190
+ @values.each do |value|
191
+ 3.times { buffer << ONE_INDENTATION_LEVEL }
192
+ buffer << "+ `#{value}`\n"
193
+ end
194
+ end
195
+
196
+ buffer
197
+ end
198
+ end
199
+
200
+ #
201
+ # Collection of URI parameters
202
+ #
203
+ class Parameters < BlueprintNode
204
+ attr_accessor :collection
205
+
206
+ def load_ast_hash!(hash)
207
+ return if hash.empty?
208
+
209
+ @collection = Array.new
210
+ hash.each do |key, value_hash|
211
+ @collection << Parameter.new(key, value_hash)
212
+ end
213
+ end
214
+
215
+ def serialize
216
+ return "" if :collection.blank?
217
+
218
+ buffer = "+ Parameters\n"
219
+ @collection.each do |parameter|
220
+ buffer << parameter.serialize
221
+ end
222
+
223
+ buffer << "\n" unless @collection.blank?
224
+ buffer
225
+ end
226
+ end
227
+
228
+ #
229
+ # Generic payload base class
230
+ #
231
+ class Payload < NamedBlueprintNode
232
+ attr_accessor :name
233
+ attr_accessor :description
234
+ attr_accessor :parameters
235
+ attr_accessor :headers
236
+ attr_accessor :body
237
+ attr_accessor :schema
238
+
239
+ def load_ast_hash!(hash)
240
+ super(hash)
241
+
242
+ @headers = Headers.new(hash[:headers]) unless hash[:headers].blank?
243
+ @body = hash[:body] unless hash[:body].blank?
244
+ @schema = hash[:schema] unless hash[:schema].blank?
245
+ end
246
+
247
+ def serialize
248
+ # Name is serialized in Payload successors
249
+ buffer = ""
250
+
251
+ unless @description.blank?
252
+ buffer << "\n"
253
+ @description.each_line { |line| buffer << "#{ONE_INDENTATION_LEVEL}#{line}" }
254
+ buffer << "\n"
255
+ end
256
+
257
+ unless @headers.blank?
258
+ buffer << @headers.serialize(1, [Headers::CONTENT_TYPE_HEADER_KEY])
259
+ end
260
+
261
+ unless @body.blank?
262
+ abbreviated_synax = (headers.blank? || headers.ignore([Headers::CONTENT_TYPE_HEADER_KEY]).blank?) \
263
+ & description.blank? \
264
+ & schema.blank?
265
+ asset_indent_level = 2
266
+ unless abbreviated_synax
267
+ asset_indent_level = 3
268
+ buffer << "#{ONE_INDENTATION_LEVEL}+ Body\n"
269
+ end
270
+ buffer << "\n"
271
+
272
+ @body.each_line do |line|
273
+ asset_indent_level.times { buffer << ONE_INDENTATION_LEVEL }
274
+ buffer << "#{line}"
275
+ end
276
+ buffer << "\n"
277
+ end
278
+
279
+ unless @schema.blank?
280
+ buffer << "#{ONE_INDENTATION_LEVEL}+ Schema\n\n"
281
+ @schema.each_line do |line|
282
+ 3.times { buffer << ONE_INDENTATION_LEVEL }
283
+ buffer << "#{line}"
284
+ end
285
+ buffer << "\n"
286
+ end
287
+
288
+ buffer << "\n" if buffer.empty? # Separate empty payloads by a newline
289
+
290
+ buffer
291
+ end
292
+
293
+ # Serialize payload section lead-in (begin)
294
+ # \param section ... section keyword name
295
+ # \param ignore_name ... true to ignore section's name, false otherwise
296
+ def serialize_lead_in(section, ignore_name = false)
297
+ buffer = ""
298
+ buffer << "+ #{section}"
299
+ buffer << " #{@name}" unless ignore_name || @name.blank?
300
+
301
+ unless @headers.blank? || @headers.content_type.blank?
302
+ buffer << " (#{@headers.content_type})"
303
+ end
304
+
305
+ buffer << "\n"
306
+ end
307
+ end
308
+
309
+ #
310
+ # Model Payload
311
+ #
312
+ class Model < Payload
313
+ def serialize
314
+ buffer = serialize_lead_in("Model", true)
315
+ buffer << super
316
+ end
317
+ end
318
+
319
+ #
320
+ # Request Payload
321
+ #
322
+ class Request < Payload
323
+ def serialize
324
+ buffer = serialize_lead_in("Request")
325
+ buffer << super
326
+ end
327
+ end
328
+
329
+ #
330
+ # Response Payload
331
+ #
332
+ class Response < Payload;
333
+ def serialize
334
+ buffer = serialize_lead_in("Response")
335
+ buffer << super
336
+ end
337
+ end
338
+
339
+ #
340
+ # Transaction Example
341
+ #
342
+ class TransactionExample < NamedBlueprintNode
343
+ attr_accessor :name
344
+ attr_accessor :description
345
+ attr_accessor :requests
346
+ attr_accessor :responses
347
+
348
+ def load_ast_hash!(hash)
349
+ super(hash)
350
+
351
+ unless hash[:requests].blank?
352
+ @requests = Array.new
353
+ hash[:requests].each { |request_hash| @requests << Request.new(request_hash) }
354
+ end
355
+
356
+ unless hash[:responses].blank?
357
+ @responses = Array.new
358
+ hash[:responses].each { |response_hash| @responses << Response.new(response_hash) }
359
+ end
360
+ end
361
+
362
+ def serialize
363
+ buffer = ""
364
+ @requests.each { |request| buffer << request.serialize } unless @requests.nil?
365
+ @responses.each { |response| buffer << response.serialize } unless @responses.nil?
366
+ buffer
367
+ end
368
+ end
369
+
370
+ #
371
+ # Action
372
+ #
373
+ class Action < NamedBlueprintNode
374
+ attr_accessor :method
375
+ attr_accessor :name
376
+ attr_accessor :description
377
+ attr_accessor :parameters
378
+ attr_accessor :headers
379
+ attr_accessor :examples
380
+
381
+ def load_ast_hash!(hash)
382
+ super(hash)
383
+
384
+ @method = hash[:method]
385
+ @parameters = Parameters.new(hash[:parameters]) unless hash[:parameters].blank?
386
+ @headers = Headers.new(hash[:headers]) unless hash[:headers].blank?
387
+
388
+ unless hash[:examples].blank?
389
+ @examples = Array.new
390
+ hash[:examples].each { |example_hash| @examples << TransactionExample.new(example_hash) }
391
+ end
392
+ end
393
+
394
+ def serialize
395
+ buffer = ""
396
+ if @name.blank?
397
+ buffer << "### #{@method}\n"
398
+ else
399
+ buffer << "### #{@name} [#{@method}]\n"
400
+ end
401
+
402
+ buffer << "#{@description}" unless @description.blank?
403
+
404
+ buffer << @parameters.serialize unless @parameters.nil?
405
+ buffer << @headers.serialize unless @headers.nil?
406
+
407
+ @examples.each { |example| buffer << example.serialize } unless @examples.nil?
408
+ buffer
409
+ end
410
+ end
411
+
412
+ #
413
+ # Resource
414
+ #
415
+ class Resource < NamedBlueprintNode
416
+ attr_accessor :uri_template
417
+ attr_accessor :name
418
+ attr_accessor :description
419
+ attr_accessor :model
420
+ attr_accessor :parameters
421
+ attr_accessor :headers
422
+ attr_accessor :actions
423
+
424
+ def load_ast_hash!(hash)
425
+ super(hash)
426
+
427
+ @uri_template = hash[:uriTemplate]
428
+ @model = Model.new(hash[:model]) unless hash[:model].blank?
429
+ @parameters = Parameters.new(hash[:parameters]) unless hash[:parameters].blank?
430
+ @headers = Headers.new(hash[:headers]) unless hash[:headers].blank?
431
+
432
+ unless hash[:actions].blank?
433
+ @actions = Array.new
434
+ hash[:actions].each { |action_hash| @actions << Action.new(action_hash) }
435
+ end
436
+ end
437
+
438
+ def serialize
439
+ buffer = ""
440
+ if @name.blank?
441
+ buffer << "## #{@uri_template}\n"
442
+ else
443
+ buffer << "## #{@name} [#{@uri_template}]\n"
444
+ end
445
+
446
+ buffer << "#{@description}" unless @description.blank?
447
+
448
+ buffer << @model.serialize unless @model.nil?
449
+ buffer << @parameters.serialize unless @parameters.nil?
450
+ buffer << @headers.serialize unless @headers.nil?
451
+
452
+ @actions.each { |action| buffer << action.serialize } unless @actions.nil?
453
+ buffer
454
+ end
455
+ end
456
+
457
+ #
458
+ # Resource Group
459
+ #
460
+ class ResourceGroup < NamedBlueprintNode
461
+ attr_accessor :name
462
+ attr_accessor :description
463
+ attr_accessor :resources
464
+
465
+ def load_ast_hash!(hash)
466
+ super(hash)
467
+
468
+ unless hash[:resources].blank?
469
+ @resources = Array.new
470
+ hash[:resources].each { |resource_hash| @resources << Resource.new(resource_hash) }
471
+ end
472
+ end
473
+
474
+ def serialize
475
+ buffer = ""
476
+ buffer << "# Group #{@name}\n" unless @name.blank?
477
+ buffer << "#{@description}" unless @description.blank?
478
+
479
+ @resources.each { |resource| buffer << resource.serialize } unless @resources.nil?
480
+ buffer
481
+ end
482
+ end
483
+
484
+ #
485
+ # Blueprint
486
+ #
487
+ class Blueprint < NamedBlueprintNode
488
+ attr_accessor :metadata
489
+ attr_accessor :name
490
+ attr_accessor :description
491
+ attr_accessor :resource_groups
492
+
493
+ VERSION_KEY = :_version
494
+ SUPPORTED_VERSIONS = ["1.0"]
495
+
496
+ def load_ast_hash!(hash)
497
+ super(hash)
498
+
499
+ # Load Metadata
500
+ unless hash[:metadata].blank?
501
+ @metadata = Metadata.new(hash[:metadata])
502
+ end
503
+
504
+ # Load Resource Groups
505
+ unless hash[:resourceGroups].blank?
506
+ @resource_groups = Array.new
507
+ hash[:resourceGroups].each { |group_hash| @resource_groups << ResourceGroup.new(group_hash) }
508
+ end
509
+ end
510
+
511
+ def serialize
512
+ buffer = ""
513
+ buffer << "#{@metadata.serialize}" unless @metadata.nil?
514
+ buffer << "# #{@name}\n" unless @name.blank?
515
+ buffer << "#{@description}" unless @description.blank?
516
+
517
+ @resource_groups.each { |group| buffer << group.serialize } unless @resource_groups.nil?
518
+ buffer
519
+ end
520
+
521
+ end
522
+ end