service_contract 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +81 -0
  7. data/Rakefile +13 -0
  8. data/lib/service_contract/abstract_endpoint.rb +11 -0
  9. data/lib/service_contract/abstract_field.rb +3 -0
  10. data/lib/service_contract/abstract_parameter.rb +19 -0
  11. data/lib/service_contract/abstract_protocol.rb +19 -0
  12. data/lib/service_contract/abstract_service.rb +28 -0
  13. data/lib/service_contract/abstract_type.rb +15 -0
  14. data/lib/service_contract/assertions.rb +44 -0
  15. data/lib/service_contract/avro/array_type.rb +17 -0
  16. data/lib/service_contract/avro/documentation.rb +34 -0
  17. data/lib/service_contract/avro/endpoint.rb +63 -0
  18. data/lib/service_contract/avro/errors.rb +7 -0
  19. data/lib/service_contract/avro/parameter.rb +23 -0
  20. data/lib/service_contract/avro/protocol.rb +39 -0
  21. data/lib/service_contract/avro/service.rb +45 -0
  22. data/lib/service_contract/avro/type.rb +31 -0
  23. data/lib/service_contract/avro/views/homepage.slim +7 -0
  24. data/lib/service_contract/avro/views/layout.slim +17 -0
  25. data/lib/service_contract/avro/views/protocol.slim +56 -0
  26. data/lib/service_contract/avro/views/version.slim +12 -0
  27. data/lib/service_contract/avro.rb +14 -0
  28. data/lib/service_contract/tasks.rb +14 -0
  29. data/lib/service_contract/version.rb +3 -0
  30. data/lib/service_contract.rb +15 -0
  31. data/service_contract.gemspec +25 -0
  32. data/src/avro-tools-1.7.7.jar +0 -0
  33. data/test/sample/1/compiled/location.avpr +113 -0
  34. data/test/sample/1/compiled/sales_region.avpr +60 -0
  35. data/test/sample/1/source/location.avdl +43 -0
  36. data/test/sample/1/source/sales_region.avdl +24 -0
  37. data/test/service_test.rb +21 -0
  38. data/test/test_helper.rb +10 -0
  39. metadata +144 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 986435826a76bf34d9367c13d65c992afc45261d
