zeta 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 63a82870a90c693bb9e34ba0ce55aff5372166c6
4
+ data.tar.gz: a78e671775bc0677bdb678a3ddd4e60377471082
5
+ SHA512:
6
+ metadata.gz: 7b08be9309a27352425d7fc8e1146746d538d18f019046d4585664d649fdeb7784e37432cd7f0d1615677227c656dc642b7a63790689465664a31c7b827b9925
7
+ data.tar.gz: 5c521252594b5344c7cd21b3649fe5f3ccafc487118e51ad515a571bac04c18a00ae97782d7ee617cfc482acafdf069b72701adb8e54b54dab8895fd8db677da
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.3
@@ -0,0 +1,2 @@
1
+ # 0.1.2 (20-Oct-15)
2
+ - add `zeta` runner
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,10 @@
1
+ guard :bundler do
2
+ watch('Gemfile')
3
+ watch(/^.+\.gemspec/)
4
+ end
5
+
6
+ guard :rspec, cmd: 'TEST=1 rspec', all_on_start: true do
7
+ watch(%r{^spec/.+\.rb$}) { "spec" }
8
+ watch(%r{^lib/(.+)\.rb$}) { "spec" }
9
+ watch('spec/spec_helper.rb') { "spec" }
10
+ end
data/README.md ADDED
@@ -0,0 +1,209 @@
1
+ [![Circle CI](https://circleci.com/gh/moviepilot/zeta.svg?style=svg)](https://circleci.com/gh/moviepilot/zeta) [![Coverage Status](https://coveralls.io/repos/moviepilot/zeta/badge.svg?branch=master&service=github)](https://coveralls.io/github/moviepilot/zeta?branch=master) [![Code Climate](https://codeclimate.com/github/moviepilot/zeta/badges/gpa.svg)](https://codeclimate.com/github/moviepilot/zeta)
2
+
3
+ ![](https://dl.dropboxusercontent.com/u/1953503/zeta.jpg)
4
+
5
+ # Old Maid
6
+
7
+ ```
8
+ TLDR:
9
+ - each service defines which objects it publishes or consumes
10
+ - these contracts are formatted in human readable markdown
11
+ - you never have to know/care about other services or repositories
12
+
13
+ Old Maid will:
14
+ - know the rest of your infrastructure and fetches the contracts of all other services
15
+ - alert you if your change in service X breaks service Y
16
+ ```
17
+
18
+ In an infrastructure where many services are talking with each other, it's sometimes hard to know **how changes in one service affect other services**, as each project often just knows about itself. Even if local tests pass, you can't know what other services might be affected when you make changes to a service.
19
+
20
+ *Old Maid* tackles this problem by allowing each service to define which objects it consumes, and which objects it publishes - in simple Markdown (specifically [MSON](https://github.com/apiaryio/mson)).It doesn't matter if these objected are transported via a HTTP, a message broker like RabbitMQ or any other mean.
21
+
22
+
23
+ ## Walk this way
24
+
25
+ Let's imagine an imaginary chat app that is split up into three independent services that communicate via a message broker:
26
+
27
+ - **MessageService** keeps track of the state of to do items
28
+ - **SearchService** makes your chat history searchable
29
+ - **NotificationService**: sends an email when a private message is received
30
+
31
+ Each time a message is sent, MessageService publishes the full message to the message broker. While the SearchService is likely interested in the full message with all of its properties to index it probably, the NotificationService might just care about the sender and the receiver of the message, discarding all other properties.
32
+
33
+ An intern is asked to implement a feature that allows one message to be sent to multiple people at the same time. They go ahead and just change the numerical `recepient_id` property of a message to an `recipient_ids` array. They run their tests and all looks good.
34
+
35
+ 😱But **THE INTERN JUST BROKE THE NOTIFICATION SERVICE** because it depends on the `recipient_id` property 😱
36
+
37
+ Wouldn't it be nice of some test local to the **MessageService** repository to tell the poorintern that removing the `recipient_id` property breaks the expectations other services have of the *MessageService* BEFORE they deploy?
38
+
39
+
40
+ ## Yes, it would!
41
+
42
+ Each project have to contain two files in order for *Old Maid* to do its job:
43
+
44
+ 1. `contracts/publish.mson`
45
+ 2. `contracts/consume.mson`
46
+
47
+ These are simple markdown files in the wonderful [MSON](https://github.com/apiaryio/mson) format. Let's look at the contracts dir of **MessageService**, shall we?
48
+
49
+ ### A publish specification:
50
+ ```shell
51
+ /home/dev/message-service$ cat contracts/publish.mson
52
+ # Data Structures
53
+ This file defines what MessageService may publish.
54
+
55
+ # Message
56
+ - id: (number, required)
57
+ - sender_id: (number, required)
58
+ - recipients: (Array[number], required)
59
+ - text: (string, required)
60
+ - emoji: (string)
61
+ ```
62
+
63
+ So far so good. This way *MessageService* can tell the world what exactly it means when a `Message` object is published. Much the same, the *NotificationService* could define which properties of a `Message` object from the `MessageService` it is actually interested in:
64
+
65
+ ### A consume specification:
66
+ ```shell
67
+ /home/dev/notification-service$ cat contracts/consume.mson
68
+ # Data Structures
69
+ We just consume one object type, and it comes from the MessageService. Check it out!
70
+
71
+ # MessageService:Message
72
+ - sender_id: (number, required)
73
+ - recipient_id: (number, required)
74
+ ```
75
+
76
+ As you can see, this consumer expects the `recipient_id` property to be present when a `Message` object is received from `MessageService`. While a publish specification just defines objects, a consume specification prefixes the names of objects it consumes with the name of the service publishing the object. As in our example above:
77
+
78
+ ```
79
+ # MessageService:Message
80
+ | `---------- object name
81
+ `------------------------- service name
82
+
83
+ ```
84
+
85
+ ## Getting started
86
+
87
+ ### 1. Installation
88
+ First, add *Old Maid* to your `Gemfile` or install manually:
89
+
90
+ ```shell
91
+ $ gem install zeta
92
+ ```
93
+
94
+ ### 2. Configuration
95
+
96
+ If you're using ruby on rails, *Old Maid* will automatically know your
97
+ environment and look for its configuration in `config/zeta.yml`, which could look like this:
98
+
99
+ ```yaml
100
+ common: &common
101
+ # This name will be used by other services to identify objects they consume
102
+ # from the MessageService
103
+ service_name: MessageService
104
+
105
+ # Which directory contains the publish.mson and consume.mson
106
+ contracts_path: contracts
107
+
108
+ # Where to cache the contracts from all other projects that are part of the
109
+ # infrastructure. Old Maid will fetch these for you.
110
+ contracts_cache_path: contracts/.cache
111
+
112
+
113
+ development:
114
+ <<: *common
115
+ # The services file contains all services that are part of your infrastructure
116
+ # and tested with Old Maid. It's just another yaml file, but the nice thing is
117
+ # that it's outside of the service's repository and has to be maintained only
118
+ # in one place.
119
+ services_file:
120
+
121
+ # The file name to look for
122
+ file: 'development.yml'
123
+
124
+ # You can either host the file yourself via HTTP, but it's quite convenient
125
+ # to have it version controlled and hosted by a service like github. We'll
126
+ # call this repo zeta-config for this example:
127
+ github:
128
+ repo: jensmander/zeta-config
129
+ branch: master
130
+ path: infrastructure
131
+
132
+ # And here we can adjust things for each environment as needed
133
+ production:
134
+ <<: *common
135
+ ...
136
+
137
+ ```
138
+
139
+ You typically just create the above file once and then don't touch it anymore. If that file is in a private repository (that would be a good idea), make sure you `export GITHUB_USERNAME=youruser` and `GITHUB_TOKEN=yourtoken` and *Old Maid* will use that.
140
+
141
+ Here's how `github.com/jensmander/zeta-config/infrastructure/master.yml` might look in our example above:
142
+
143
+ ```yaml
144
+ MessageService:
145
+ github:
146
+ repo: 'jensmander/messages'
147
+ branch: 'master'
148
+ path: 'contracts'
149
+ SearchService:
150
+ github:
151
+ repo: 'jensmander/search'
152
+ branch: 'master'
153
+ path: 'contracts'
154
+ NotificationService:
155
+ github:
156
+ repo: 'jensmander/notifications'
157
+ branch: 'master'
158
+ path: 'contracts'
159
+ ```
160
+
161
+ Whenever you add a service to the infrastructure, you just add it to this central file and all existing services will automatically know about your new service.
162
+
163
+
164
+ ### 3. Usage
165
+
166
+ ```shell
167
+ Usage: zeta [options] full_check|fetch_remote_contracts|update_own_contracts|validate
168
+
169
+ Specific options:
170
+ -c, --config=CONFIG_FILE Config file (default: config/zeta.yml)
171
+ -e, --env=ENVIRONMENT Environment (default: RAILS_ENV, if it is set)
172
+ -s, --silent No output, just an appropriate return code
173
+
174
+ Common options:
175
+ -h, --help Show this message
176
+ -v, --version Show version
177
+ ```
178
+
179
+ Example time. You can tell *Old Maid* to validate the whole infrastructure like this:
180
+
181
+ ```shell
182
+ $ zeta -e development full_check
183
+ ```
184
+
185
+ The above command performs the following three steps:
186
+
187
+ 1. Fetch all contracts from remote repositories and put them into the cache directory configured above
188
+ 2. Copy the current projects contracts (that you might have changed) into the cache directory
189
+ 3. Validate all contracts (i.e. make sure that every publishing service satisfies its consumers)
190
+
191
+ The above commands can also be run in isolation:
192
+
193
+ ```shell
194
+ $ zeta -e development fetch_contracts
195
+ ```
196
+
197
+ This command will populate the `contracts/.cache` directory with the current version of all contracts and then copy over your local changes to your contract. You can then validate your infrastructure like this:
198
+
199
+ ```shell
200
+ $ zeta -e development validate
201
+ ```
202
+
203
+ If you just made changes to your local contracts, you can copy them over to the cache and validate your infrastructure like this:
204
+
205
+ ```shell
206
+ $ zeta -e development update_own_contracts validate
207
+ ```
208
+
209
+ Otherwise it will exit with an error and display any contract violations in JSON.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ require "minimum-term/tasks"
data/bin/zeta ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require 'zeta'
5
+ require 'zeta/runner'
6
+
7
+ Zeta::Runner.run
data/circle.yml ADDED
@@ -0,0 +1,3 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.2.1
@@ -0,0 +1 @@
1
+ Some MSON
@@ -0,0 +1 @@
1
+ Some MSON
@@ -0,0 +1 @@
1
+ Some MSON
@@ -0,0 +1 @@
1
+ Some MSON
@@ -0,0 +1,145 @@
1
+ require 'active_support/core_ext/hash/indifferent_access'
2
+ require 'yaml'
3
+ require 'fileutils'
4
+ require 'tmpdir'
5
+ require 'minimum-term'
6
+
7
+ require 'zeta/local_or_remote_file'
8
+
9
+ class Zeta
10
+ module Instance
11
+ attr_reader :config
12
+
13
+ def initialize(options = {})
14
+ @mutex = Mutex.new
15
+ @options = options
16
+ puts "Using config file #{config_file}" if verbose?
17
+ end
18
+
19
+ def update_contracts
20
+ i = infrastructure
21
+ @mutex.synchronize do
22
+ puts "Updating #{cache_dir}" if verbose?
23
+ update_other_contracts
24
+ update_own_contracts
25
+ i.convert_all!
26
+ end
27
+ true
28
+ end
29
+
30
+ def update_own_contracts
31
+ contract_files.each do |file|
32
+ source_file = File.join(config[:contracts_path], file)
33
+ target_file = File.join(cache_dir, config[:service_name], file)
34
+ puts "cp #{source_file} #{target_file}" if verbose?
35
+ FileUtils.rm_f(target_file)
36
+ FileUtils.cp(source_file, target_file) if File.exists?(source_file)
37
+ end
38
+ end
39
+
40
+ def errors
41
+ infrastructure.errors
42
+ end
43
+
44
+ def contracts_fulfilled?
45
+ infrastructure.contracts_fulfilled?
46
+ end
47
+
48
+ def infrastructure
49
+ @mutex.synchronize do
50
+ return @infrastructure if @infrastructure
51
+ @infrastructure = MinimumTerm::Infrastructure.new(data_dir: cache_dir, verbose: verbose?)
52
+ @infrastructure
53
+ end
54
+ end
55
+
56
+ def config_file
57
+ return File.expand_path(@options[:config_file]) if @options[:config_file]
58
+ File.join(Dir.pwd, 'config', 'zeta.yml')
59
+ end
60
+
61
+ def env
62
+ return @options[:env].to_sym if @options[:env]
63
+ if Object.const_defined?('Rails')
64
+ Rails.env.to_sym
65
+ else
66
+ guessed = ENV['RAILS_ENV'] || ENV['RACK_ENV']
67
+ raise "No environment given" unless guessed
68
+ guessed
69
+ end
70
+ end
71
+
72
+ def cache_dir
73
+ return @cache_dir if @cache_dir
74
+ full_path = File.expand_path(config[:contracts_cache_path])
75
+ FileUtils.mkdir_p(full_path)
76
+ @cache_dir = full_path
77
+ end
78
+
79
+ def config
80
+ return @config if @config
81
+ full_config = YAML.load_file(config_file).with_indifferent_access
82
+ env_config = full_config[env]
83
+
84
+ raise "No config for environment '#{env}' found in #{config_file}" unless env_config
85
+
86
+ # TODO validate it properly
87
+ [:service_name, :contracts_path, :contracts_cache_path].each do |k|
88
+ raise ":#{k} missing in #{full_config.to_json}" unless env_config[k]
89
+ end
90
+
91
+ @config = env_config
92
+ end
93
+
94
+ private
95
+
96
+ def verbose?
97
+ @options[:verbose]
98
+ end
99
+
100
+ def fetch_service_contracts(service_name, config)
101
+ target_dir = File.join(cache_dir, service_name)
102
+ FileUtils.mkdir_p(target_dir)
103
+
104
+ contract_files.each do |contract|
105
+ file = File.join(target_dir, contract)
106
+ FileUtils.rm_f(file)
107
+
108
+ File.open(file, 'w') do |out|
109
+ contract = LocalOrRemoteFile.new(config.merge(file: contract, verbose: verbose?)).read
110
+ raise "Invalid contract:\n\n#{contract}\n#{'~'*80}" unless contract_looks_valid?(contract)
111
+ out.puts contract
112
+ end
113
+ end
114
+ end
115
+
116
+ def contract_looks_valid?(contract)
117
+ true # TODO
118
+ end
119
+
120
+ def update_other_contracts
121
+ services.each do |service_name, config|
122
+ fetch_service_contracts(service_name, config)
123
+ end
124
+ end
125
+
126
+ def contract_files
127
+ ['publish.mson', 'consume.mson']
128
+ end
129
+
130
+ def services
131
+ if config[:services]
132
+ return config[:services]
133
+ elsif config[:services_file]
134
+ file = LocalOrRemoteFile.new(config[:services_file].merge(debug: verbose?))
135
+ services = YAML.load(file.read)
136
+ begin
137
+ services.with_indifferent_access
138
+ rescue
139
+ raise "Could not load services from #{config[:services_file].to_json}"
140
+ end
141
+ end
142
+ end
143
+
144
+ end
145
+ end
@@ -0,0 +1,61 @@
1
+ require 'colorize'
2
+ require 'httparty'
3
+ require 'open-uri'
4
+
5
+ require 'pry'
6
+
7
+ class Zeta::LocalOrRemoteFile
8
+ def initialize(options)
9
+ @options = options
10
+ end
11
+
12
+ def read
13
+ if @options[:path]
14
+ read_local
15
+ elsif @options[:github]
16
+ read_from_github
17
+ else
18
+ raise "Unknown file location #{@options}"
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def read_local
25
+ open(File.join(@options[:path], @options[:file])).read
26
+ end
27
+
28
+ def read_from_github
29
+ self.class.http_get(github_url, verbose?)
30
+ end
31
+
32
+ def self.http_get(url, verbose)
33
+ masked_url = ENV['GITHUB_TOKEN'].blank? ? url : url.sub(ENV['GITHUB_TOKEN'], '***')
34
+ print "GET #{masked_url}... " if verbose
35
+ result = HTTParty.get url
36
+ raise "Error #{result.code}" unless result.code == 200
37
+ print "OK\n".green if verbose
38
+ result.to_s
39
+ rescue
40
+ print "ERROR\n".blue if verbose
41
+ raise
42
+ end
43
+
44
+ def verbose?
45
+ !!@options[:verbose]
46
+ end
47
+
48
+ def github_url
49
+ repo = @options[:github][:repo]
50
+ branch = @options[:github][:branch]
51
+ path = @options[:github][:path]
52
+ file = @options[:file]
53
+
54
+ uri = [branch, path, file].compact.join('/')
55
+ if u = ENV['GITHUB_USER'] and t = ENV['GITHUB_TOKEN']
56
+ "https://#{u}:#{t}@raw.githubusercontent.com/#{repo}/#{uri}"
57
+ else
58
+ "https://raw.githubusercontent.com/#{repo}/#{uri}"
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,83 @@
1
+ # encoding: UTF-8
2
+ require 'optparse'
3
+
4
+ class Zeta::Runner
5
+ COMMANDS = %w{full_check fetch_remote_contracts update_own_contracts validate}
6
+
7
+ def self.run
8
+ options = {}
9
+ parser = OptionParser.new do |opts|
10
+ opts.banner = "Usage: zeta [options] #{COMMANDS.join('|')}"
11
+
12
+ opts.separator ""
13
+ opts.separator "Specific options:"
14
+
15
+ opts.on("-c CONFIG_FILE", "--config=CONFIG_FILE", "Config file (default: config/zeta.yml)") do |c|
16
+ options[:config_file] = c
17
+ end
18
+
19
+ opts.on("-e ENVIRONMENT", "--env=ENVIRONMENT", "Environment (default: RAILS_ENV, if it is set)") do |e|
20
+ options[:env] = e
21
+ end
22
+
23
+ opts.on("-s", "--silent", "No output, just an appropriate return code") do |s|
24
+ options[:silent] = s
25
+ end
26
+
27
+ opts.separator ""
28
+ opts.separator "Common options:"
29
+
30
+ opts.on_tail("-h", "--help", "Show this message") do
31
+ puts opts
32
+ exit
33
+ end
34
+
35
+ # Another typical switch to print the version.
36
+ opts.on_tail("-v", "--version", "Show version") do
37
+ puts Zeta::VERSION
38
+ exit
39
+ end
40
+ end
41
+ parser.parse!
42
+
43
+ commands = ARGV
44
+ if commands.empty? or !(commands-COMMANDS).empty?
45
+ puts parser
46
+ exit(-1)
47
+ end
48
+
49
+ options[:verbose] = !options.delete(:silent)
50
+ zeta = Zeta.new(options)
51
+
52
+ begin
53
+ if commands.include?('fetch_remote_contracts') or commands.include?('full_check')
54
+ zeta.update_contracts
55
+ puts "\n" if options[:verbose]
56
+ end
57
+
58
+ if commands.include?('update_own_contracts')
59
+ puts "Copying #{zeta.config[:service_name].to_s.camelize} contracts..." if options[:verbose]
60
+ zeta.update_own_contracts
61
+ puts "\n" if options[:verbose]
62
+ end
63
+
64
+ if commands.include?('validate') or commands.include?('full_check')
65
+ puts "Validating your infrastructure with #{zeta.infrastructure.publishers.length} publishers and #{zeta.infrastructure.consumers.length} consumers..." if options[:verbose]
66
+ zeta.contracts_fulfilled?
67
+ unless zeta.errors.empty?
68
+ puts JSON.pretty_generate(zeta.errors)
69
+ puts "#{zeta.errors.length} invalid contracts".red
70
+ exit(-1)
71
+ end
72
+ puts "All contracts valid 🙌".green if options[:verbose]
73
+ end
74
+ rescue => e
75
+ if options[:verbose]
76
+ raise
77
+ else
78
+ puts "ERROR: ".red + e.message
79
+ end
80
+ exit(-1)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,3 @@
1
+ class Zeta
2
+ VERSION = "0.2.2"
3
+ end
data/lib/zeta.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'forwardable'
2
+ require 'zeta/version'
3
+ require 'zeta/instance'
4
+
5
+ class Zeta
6
+ include Zeta::Instance
7
+ MUTEX = Mutex.new
8
+
9
+ # Not using the SingleForwardable module here so that, when
10
+ # somebody tries to figure out how Zeta works by looking at
11
+ # its methods, they don't get confused.
12
+ Zeta::Instance.instance_methods.each do |method|
13
+ define_singleton_method method do |*args|
14
+ send_args = [method, args].flatten.compact
15
+ MUTEX.synchronize do
16
+ @singleton ||= new
17
+ @singleton.send(*send_args)
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+
data/todo.txt ADDED
@@ -0,0 +1,4 @@
1
+ - invalid primitives
2
+ - 404 should report url
3
+ - default primitive is object
4
+ -
data/zeta.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'zeta/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'zeta'
8
+ spec.version = Zeta::VERSION
9
+ spec.authors = ['Jannis Hermanns']
10
+ spec.email = ['jannis@gmail.com']
11
+
12
+ spec.summary = 'Collects and validates the publish/consume contracts of your infrastructure'
13
+ spec.description = 'Vlad'
14
+ spec.homepage = 'https://github.com/moviepilot/zeta'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = 'bin'
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_runtime_dependency 'rake', '~> 10.2'
22
+ spec.add_runtime_dependency 'minimum-term', '~> 0.2'
23
+ spec.add_runtime_dependency 'activesupport', '~> 4.2'
24
+ spec.add_runtime_dependency 'httparty', '~> 0.13'
25
+ spec.add_runtime_dependency 'colorize'
26
+ spec.add_runtime_dependency 'webmock'
27
+
28
+ spec.add_development_dependency 'bundler', '~> 1.9'
29
+ spec.add_development_dependency 'guard-bundler'
30
+ spec.add_development_dependency 'guard-rspec'
31
+ spec.add_development_dependency "coveralls", ["~> 0.8"]
32
+ end
metadata ADDED
@@ -0,0 +1,206 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zeta
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.2
5
+ platform: ruby
6
+ authors:
7
+ - Jannis Hermanns
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minimum-term
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activesupport
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: httparty
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.13'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.13'
69
+ - !ruby/object:Gem::Dependency
70
+ name: colorize
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: bundler
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.9'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.9'
111
+ - !ruby/object:Gem::Dependency
112
+ name: guard-bundler
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: guard-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: coveralls
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '0.8'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '0.8'
153
+ description: Vlad
154
+ email:
155
+ - jannis@gmail.com
156
+ executables:
157
+ - zeta
158
+ extensions: []
159
+ extra_rdoc_files: []
160
+ files:
161
+ - ".gitignore"
162
+ - ".rspec"
163
+ - ".travis.yml"
164
+ - CHANGELOG.markdown
165
+ - Gemfile
166
+ - Guardfile
167
+ - README.md
168
+ - Rakefile
169
+ - bin/zeta
170
+ - circle.yml
171
+ - contracts/cache/service_1/consume.mson
172
+ - contracts/cache/service_1/publish.mson
173
+ - contracts/cache/service_2/consume.mson
174
+ - contracts/cache/service_2/publish.mson
175
+ - lib/zeta.rb
176
+ - lib/zeta/instance.rb
177
+ - lib/zeta/local_or_remote_file.rb
178
+ - lib/zeta/runner.rb
179
+ - lib/zeta/version.rb
180
+ - todo.txt
181
+ - zeta.gemspec
182
+ homepage: https://github.com/moviepilot/zeta
183
+ licenses: []
184
+ metadata: {}
185
+ post_install_message:
186
+ rdoc_options: []
187
+ require_paths:
188
+ - lib
189
+ required_ruby_version: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ required_rubygems_version: !ruby/object:Gem::Requirement
195
+ requirements:
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ version: '0'
199
+ requirements: []
200
+ rubyforge_project:
201
+ rubygems_version: 2.4.8
202
+ signing_key:
203
+ specification_version: 4
204
+ summary: Collects and validates the publish/consume contracts of your infrastructure
205
+ test_files: []
206
+ has_rdoc: