matter_compiler 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +13 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +47 -0
- data/LICENSE +22 -0
- data/README.md +37 -0
- data/Rakefile +21 -0
- data/bin/matter_compiler +3 -0
- data/features/compose.feature +364 -0
- data/features/step_definitions/file_content_step.rb +8 -0
- data/features/support/setup.rb +1 -0
- data/lib/matter_compiler/blueprint.rb +522 -0
- data/lib/matter_compiler/cli.rb +82 -0
- data/lib/matter_compiler/composer.rb +89 -0
- data/lib/matter_compiler/version.rb +3 -0
- data/lib/matter_compiler.rb +5 -0
- data/lib/object.rb +17 -0
- data/matter_compiler.gemspec +28 -0
- data/test/action_test.rb +48 -0
- data/test/blueprint_test.rb +43 -0
- data/test/composer_test.rb +10 -0
- data/test/headers_test.rb +75 -0
- data/test/metadata_test.rb +28 -0
- data/test/parameters_test.rb +84 -0
- data/test/payload_test.rb +166 -0
- data/test/resource_group_test.rb +32 -0
- data/test/resource_test.rb +53 -0
- data/test/transaction_example_test.rb +38 -0
- metadata +143 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'matter_compiler/composer'
|
3
|
+
require 'matter_compiler/version'
|
4
|
+
|
5
|
+
module MatterCompiler
|
6
|
+
|
7
|
+
class CLI
|
8
|
+
|
9
|
+
attr_reader :command
|
10
|
+
|
11
|
+
def self.start
|
12
|
+
cli = CLI.new
|
13
|
+
options = cli.parse_options!(ARGV)
|
14
|
+
cli.runCommand(ARGV, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def runCommand(args, options)
|
18
|
+
command = :compose if args.first.nil? || @command.nil?
|
19
|
+
command = @command if @command
|
20
|
+
|
21
|
+
if command == :compose && args.first.nil? && (options[:format].nil? || options[:format] == :unknown_ast)
|
22
|
+
|
23
|
+
print options[:format] ? "invalid value of" : "missing"
|
24
|
+
print " '--format option'\n\n"
|
25
|
+
|
26
|
+
CLI.help
|
27
|
+
exit 1
|
28
|
+
end
|
29
|
+
|
30
|
+
case command
|
31
|
+
when :compose
|
32
|
+
Composer.compose(args.first, options[:format])
|
33
|
+
when :version
|
34
|
+
puts MatterCompiler::VERSION
|
35
|
+
else
|
36
|
+
CLI.help
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_options!(args)
|
42
|
+
@command = nil
|
43
|
+
options = {}
|
44
|
+
options_parser = OptionParser.new do |opts|
|
45
|
+
opts.on('-f', '--format (yaml|json)') do |format|
|
46
|
+
options[:format] = Composer.parse_format(format)
|
47
|
+
@command = :compose
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on('-v', '--version') do
|
51
|
+
@command = :version
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.on( '-h', '--help') do
|
55
|
+
@command = :help
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
options_parser.parse!
|
60
|
+
options
|
61
|
+
|
62
|
+
rescue OptionParser::InvalidOption => e
|
63
|
+
puts e
|
64
|
+
CLI.help
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.help
|
69
|
+
puts "Usage: matter_compiler [options] [<ast file>]"
|
70
|
+
puts "\nCompose an API blueprint from its AST."
|
71
|
+
puts "If no <ast file> is specified 'matter_compiler' will listen on stdin."
|
72
|
+
|
73
|
+
puts "\nOptions:\n\n"
|
74
|
+
puts "\t-f, --format (yaml|json) Set the AST media-type format of the input"
|
75
|
+
puts "\t-h, --help Show this help"
|
76
|
+
puts "\t-v, --version Show version"
|
77
|
+
puts "\n"
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
require 'matter_compiler/blueprint'
|
4
|
+
require 'object'
|
5
|
+
|
6
|
+
module MatterCompiler
|
7
|
+
|
8
|
+
class Composer
|
9
|
+
|
10
|
+
# Read AST file
|
11
|
+
def self.read_file(file)
|
12
|
+
unless File.readable?(file)
|
13
|
+
abort "Unable to read input ast file: #{file.inspect}"
|
14
|
+
end
|
15
|
+
input = File.read(file)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Read AST from stdin.
|
19
|
+
def self.read_stdin
|
20
|
+
input = $stdin.read
|
21
|
+
end
|
22
|
+
|
23
|
+
# Parse format from string
|
24
|
+
def self.parse_format(format)
|
25
|
+
format.downcase!
|
26
|
+
case format
|
27
|
+
when "json"
|
28
|
+
return :json_ast
|
29
|
+
when "yml"
|
30
|
+
when "yaml"
|
31
|
+
return :yaml_ast
|
32
|
+
else
|
33
|
+
return :unknown_ast
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Guess format from filename extension.
|
38
|
+
def self.guess_format(file)
|
39
|
+
extension = File.extname(file)
|
40
|
+
if extension.length < 1
|
41
|
+
return :unknown_ast
|
42
|
+
end
|
43
|
+
|
44
|
+
self.parse_format(extension[1..-1])
|
45
|
+
end
|
46
|
+
|
47
|
+
# Compose API Blueprint from an AST file.
|
48
|
+
# Returns a string with composed API Blueprint.
|
49
|
+
def self.compose(file = nil, format = nil)
|
50
|
+
# Read input
|
51
|
+
input = nil
|
52
|
+
if file.nil?
|
53
|
+
input = self.read_stdin
|
54
|
+
else
|
55
|
+
input = self.read_file(file)
|
56
|
+
end
|
57
|
+
|
58
|
+
if input.blank?
|
59
|
+
puts "Empty input"
|
60
|
+
exit
|
61
|
+
end
|
62
|
+
|
63
|
+
# Parse input
|
64
|
+
input_format = format ? format : self.guess_format(file)
|
65
|
+
ast_hash = nil;
|
66
|
+
case input_format
|
67
|
+
when :json_ast
|
68
|
+
ast_hash = JSON.parse(input).deep_symbolize_keys
|
69
|
+
when :yaml_ast
|
70
|
+
ast_hash = YAML.load(input).deep_symbolize_keys
|
71
|
+
else
|
72
|
+
abort "Undefined input format"
|
73
|
+
end
|
74
|
+
|
75
|
+
# Check version of the AST
|
76
|
+
unless Blueprint::SUPPORTED_VERSIONS.include?(ast_hash[Blueprint::VERSION_KEY].to_s)
|
77
|
+
abort("unsupported AST version: '#{ast_hash[Blueprint::VERSION_KEY]}'\n")
|
78
|
+
end
|
79
|
+
|
80
|
+
# Process the AST hash
|
81
|
+
blueprint = Blueprint.new(ast_hash)
|
82
|
+
|
83
|
+
# TODO: use $stdout for now, add serialization options later
|
84
|
+
puts blueprint.serialize
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
data/lib/object.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Essential extensions to base object class
|
2
|
+
# used throughout MatterCompiler
|
3
|
+
|
4
|
+
class Object
|
5
|
+
|
6
|
+
# Symbolizes keys of a hash
|
7
|
+
def deep_symbolize_keys
|
8
|
+
return self.inject({}){|memo, (k,v)| memo[k.to_sym] = v.deep_symbolize_keys; memo} if self.is_a? Hash
|
9
|
+
return self.inject([]){|memo, v | memo << v.deep_symbolize_keys; memo} if self.is_a? Array
|
10
|
+
return self
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns true if object is nil or empty, false otherwise
|
14
|
+
def blank?
|
15
|
+
respond_to?(:empty?) ? empty? : !self
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'matter_compiler/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "matter_compiler"
|
8
|
+
spec.version = MatterCompiler::VERSION
|
9
|
+
spec.authors = ["Zdenek Nemec"]
|
10
|
+
spec.email = ["z@apiary.io"]
|
11
|
+
spec.summary = %q{API Blueprint AST to API Blueprint convertor}
|
12
|
+
spec.description = %q{Matter Compiler is a API Blueprint AST Media Types to API Blueprint conversion tool. It composes an API blueprint from its serialzed AST media-type.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "cucumber"
|
24
|
+
spec.add_development_dependency "minitest"
|
25
|
+
|
26
|
+
# Use latest aruba for STDIN capabilities, see the Gemfile
|
27
|
+
# spec.add_development_dependency "aruba"
|
28
|
+
end
|
data/test/action_test.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'matter_compiler/blueprint'
|
3
|
+
require_relative 'parameters_test'
|
4
|
+
require_relative 'headers_test'
|
5
|
+
require_relative 'transaction_example_test'
|
6
|
+
|
7
|
+
class ActionTest < Minitest::Unit::TestCase
|
8
|
+
AST_HASH = {
|
9
|
+
:name => "Into Action",
|
10
|
+
:description => "Dolor sit amet\n\n",
|
11
|
+
:method => "GET",
|
12
|
+
:parameters => ParametersTest::AST_HASH,
|
13
|
+
:headers => HeadersTest::AST_HASH,
|
14
|
+
:examples => [TransactionExampleTest::AST_HASH]
|
15
|
+
}
|
16
|
+
|
17
|
+
BLUEPRINT = \
|
18
|
+
%Q{### Into Action [GET]
|
19
|
+
Dolor sit amet
|
20
|
+
|
21
|
+
#{ParametersTest::BLUEPRINT}#{HeadersTest::BLUEPRINT}#{TransactionExampleTest::BLUEPRINT}}
|
22
|
+
|
23
|
+
def test_from_ast_hash
|
24
|
+
action = MatterCompiler::Action.new(ActionTest::AST_HASH)
|
25
|
+
assert_equal ActionTest::AST_HASH[:name], action.name
|
26
|
+
assert_equal ActionTest::AST_HASH[:description], action.description
|
27
|
+
assert_equal ActionTest::AST_HASH[:method], action.method
|
28
|
+
|
29
|
+
assert_instance_of MatterCompiler::Parameters, action.parameters
|
30
|
+
assert_instance_of Array, action.parameters.collection
|
31
|
+
assert_equal ParametersTest::AST_HASH.keys.length, action.parameters.collection.length
|
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
|
36
|
+
|
37
|
+
assert_instance_of Array, action.examples
|
38
|
+
assert_equal ActionTest::AST_HASH[:examples].length, action.examples.length
|
39
|
+
assert action.examples.length > 0
|
40
|
+
assert_instance_of MatterCompiler::TransactionExample, action.examples[0]
|
41
|
+
assert_equal TransactionExampleTest::AST_HASH[:name], action.examples[0].name
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_serialize
|
45
|
+
action = MatterCompiler::Action.new(ActionTest::AST_HASH)
|
46
|
+
assert_equal ActionTest::BLUEPRINT, action.serialize
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'matter_compiler/blueprint'
|
3
|
+
require_relative 'resource_group_test'
|
4
|
+
require_relative 'metadata_test'
|
5
|
+
|
6
|
+
class BlueprintTest < Minitest::Unit::TestCase
|
7
|
+
AST_HASH = {
|
8
|
+
:_version => 1.0,
|
9
|
+
:metadata => MetadataTest::AST_HASH,
|
10
|
+
:name => "My API",
|
11
|
+
:description => "Lorem Ipsum\n\n",
|
12
|
+
:resourceGroups => [ ResourceGroupTest::AST_HASH ]
|
13
|
+
}
|
14
|
+
|
15
|
+
BLUEPRINT = \
|
16
|
+
%Q{#{MetadataTest::BLUEPRINT}# My API
|
17
|
+
Lorem Ipsum
|
18
|
+
|
19
|
+
#{ResourceGroupTest::BLUEPRINT}}
|
20
|
+
|
21
|
+
def test_from_ast_hash
|
22
|
+
blueprint = MatterCompiler::Blueprint.new(BlueprintTest::AST_HASH)
|
23
|
+
|
24
|
+
assert_equal BlueprintTest::AST_HASH[:name], blueprint.name
|
25
|
+
assert_equal BlueprintTest::AST_HASH[:description], blueprint.description
|
26
|
+
|
27
|
+
assert_instance_of Array, blueprint.resource_groups
|
28
|
+
assert_equal BlueprintTest::AST_HASH[:resourceGroups].length, blueprint.resource_groups.length
|
29
|
+
assert_equal ResourceGroupTest::AST_HASH[:name], blueprint.resource_groups[0].name
|
30
|
+
|
31
|
+
assert_instance_of MatterCompiler::Metadata, blueprint.metadata
|
32
|
+
assert_instance_of Array, blueprint.metadata.collection
|
33
|
+
assert_equal BlueprintTest::AST_HASH[:metadata].keys.length, blueprint.metadata.collection.length
|
34
|
+
assert_equal BlueprintTest::AST_HASH[:metadata].keys[0], blueprint.metadata.collection[0].keys[0]
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_serialize
|
38
|
+
blueprint = MatterCompiler::Blueprint.new(BlueprintTest::AST_HASH)
|
39
|
+
assert_equal BlueprintTest::BLUEPRINT, blueprint.serialize
|
40
|
+
|
41
|
+
#puts "\n\n>>>\n#{blueprint.serialize}"
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'matter_compiler/composer'
|
3
|
+
|
4
|
+
class ComposerTest < Minitest::Unit::TestCase
|
5
|
+
def test_guess_format
|
6
|
+
assert_equal :yaml_ast, MatterCompiler::Composer.guess_format('test.yaml')
|
7
|
+
assert_equal :json_ast, MatterCompiler::Composer.guess_format('test.json')
|
8
|
+
assert_equal :unknown_ast, MatterCompiler::Composer.guess_format('test.txt')
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'matter_compiler/blueprint'
|
3
|
+
|
4
|
+
class HeadersTest < Minitest::Unit::TestCase
|
5
|
+
AST_HASH = {
|
6
|
+
:'X-Header' => {
|
7
|
+
:value => "1"
|
8
|
+
},
|
9
|
+
:'Content-Type' => {
|
10
|
+
:value => "text/plain"
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
AST_HASH_CONTENT_ONLY = {
|
15
|
+
:'Content-Type' => {
|
16
|
+
:value => "text/plain"
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
BLUEPRINT = \
|
21
|
+
%Q{+ Headers
|
22
|
+
|
23
|
+
X-Header: 1
|
24
|
+
Content-Type: text/plain
|
25
|
+
|
26
|
+
}
|
27
|
+
|
28
|
+
BLUEPRINT_IGNORE_CONTENT = \
|
29
|
+
%Q{+ Headers
|
30
|
+
|
31
|
+
X-Header: 1
|
32
|
+
|
33
|
+
}
|
34
|
+
|
35
|
+
BLUEPRINT_CONTENT_ONLY = \
|
36
|
+
%Q{+ Headers
|
37
|
+
|
38
|
+
Content-Type: text/plain
|
39
|
+
|
40
|
+
}
|
41
|
+
|
42
|
+
BLUEPRINT_IGNORE_CONTENT_CONTENT_ONLY = %Q{}
|
43
|
+
|
44
|
+
BLUEPRINT_NESTED_IGNORE_CONTENT = \
|
45
|
+
%Q{ + Headers
|
46
|
+
|
47
|
+
X-Header: 1
|
48
|
+
|
49
|
+
}
|
50
|
+
|
51
|
+
def test_from_ast_hash
|
52
|
+
headers = MatterCompiler::Headers.new(HeadersTest::AST_HASH)
|
53
|
+
assert_equal 2, headers.collection.length
|
54
|
+
|
55
|
+
assert_instance_of Hash, headers.collection[0]
|
56
|
+
assert_equal :'X-Header', headers.collection[0].keys[0]
|
57
|
+
assert_equal "1", headers.collection[0][:'X-Header']
|
58
|
+
|
59
|
+
assert_instance_of Hash, headers.collection[1]
|
60
|
+
assert_equal :'Content-Type', headers.collection[1].keys[0]
|
61
|
+
assert_equal "text/plain", headers.collection[1][:'Content-Type']
|
62
|
+
|
63
|
+
assert_equal "text/plain", headers.content_type
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_serialize
|
67
|
+
headers = MatterCompiler::Headers.new(HeadersTest::AST_HASH)
|
68
|
+
assert_equal HeadersTest::BLUEPRINT, headers.serialize(0)
|
69
|
+
assert_equal HeadersTest::BLUEPRINT_IGNORE_CONTENT, headers.serialize(0, [MatterCompiler::Headers::CONTENT_TYPE_HEADER_KEY])
|
70
|
+
|
71
|
+
headers = MatterCompiler::Headers.new(HeadersTest::AST_HASH_CONTENT_ONLY)
|
72
|
+
assert_equal HeadersTest::BLUEPRINT_CONTENT_ONLY, headers.serialize(0)
|
73
|
+
assert_equal HeadersTest::BLUEPRINT_IGNORE_CONTENT_CONTENT_ONLY, headers.serialize(0, [MatterCompiler::Headers::CONTENT_TYPE_HEADER_KEY])
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'matter_compiler/blueprint'
|
3
|
+
|
4
|
+
class MetadataTest < Minitest::Unit::TestCase
|
5
|
+
AST_HASH = {
|
6
|
+
:FORMAT => {
|
7
|
+
:value => "1A"
|
8
|
+
}
|
9
|
+
}
|
10
|
+
|
11
|
+
BLUEPRINT = \
|
12
|
+
%Q{FORMAT: 1A
|
13
|
+
|
14
|
+
}
|
15
|
+
|
16
|
+
def test_from_ast_hash
|
17
|
+
metadata = MatterCompiler::Metadata.new(MetadataTest::AST_HASH)
|
18
|
+
assert_equal 1, metadata.collection.length
|
19
|
+
assert_instance_of Hash, metadata.collection[0]
|
20
|
+
assert_equal :FORMAT, metadata.collection[0].keys[0]
|
21
|
+
assert_equal "1A", metadata.collection[0][:FORMAT]
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_serialize
|
25
|
+
metadata = MatterCompiler::Metadata.new(MetadataTest::AST_HASH)
|
26
|
+
assert_equal MetadataTest::BLUEPRINT, metadata.serialize
|
27
|
+
end
|
28
|
+
end
|