4
+ data.tar.gz: a0ddf05d5d5b1b26d993578a63b3bba561ccee56
5
+ SHA512:
6
+ metadata.gz: c79dc84b3b9e8490f78b96506113f9c83b4687e7ec1f204f9e671f4be197981f3cc5a64a82f86e04bcbe594608e9f73a523cc02e8fe7a6136eca6a6a51f7239e
7
+ data.tar.gz: 78721a558a768fa5c70baa903ecb099717d5641aebe9f86e73ebb764767f95d613c96539276e82d40bea9753d46fbbe6416c552d86eec68c4bc580669ac640a2
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *.gem
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ env:
2
+ global:
3
+ - CODECLIMATE_REPO_TOKEN=2ca11363f82515a172ab992c3e4bacf032fc088c57a9907bcb2bcb9eaa71e344
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in service_contract.gemspec
4
+ gemspec
5
+
6
+ gem 'codeclimate-test-reporter', group: 'test', require: nil
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Jeff Ching
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # ServiceContract [![Build Status](https://travis-ci.org/chingor13/service_contract.png)](https://travis-ci.org/chingor13/service_contract) [![Code Climate](https://codeclimate.com/github/chingor13/service_contract.png)](https://codeclimate.com/github/chingor13/service_contract) [![Code Coverage](https://codeclimate.com/github/chingor13/service_contract/coverage.png)](https://codeclimate.com/github/chingor13/service_contract)
2
+
3
+ The gem tries to abstract the definition of a service's interface contract
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```
10
+ gem 'service_contract'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install service_contract
20
+
21
+ ## Providers
22
+
23
+ ### Avro
24
+
25
+ We can read an Avro protocol file to determine a set of service definitions.
26
+
27
+ Example:
28
+
29
+ ```
30
+ class Gnomon::Contract::Service < ServiceContract::Avro::Service
31
+ def self.data_dir
32
+ return "/path/to/avro_files"
33
+ end
34
+ end
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ We define a service as having many versions with each version having several `Protocol`s. A `Protocol` has many `Endpoint`s.
40
+
41
+ Example:
42
+
43
+ ```
44
+ # a Gnomon::Contract::Service instance (subclass of ServiceContract::AbstractService)
45
+ version = Gnomon::Contract::Service.find(1)
46
+ => <#Gnomon::Contract::Service>
47
+
48
+ protocol = version.protocol("location")
49
+ => <#ServiceContract::Avro::Protocol>
50
+
51
+ protocol.endpoints
52
+ => [<#ServiceContract::Avro::Endpoint>,...]
53
+ ```
54
+
55
+ ## Documentation
56
+
57
+ `service_contract` also provides a nice documentation layer packaged in the form of a [sinatra](http://www.sinatrarb.com/) rack application.
58
+
59
+ ```
60
+ module Gnomon
61
+ module Contract
62
+ class Documentation < ServiceContract::Avro::Documentation
63
+ helpers do
64
+ def service
65
+ Gnomon::Contract::Service
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ # in your rackup config
73
+ run Gnomon::Contract::Documentation
74
+
75
+ # or mount in rails routes
76
+ MyApplication.routes.draw do
77
+ mount Gnomon::Contract::Documentation, at: "docs/"
78
+ end
79
+ ```
80
+
81
+ **Note**: You will have to add `sinatra` and `slim` (templating engine) to your gem dependencies yourself because it's not a core dependency.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.libs << 'lib'
7
+ t.libs << 'test'
8
+ t.pattern = 'test/**/*_test.rb'
9
+ t.verbose = false
10
+ end
11
+
12
+
13
+ task default: :test
@@ -0,0 +1,11 @@
1
+ module ServiceContract
2
+ AbstractEndpoint = Struct.new(:protocol, :definition) do
3
+ def parameters
4
+ []
5
+ end
6
+
7
+ def response_type
8
+ nil
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module ServiceContract
2
+ AbstractField = Struct.new(:type, :name)
3
+ end
@@ -0,0 +1,19 @@
1
+ module ServiceContract
2
+ AbstractParameter = Struct.new(:definition) do
3
+ def name
4
+ raise :not_implemented
5
+ end
6
+
7
+ def type
8
+ raise :not_implemented
9
+ end
10
+
11
+ def default
12
+ nil
13
+ end
14
+
15
+ def doc
16
+ nil
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module ServiceContract
2
+ AbstractProtocol = Struct.new(:name, :service) do
3
+ def endpoints
4
+ []
5
+ end
6
+
7
+ def endpoint(name)
8
+ endpoints.detect{|endpoint| endpoint.name == name}
9
+ end
10
+
11
+ def types
12
+ []
13
+ end
14
+
15
+ def type(name)
16
+ types.detect{|type| type.name == name}
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ module ServiceContract
2
+ class AbstractService
3
+ class << self
4
+ def all
5
+ []
6
+ end
7
+
8
+ def find(version)
9
+ all.detect{|definition| definition.version == version.to_s}
10
+ end
11
+ end
12
+
13
+ attr_reader :version
14
+ def initialize(version)
15
+ @version = version.to_s
16
+ end
17
+
18
+ # returns an array of AbstractProtocol
19
+ def protocols
20
+ []
21
+ end
22
+
23
+ def protocol(name)
24
+ protocols.detect{|protocol| protocol.name == name}
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ module ServiceContract
2
+ AbstractType = Struct.new(:definition) do
3
+ def name
4
+ raise :not_implemented
5
+ end
6
+
7
+ def subtype
8
+ nil
9
+ end
10
+
11
+ def fields
12
+ []
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,44 @@
1
+ module ServiceContract
2
+ module Assertions
3
+
4
+ def assert_endpoint_response(data, endpoint, allow_nil = true)
5
+ assert_data_matches_type(data, endpoint.response_type, allow_nil)
6
+ end
7
+
8
+ def assert_data_matches_type(data, type, allow_nil = true)
9
+ if type.is_a?(ServiceContract::Avro::ArrayType)
10
+ assert data.is_a?(Array), "expected response to be an Array"
11
+ data.each do |datum|
12
+ assert_data_matches_type(datum, type.subtype)
13
+ end
14
+ else
15
+ type.fields.each do |field|
16
+ assert data.has_key?(field.name), "expected #{type.name} to have attribute: #{field.name}"
17
+ value = data.fetch(field.name)
18
+
19
+ expected_classes = classes_for_parameter(field)
20
+ assert (allow_nil && value.nil?) || expected_classes.any?{|klass| value.is_a?(klass)}, "expected #{type.name}.#{field.name} to be a #{expected_classes.join(", ")}"
21
+ end
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ def classes_for_parameter(field)
28
+ classes = case field.type
29
+ when "int"
30
+ Fixnum
31
+ when "string"
32
+ String
33
+ when "float"
34
+ Float
35
+ when "boolean"
36
+ [TrueClass, FalseClass]
37
+ else # a complex type
38
+ Hash
39
+ end
40
+ Array(classes)
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ module ServiceContract
2
+ module Avro
3
+ class ArrayType < Type
4
+ def name
5
+ "Array"
6
+ end
7
+
8
+ def subtype
9
+ Type.build(definition.items)
10
+ end
11
+
12
+ def to_s
13
+ "Array(#{subtype.to_s})"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,34 @@
1
+ require 'sinatra/base'
2
+ require 'slim'
3
+
4
+ module ServiceContract
5
+ module Avro
6
+ class Documentation < Sinatra::Base
7
+ get '/:version/:protocol' do
8
+ version = service.find(params[:version])
9
+ protocol = version.protocol(params[:protocol])
10
+ slim :protocol, locals: { version: version, protocol: protocol }
11
+ end
12
+
13
+ get '/:version' do
14
+ version = service.find(params[:version])
15
+
16
+ if version
17
+ slim :version, locals: { version: version }
18
+ else
19
+ status 404
20
+ end
21
+ end
22
+
23
+ get '/' do
24
+ slim :homepage
25
+ end
26
+
27
+ helpers do
28
+ def service
29
+ raise :not_implemented
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,63 @@
1
+ require 'forwardable'
2
+
3
+ module ServiceContract
4
+ module Avro
5
+ class Endpoint < AbstractEndpoint
6
+ extend Forwardable
7
+ def_delegators :definition, :name, :response, :request
8
+
9
+ def description
10
+ [request_method, path].join(" ")
11
+ end
12
+
13
+ def doc
14
+ definition.respond_to?(:doc) ? definition.doc : nil
15
+ end
16
+
17
+ def response_type
18
+ Type.build(response)
19
+ end
20
+
21
+ def parameters
22
+ request.fields.map{|field| Parameter.new(field) }
23
+ end
24
+
25
+ protected
26
+
27
+ def request_method
28
+ case name
29
+ when "create"
30
+ "POST"
31
+ when "update"
32
+ "PUT"
33
+ when "destroy"
34
+ "DELETE"
35
+ else
36
+ "GET"
37
+ end
38
+ end
39
+
40
+ def path
41
+ case name
42
+ when "index", "create"
43
+ protocol.path
44
+ when "show", "destroy", "update"
45
+ File.join(protocol.path, ":id")
46
+ else
47
+ if member?
48
+ File.join(protocol.path, ":id", name)
49
+ else
50
+ File.join(protocol.path, name)
51
+ end
52
+ end
53
+ end
54
+
55
+ # seems kinda hacky
56
+ def member?
57
+ first_field_type = request.fields.first.type
58
+ first_field_type.is_a?(::Avro::Schema::RecordSchema) &&
59
+ first_field_type.name == protocol.main_type
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,7 @@
1
+ module ServiceContract
2
+ module Avro
3
+ module Errors
4
+ class NotFound < Exception; end
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ module ServiceContract
2
+ module Avro
3
+ class Parameter < AbstractParameter
4
+ def name
5
+ definition.name
6
+ end
7
+
8
+ def type
9
+ definition.type.type_sym == :record ?
10
+ definition.type.name :
11
+ definition.type.type_sym.to_s
12
+ end
13
+
14
+ def default
15
+ definition.default
16
+ end
17
+
18
+ def doc
19
+ definition.respond_to?(:doc) ? definition.doc : nil
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,39 @@
1
+ module ServiceContract
2
+ module Avro
3
+ class Protocol < AbstractProtocol
4
+ def file_path
5
+ File.join(service.data_dir, "#{name}.avpr")
6
+ end
7
+
8
+ def endpoints
9
+ avro.messages.map do |name, message|
10
+ Endpoint.new(self, message)
11
+ end
12
+ end
13
+
14
+ def types
15
+ avro.types.map do |type|
16
+ Type.new(type)
17
+ end
18
+ end
19
+
20
+ def path
21
+ File.join(service.path, resource_name)
22
+ end
23
+
24
+ protected
25
+
26
+ def resource_name
27
+ name.respond_to?(:pluralize) ? name.pluralize : "#{name}s"
28
+ end
29
+
30
+ def avro
31
+ @avro ||= begin
32
+ raise Errors::NotFound unless File.exists?(file_path)
33
+
34
+ ::Avro::Protocol.parse(File.read(file_path))
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,45 @@
1
+ module ServiceContract
2
+ module Avro
3
+ class Service < AbstractService
4
+ class << self
5
+ def all
6
+ @all ||= begin
7
+ Dir.glob(File.join(data_dir, "*")).map do |filepath|
8
+ new(File.basename(filepath).to_s)
9
+ end
10
+ end
11
+ end
12
+
13
+ def data_dir
14
+ raise :not_implemented
15
+ end
16
+
17
+ def title
18
+ "Avro Service"
19
+ end
20
+
21
+ def description
22
+ ""
23
+ end
24
+
25
+ end
26
+
27
+ def protocols
28
+ @protocols ||= begin
29
+ Dir.glob(File.join(data_dir, "*.avpr")).map do |filepath|
30
+ name = File.basename(filepath, ".avpr")
31
+ Protocol.new(name, self)
32
+ end
33
+ end
34
+ end
35
+
36
+ def path
37
+ "/#{version}"
38
+ end
39
+
40
+ def data_dir
41
+ File.join(self.class.data_dir, version, "compiled")
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
1
+ module ServiceContract
2
+ module Avro
3
+ class Type < AbstractType
4
+ def name
5
+ definition.name
6
+ end
7
+
8
+ def fields
9
+ definition.fields.map do |field|
10
+ Parameter.new(field)
11
+ end
12
+ end
13
+
14
+ def to_s
15
+ name
16
+ end
17
+
18
+ def self.build(definition)
19
+ definition.is_a?(::Avro::Schema::ArraySchema) ?
20
+ ArrayType.new(definition) :
21
+ Type.new(definition)
22
+ end
23
+
24
+ protected
25
+
26
+ def record?
27
+ definition.type.type_sym == :record
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ h2 Versions
2
+
3
+ - service.all.each do |version|
4
+ p
5
+ a href=to("#{version.version}")
6
+ | Version
7
+ =< version.version
@@ -0,0 +1,17 @@
1
+ doctype html
2
+ html
3
+ head
4
+ title
5
+ => service.name
6
+ | Documentation
7
+ link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css"
8
+ script src="//code.jquery.com/jquery-2.1.1.min.js"
9
+ script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"
10
+ meta name="viewport" content="width=device-width, initial-scale=1"
11
+
12
+ body
13
+ .container
14
+ h1 = service.title
15
+ p = service.description
16
+
17
+ == yield
@@ -0,0 +1,56 @@
1
+ h2
2
+ | Version
3
+ =<> version.version
4
+ a href=to(version.version)
5
+ i.glyphicon.glyphicon-chevron-left>
6
+ | Back
7
+
8
+ .row
9
+ .col-md-8
10
+ h2 Endpoints
11
+
12
+ .panel-group#endpoints
13
+ - protocol.endpoints.each do |endpoint|
14
+ .panel.panel-default
15
+ .panel-heading
16
+ h4.panel-title
17
+ a data-toggle="collapse" href="#collapse#{endpoint.name}"
18
+ = endpoint.description
19
+ .panel-collapse.collapse.in id="collapse#{endpoint.name}"
20
+ .panel-body
21
+ p = endpoint.doc
22
+
23
+ h4 Parameters
24
+
25
+ ul
26
+ - endpoint.parameters.each do |parameter|
27
+ li
28
+ => parameter.type
29
+ = parameter.name
30
+
31
+ h4 Response
32
+ p
33
+ = endpoint.response_type
34
+
35
+ .col-md-4
36
+ h2 Types
37
+
38
+ .panel-group#types
39
+ - protocol.types.each do |type|
40
+ .panel.panel-default
41
+ .panel-heading
42
+ h4.panel-title
43
+ a data-toggle="collapse" href="#collapse#{type.name}"
44
+ = type.name
45
+ .panel-collapse.collapse.in id="collapse#{type.name}"
46
+ .panel-body
47
+ - type.fields.each do |field|
48
+ p
49
+ a.pull-right data-toggle="collapse" href="#collapseDescription#{type.name}#{field.name}"
50
+ i.glyphicon.glyphicon-info-sign
51
+ => field.type
52
+ | -
53
+ =< field.name
54
+ - if field.doc
55
+ p.collapse id="collapseDescription#{type.name}#{field.name}"
56
+ em = field.doc
@@ -0,0 +1,12 @@
1
+ a href=to("")
2
+ i.glyphicon.glyphicon-chevron-left>
3
+ | Back
4
+ h2
5
+ | Version
6
+ =< version.version
7
+
8
+ h3 Protocols
9
+ - version.protocols.each do |protocol|
10
+ p
11
+ a href=to("#{version.version}/#{protocol.name}")
12
+ = protocol.name
@@ -0,0 +1,14 @@
1
+ require 'avro'
2
+
3
+ module ServiceContract
4
+ module Avro
5
+ autoload :ArrayType, 'service_contract/avro/array_type'
6
+ autoload :Documentation, 'service_contract/avro/documentation'
7
+ autoload :Endpoint, 'service_contract/avro/endpoint'
8
+ autoload :Errors, 'service_contract/avro/errors'
9
+ autoload :Parameter, 'service_contract/avro/parameter'
10
+ autoload :Protocol, 'service_contract/avro/protocol'
11
+ autoload :Service, 'service_contract/avro/service'
12
+ autoload :Type, 'service_contract/avro/type'
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ namespace :avro do
2
+ desc 'build definitions'
3
+ task :build do
4
+ jar_file = File.expand_path("../../../src/avro-tools-1.7.7.jar", __FILE__)
5
+ spec_dir = File.expand_path(".")
6
+ Dir.glob(File.join(spec_dir, "/**/*.avdl")) do |file|
7
+ puts file
8
+ new_file = File.expand_path(File.join(File.dirname(file), "../compiled", File.basename(file, ".avdl")))
9
+ command = "java -jar #{jar_file} idl #{file} #{new_file}.avpr"
10
+ puts command
11
+ `#{command}`
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module ServiceContract
2
+ VERSION = "0.0.5"
3
+ end
@@ -0,0 +1,15 @@
1
+ require "service_contract/version"
2
+
3
+ module ServiceContract
4
+ autoload :AbstractEndpoint, 'service_contract/abstract_endpoint'
5
+ autoload :AbstractParameter, 'service_contract/abstract_parameter'
6
+ autoload :AbstractProtocol, 'service_contract/abstract_protocol'
7
+ autoload :AbstractService, 'service_contract/abstract_service'
8
+ autoload :AbstractType, 'service_contract/abstract_type'
9
+
10
+ # test hooks
11
+ autoload :Assertions, 'service_contract/assertions'
12
+
13
+ # providers
14
+ autoload :Avro, 'service_contract/avro'
15
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'service_contract/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "service_contract"
8
+ spec.version = ServiceContract::VERSION
9
+ spec.authors = ["Jeff Ching"]
10
+ spec.email = ["jching@avvo.com"]
11
+ spec.summary = %q{Abstract the definition of a service's interface contract}
12
+ spec.description = %q{Abstract the definition of a service's interface contract. Supports Avro}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
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_dependency "avro", "~> 1.7"
22
+ spec.add_development_dependency "bundler"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "minitest", "~> 5"
25
+ end
Binary file
@@ -0,0 +1,113 @@
1
+ {
2
+ "protocol" : "Location",
3
+ "namespace" : "Gnomon",
4
+ "types" : [ {
5
+ "type" : "record",
6
+ "name" : "Timestamp",
7
+ "fields" : [ {
8
+ "name" : "timestamp",
9
+ "type" : "int"
10
+ } ]
11
+ }, {
12
+ "type" : "record",
13
+ "name" : "Location",
14
+ "doc" : "This main class for this protocol",
15
+ "fields" : [ {
16
+ "name" : "id",
17
+ "type" : "int",
18
+ "doc" : "foo"
19
+ }, {
20
+ "name" : "type",
21
+ "type" : "string"
22
+ }, {
23
+ "name" : "county_name",
24
+ "type" : "string"
25
+ }, {
26
+ "name" : "state_name",
27
+ "type" : "string"
28
+ }, {
29
+ "name" : "country_name",
30
+ "type" : "string"
31
+ }, {
32
+ "name" : "city_name",
33
+ "type" : "string"
34
+ }, {
35
+ "name" : "neighborhood_name",
36
+ "type" : "string"
37
+ }, {
38
+ "name" : "postal_code_name",
39
+ "type" : "string"
40
+ }, {
41
+ "name" : "to_location",
42
+ "type" : "string"
43
+ }, {
44
+ "name" : "to_short_location",
45
+ "type" : "string"
46
+ }, {
47
+ "name" : "to_long_location",
48
+ "type" : "string"
49
+ }, {
50
+ "name" : "to_location_with_region",
51
+ "type" : "string"
52
+ }, {
53
+ "name" : "latitude",
54
+ "type" : "float"
55
+ }, {
56
+ "name" : "longitude",
57
+ "type" : "float"
58
+ }, {
59
+ "name" : "population",
60
+ "type" : "int"
61
+ }, {
62
+ "name" : "updated_at",
63
+ "type" : "Timestamp"
64
+ } ]
65
+ }, {
66
+ "type" : "record",
67
+ "name" : "FetchOption",
68
+ "fields" : [ {
69
+ "name" : "page",
70
+ "type" : "int",
71
+ "default" : 0
72
+ }, {
73
+ "name" : "per_page",
74
+ "type" : "int",
75
+ "default" : 30
76
+ } ]
77
+ } ],
78
+ "messages" : {
79
+ "index" : {
80
+ "doc" : "Returns a list of Locations",
81
+ "request" : [ {
82
+ "name" : "options",
83
+ "type" : "FetchOption"
84
+ } ],
85
+ "response" : {
86
+ "type" : "array",
87
+ "items" : "Location"
88
+ }
89
+ },
90
+ "show" : {
91
+ "doc" : "Return a single Location",
92
+ "request" : [ {
93
+ "name" : "id",
94
+ "type" : "int"
95
+ } ],
96
+ "response" : {
97
+ "type" : "array",
98
+ "items" : "Location"
99
+ }
100
+ },
101
+ "resolve" : {
102
+ "doc" : "Try to guess a location from a given string",
103
+ "request" : [ {
104
+ "name" : "q",
105
+ "type" : "string"
106
+ } ],
107
+ "response" : {
108
+ "type" : "array",
109
+ "items" : "Location"
110
+ }
111
+ }
112
+ }
113
+ }
@@ -0,0 +1,60 @@
1
+ {
2
+ "protocol" : "SalesRegion",
3
+ "namespace" : "Gnomon",
4
+ "types" : [ {
5
+ "type" : "record",
6
+ "name" : "SalesRegion",
7
+ "fields" : [ {
8
+ "name" : "id",
9
+ "type" : "int"
10
+ }, {
11
+ "name" : "name",
12
+ "type" : "string"
13
+ }, {
14
+ "name" : "state_id",
15
+ "type" : "int"
16
+ }, {
17
+ "name" : "split_region",
18
+ "type" : "boolean"
19
+ } ]
20
+ }, {
21
+ "type" : "record",
22
+ "name" : "SalesRegionFetchOption",
23
+ "fields" : [ {
24
+ "name" : "page",
25
+ "type" : "int",
26
+ "default" : 1
27
+ }, {
28
+ "name" : "per_page",
29
+ "type" : "int",
30
+ "default" : 30
31
+ }, {
32
+ "name" : "state_id",
33
+ "type" : "int"
34
+ } ]
35
+ } ],
36
+ "messages" : {
37
+ "index" : {
38
+ "doc" : "Returns a list of SalesRegion",
39
+ "request" : [ {
40
+ "name" : "options",
41
+ "type" : "SalesRegionFetchOption"
42
+ } ],
43
+ "response" : {
44
+ "type" : "array",
45
+ "items" : "SalesRegion"
46
+ }
47
+ },
48
+ "show" : {
49
+ "doc" : "Return a single SalesRegion",
50
+ "request" : [ {
51
+ "name" : "id",
52
+ "type" : "int"
53
+ } ],
54
+ "response" : {
55
+ "type" : "array",
56
+ "items" : "SalesRegion"
57
+ }
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,43 @@
1
+ @namespace("Gnomon")
2
+
3
+ protocol Location {
4
+
5
+ record Timestamp {
6
+ int timestamp;
7
+ }
8
+
9
+ /** This main class for this protocol */
10
+ record Location {
11
+ /** foo */
12
+ int id;
13
+ string type;
14
+ string county_name;
15
+ string state_name;
16
+ string country_name;
17
+ string city_name;
18
+ string neighborhood_name;
19
+ string postal_code_name;
20
+ string to_location;
21
+ string to_short_location;
22
+ string to_long_location;
23
+ string to_location_with_region;
24
+ float latitude;
25
+ float longitude;
26
+ int population;
27
+ Timestamp updated_at;
28
+ }
29
+
30
+ record FetchOption {
31
+ int page = 0;
32
+ int per_page = 30;
33
+ }
34
+
35
+ /** Returns a list of Locations */
36
+ array<Location> index(FetchOption options);
37
+
38
+ /** Return a single Location */
39
+ array<Location> show(int id);
40
+
41
+ /** Try to guess a location from a given string */
42
+ array<Location> resolve(string q);
43
+ }
@@ -0,0 +1,24 @@
1
+ @namespace("Gnomon")
2
+
3
+ protocol SalesRegion {
4
+
5
+ record SalesRegion {
6
+ int id;
7
+ string name;
8
+ int state_id;
9
+ boolean split_region;
10
+ }
11
+
12
+ record SalesRegionFetchOption {
13
+ int page = 1;
14
+ int per_page = 30;
15
+ int state_id;
16
+ }
17
+
18
+ /** Returns a list of SalesRegion */
19
+ array<SalesRegion> index(SalesRegionFetchOption options);
20
+
21
+ /** Return a single SalesRegion */
22
+ array<SalesRegion> show(int id);
23
+
24
+ }
@@ -0,0 +1,21 @@
1
+ require 'test_helper'
2
+
3
+ class ServiceTest < Minitest::Test
4
+
5
+ def test_all
6
+ assert_equal(1, SampleService.all.length)
7
+ end
8
+
9
+ def test_find
10
+ service = SampleService.find(1)
11
+ assert service, "expect to find a service by version"
12
+ assert_equal "1", service.version
13
+
14
+ assert_equal 2, service.protocols.length
15
+ protocol = service.protocol("location")
16
+
17
+ assert_equal "location", protocol.name
18
+ assert_equal 3, protocol.endpoints.length
19
+ end
20
+
21
+ end
@@ -0,0 +1,10 @@
1
+ require 'codeclimate-test-reporter'
2
+ CodeClimate::TestReporter.start
3
+ require 'minitest/autorun'
4
+ require 'service_contract'
5
+
6
+ class SampleService < ServiceContract::Avro::Service
7
+ def self.data_dir
8
+ File.expand_path("../sample", __FILE__)
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,144 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: service_contract
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Jeff Ching
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: avro
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '5'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '5'
69
+ description: Abstract the definition of a service's interface contract. Supports Avro
70
+ email:
71
+ - jching@avvo.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".travis.yml"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - lib/service_contract.rb
83
+ - lib/service_contract/abstract_endpoint.rb
84
+ - lib/service_contract/abstract_field.rb
85
+ - lib/service_contract/abstract_parameter.rb
86
+ - lib/service_contract/abstract_protocol.rb
87
+ - lib/service_contract/abstract_service.rb
88
+ - lib/service_contract/abstract_type.rb
89
+ - lib/service_contract/assertions.rb
90
+ - lib/service_contract/avro.rb
91
+ - lib/service_contract/avro/array_type.rb
92
+ - lib/service_contract/avro/documentation.rb
93
+ - lib/service_contract/avro/endpoint.rb
94
+ - lib/service_contract/avro/errors.rb
95
+ - lib/service_contract/avro/parameter.rb
96
+ - lib/service_contract/avro/protocol.rb
97
+ - lib/service_contract/avro/service.rb
98
+ - lib/service_contract/avro/type.rb
99
+ - lib/service_contract/avro/views/homepage.slim
100
+ - lib/service_contract/avro/views/layout.slim
101
+ - lib/service_contract/avro/views/protocol.slim
102
+ - lib/service_contract/avro/views/version.slim
103
+ - lib/service_contract/tasks.rb
104
+ - lib/service_contract/version.rb
105
+ - service_contract.gemspec
106
+ - src/avro-tools-1.7.7.jar
107
+ - test/sample/1/compiled/location.avpr
108
+ - test/sample/1/compiled/sales_region.avpr
109
+ - test/sample/1/source/location.avdl
110
+ - test/sample/1/source/sales_region.avdl
111
+ - test/service_test.rb
112
+ - test/test_helper.rb
113
+ homepage: ''
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.2.2
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: Abstract the definition of a service's interface contract
137
+ test_files:
138
+ - test/sample/1/compiled/location.avpr
139
+ - test/sample/1/compiled/sales_region.avpr
140
+ - test/sample/1/source/location.avdl
141
+ - test/sample/1/source/sales_region.avdl
142
+ - test/service_test.rb
143
+ - test/test_helper.rb
144
+ has_rdoc: