matter_compiler 0.1.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 +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
|