messaging-stash 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 20f5e5944deef86579418b822f50db49ac4aafb5165681c80619cf581c85bcc9
4
+ data.tar.gz: 86e865833f15ec015d00942a1b39d43935c17140cf919a33d3b31700fe0b79bc
5
+ SHA512:
6
+ metadata.gz: d5a84acaa60383dfff50b20fe2c500f2264b05410e3b40ee2624ecb2bae407661557781bad3aa4b3951b8dfb123a23c013d2c7a004ecd778d6cb547f1ded0e70
7
+ data.tar.gz: db50c87e2fb21c828a671510200b32089c2fbaa71c897c95d5ac98a9df3e0a2b3e1424910149b754d7a61edef7fdaba3ffb7f5c423254e83975d9ccb28e0b63b
@@ -0,0 +1,104 @@
1
+ # Ruby CircleCI 2.0 configuration file
2
+ #
3
+ # Check https://circleci.com/docs/2.0/language-ruby/ for more details
4
+ #
5
+ version: 2
6
+ jobs:
7
+ build:
8
+ docker:
9
+ # specify the version you desire here
10
+ - image: circleci/ruby:2.5.1
11
+
12
+ # Specify service dependencies here if necessary
13
+ # CircleCI maintains a library of pre-built images
14
+ # documented at https://circleci.com/docs/2.0/circleci-images/
15
+ # - image: circleci/postgres:9.4
16
+
17
+ working_directory: ~/repo
18
+
19
+ steps:
20
+ - checkout
21
+
22
+ # Download and cache dependencies
23
+ - restore_cache:
24
+ keys:
25
+ - v1-dependencies-{{ checksum "Gemfile.lock" }}
26
+ # fallback to using the latest cache if no exact match is found
27
+ - v1-dependencies-
28
+
29
+ - run:
30
+ name: install dependencies
31
+ command: |
32
+ bundle install --jobs=4 --retry=3 --path vendor/bundle
33
+
34
+ - save_cache:
35
+ paths:
36
+ - ./vendor/bundle
37
+ key: v1-dependencies-{{ checksum "Gemfile.lock" }}
38
+
39
+ # run tests!
40
+ - run:
41
+ name: run tests
42
+ command: |
43
+ mkdir /tmp/test-results
44
+ TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
45
+
46
+ bundle exec rspec --format progress \
47
+ $TEST_FILES
48
+
49
+ # collect reports
50
+ - store_test_results:
51
+ path: /tmp/test-results
52
+ - store_artifacts:
53
+ path: /tmp/test-results
54
+ destination: test-results
55
+
56
+ build-2.4.1:
57
+ docker:
58
+ - image: circleci/ruby:2.4.2
59
+
60
+ working_directory: ~/repo
61
+
62
+ steps:
63
+ - checkout
64
+
65
+ # Download and cache dependencies
66
+ - restore_cache:
67
+ keys:
68
+ - v1-dependencies-{{ checksum "Gemfile.lock" }}
69
+ # fallback to using the latest cache if no exact match is found
70
+ - v1-dependencies-
71
+
72
+ - run:
73
+ name: install dependencies
74
+ command: |
75
+ bundle install --jobs=4 --retry=3 --path vendor/bundle
76
+
77
+ - save_cache:
78
+ paths:
79
+ - ./vendor/bundle
80
+ key: v1-dependencies-{{ checksum "Gemfile.lock" }}
81
+
82
+ # run tests!
83
+ - run:
84
+ name: run tests
85
+ command: |
86
+ mkdir /tmp/test-results
87
+ TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
88
+
89
+ bundle exec rspec --format progress \
90
+ $TEST_FILES
91
+
92
+ # collect reports
93
+ - store_test_results:
94
+ path: /tmp/test-results
95
+ - store_artifacts:
96
+ path: /tmp/test-results
97
+ destination: test-results
98
+
99
+ workflows:
100
+ version: 2
101
+ build-multiple-targets:
102
+ jobs:
103
+ - build
104
+ - build-2.4.1
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.1
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in messaging-stash.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ messaging-stash (0.1.0)
5
+ phobos (>= 1.7.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activesupport (5.2.0)
11
+ concurrent-ruby (~> 1.0, >= 1.0.2)
12
+ i18n (>= 0.7, < 2)
13
+ minitest (~> 5.1)
14
+ tzinfo (~> 1.1)
15
+ concurrent-ruby (1.0.5)
16
+ concurrent-ruby-ext (1.0.5)
17
+ concurrent-ruby (= 1.0.5)
18
+ diff-lcs (1.3)
19
+ exponential-backoff (0.0.2)
20
+ i18n (1.0.0)
21
+ concurrent-ruby (~> 1.0)
22
+ little-plugger (1.1.4)
23
+ logging (2.2.2)
24
+ little-plugger (~> 1.1)
25
+ multi_json (~> 1.10)
26
+ minitest (5.11.3)
27
+ multi_json (1.13.1)
28
+ phobos (1.7.1)
29
+ activesupport (>= 3.0.0)
30
+ concurrent-ruby (>= 1.0.2)
31
+ concurrent-ruby-ext (>= 1.0.2)
32
+ exponential-backoff
33
+ logging
34
+ ruby-kafka (>= 0.3.14)
35
+ thor
36
+ rake (10.5.0)
37
+ rspec (3.7.0)
38
+ rspec-core (~> 3.7.0)
39
+ rspec-expectations (~> 3.7.0)
40
+ rspec-mocks (~> 3.7.0)
41
+ rspec-core (3.7.1)
42
+ rspec-support (~> 3.7.0)
43
+ rspec-expectations (3.7.0)
44
+ diff-lcs (>= 1.2.0, < 2.0)
45
+ rspec-support (~> 3.7.0)
46
+ rspec-mocks (3.7.0)
47
+ diff-lcs (>= 1.2.0, < 2.0)
48
+ rspec-support (~> 3.7.0)
49
+ rspec-support (3.7.1)
50
+ ruby-kafka (0.5.5)
51
+ thor (0.20.0)
52
+ thread_safe (0.3.6)
53
+ tzinfo (1.2.5)
54
+ thread_safe (~> 0.1)
55
+
56
+ PLATFORMS
57
+ ruby
58
+
59
+ DEPENDENCIES
60
+ bundler (~> 1.16)
61
+ messaging-stash!
62
+ rake (~> 10.0)
63
+ rspec (~> 3.0)
64
+
65
+ BUNDLED WITH
66
+ 1.16.1
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Messaging::Stash
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/messaging/stash`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'messaging-stash'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install messaging-stash
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/messaging-stash.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "messaging/stash"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,43 @@
1
+ module Messaging
2
+ module Kafka
3
+ module MessageLogger
4
+ class << self
5
+
6
+ def message_published(topic, payload)
7
+ logger.info("Message Published. Topic: #{topic}, Payload: #{payload}")
8
+ end
9
+
10
+ def message_received(payload, metadata)
11
+ logger.info("Message Received. #{message_processed_or_received_string(payload, metadata)}")
12
+ end
13
+
14
+ def message_processed(payload, metadata)
15
+ logger.info("Message Processed. #{message_processed_or_received_string(payload, metadata)}")
16
+ end
17
+
18
+ def message_publish_failed(message, exception)
19
+ logger.error("Message Publish Error. Topic: #{message.topic}, Body: #{message.body_json}, Exception Class: #{exception.class} Exception: #{exception}")
20
+ end
21
+
22
+ def message_publish_list_failed(messages, exception)
23
+ logger.error("Message Publish List Error. Messages: #{messages}, Exception Class: #{exception.class}, Exception: #{exception}")
24
+ end
25
+
26
+ def message_buffer_flushed_on_shutdown
27
+ logger.info("Published any pending messages before shutdown.")
28
+ end
29
+
30
+ def logger
31
+ @logger ||= Logger.new(STDOUT)
32
+ end
33
+
34
+ private
35
+
36
+ def message_processed_or_received_string(payload, metadata)
37
+ "#{metadata[:handler]}. payload: #{payload}, metadata: #{metadata}"
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,41 @@
1
+ require 'json'
2
+ module Messaging
3
+ module Kafka
4
+ class MessageReceiver < Messaging::Messages::MessageReceiver
5
+ include Phobos::Handler
6
+ include Phobos::Producer
7
+
8
+ ERROR_THRESHOLD = 5
9
+
10
+ def consume(payload, metadata)
11
+ begin
12
+ # metadata comes through as a hash, no need to parse
13
+ payload_hash = JSON.parse(payload)
14
+ Messaging::Kafka::MessageLogger.message_received(payload_hash, metadata)
15
+ process(payload_hash, metadata)
16
+ Messaging::Kafka::MessageLogger.message_processed(payload_hash, metadata)
17
+ rescue JSON::ParserError => e
18
+ # A json parse error cannot be recovered from. Log the error and the body.
19
+ self.class.logger.error("MessageReceiver JSON::ParserError. payload: #{payload}, error: #{e}")
20
+ rescue => e
21
+ if metadata[:retry_count] > ERROR_THRESHOLD
22
+ # move the message to the poison topic if the threshold is reached.
23
+ # message = Messages::Events::MessagePoisoned.new(payload: payload_hash, listener: metadata[:handler])
24
+ # Messaging::Kafka::MessageSender.publish(message)
25
+ self.class.logger.error('Message poisoned')
26
+ else
27
+ raise
28
+ end
29
+ end
30
+ end
31
+
32
+ def process(payload, metadata)
33
+ raise NotImplementedError
34
+ end
35
+
36
+ def self.logger
37
+ @logger ||= Logger.new(STDOUT)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,92 @@
1
+ require 'phobos'
2
+
3
+ module Messaging
4
+ module Kafka
5
+ class MessageSender < Messaging::Messages::MessageSender
6
+ include Phobos::Producer
7
+ class << self
8
+
9
+ # .publish will attempt to publish a single message synchronously.
10
+ # A publish failure will not retry.
11
+ # Use sparingly.
12
+ def publish(message)
13
+ return unless publish_message?
14
+ begin
15
+ producer.publish(message.topic, message.body_json)
16
+ rescue => e
17
+ Messaging::Kafka::MessageLogger.message_publish_failed(message, e)
18
+ end
19
+ end
20
+
21
+ # .publish_list will attempt to publish a single message synchronously.
22
+ # A publish_list failure will not retry.
23
+ # Use sparingly.
24
+ def publish_list(messages)
25
+ return unless publish_message?
26
+ begin
27
+ message_list = build_messages(messages)
28
+ producer.publish_list(message_list)
29
+ rescue => e
30
+ Messaging::Kafka::MessageLogger.message_publish_list_failed(messages, e)
31
+ end
32
+ end
33
+
34
+ # .async_publish will buffer a message to be sent according to the asynchronous delivery configuration.
35
+ # This method will return immediately.
36
+ def async_publish(message)
37
+ return unless publish_message?
38
+ async_publish_list([message])
39
+ end
40
+
41
+ # .async_publish_list will buffer a message to be sent according to the asynchronous delivery configuration.
42
+ # This method will return immediately.
43
+ def async_publish_list(messages)
44
+ return unless publish_message?
45
+ message_list = build_messages(messages)
46
+ init_async_producer
47
+ producer.async_publish_list(message_list)
48
+ end
49
+
50
+ # Shutdown and publish any buffered messages to Kafka.
51
+ # This should be called before the application closes.
52
+ def async_producer_shutdown
53
+ async_producers.each do |async_producer|
54
+ async_producer&.deliver_messages
55
+ async_producer&.shutdown
56
+ end
57
+ Messaging::Kafka::MessageLogger.message_buffer_flushed_on_shutdown
58
+ end
59
+
60
+ private
61
+
62
+ # Force Phobos to create a new asynchronous producer if it doesn't already exist and add it to our list.
63
+ # Phobos will maintain a producer for each thread publishing messages.
64
+ def init_async_producer
65
+ async_producer = producer.async_producer || producer.create_async_producer
66
+ @async_producers << async_producer unless async_producers.include?(async_producer)
67
+ end
68
+
69
+ def build_messages(messages)
70
+ messages.map do |message|
71
+ {
72
+ topic: message.topic,
73
+ payload: message.body_json,
74
+ key: nil
75
+ }
76
+ end
77
+ end
78
+
79
+ def publish_message?
80
+ true
81
+ # Flipper.enabled?(:publish_messages_to_kafka)
82
+ end
83
+
84
+ # A list of Phobos created asynchronous producers.
85
+ # This is an array of Kafka::AsyncProducer from the ruby-kafka gem.
86
+ def async_producers
87
+ @async_producers ||= []
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,40 @@
1
+ module Messaging
2
+ module Messages
3
+ class Message
4
+
5
+ def self.publish(options = {})
6
+ message = self.new(options)
7
+ Messaging::Kafka::MessageSender.publish(message)
8
+ end
9
+
10
+ def self.async_publish(options = {})
11
+ message = self.new(options)
12
+ Messaging::Kafka::MessageSender.async_publish(message)
13
+ end
14
+
15
+ def initialize(options = {})
16
+ ;
17
+ end
18
+
19
+ def topic
20
+ ENV.fetch('KAFKA_PREFIX', '') + topic_name
21
+ end
22
+
23
+ def body_json
24
+ body.merge({ message_id: message_identifier }).to_json
25
+ end
26
+
27
+ def message_identifier
28
+ SecureRandom.uuid
29
+ end
30
+
31
+ def topic_name
32
+ raise NotImplementedError
33
+ end
34
+
35
+ def body
36
+ raise NotImplementedError
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,10 @@
1
+ module Messaging
2
+ module Messages
3
+ class MessageReceiver
4
+
5
+ def consume(payload, metadata)
6
+ raise NotImplementedError
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ module Messaging
2
+ module Messages
3
+ class MessageSender
4
+
5
+ def self.publish(message)
6
+ raise NotImplementedError
7
+ end
8
+
9
+ def self.publish_list(messages)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ def self.async_publish(message)
14
+ raise NotImplementedError
15
+ end
16
+
17
+ def self.async_publish_list(messages)
18
+ raise NotImplementedError
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ require "messaging/stash/version"
2
+ require "messaging/messages/message_sender"
3
+ require "messaging/messages/message_receiver"
4
+ require "messaging/messages/message"
5
+ require "messaging/kafka/message_logger"
6
+ require "messaging/kafka/message_sender"
7
+ require "messaging/kafka/message_receiver"
8
+ require "phobos"
9
+ require "logger"
10
+
11
+ module Messaging
12
+ module Stash
13
+ # Your code goes here...
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module Messaging
2
+ module Stash
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,43 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "messaging/stash/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "messaging-stash"
8
+ spec.version = Messaging::Stash::VERSION
9
+ spec.authors = [
10
+ "Alex Kaufman",
11
+ "Nicholas Balsamo"
12
+ ]
13
+ spec.email = [
14
+ "alex@stashinvest.com",
15
+ "nbalsambo@stashinvest.com"
16
+ ]
17
+
18
+ spec.summary = %q{Phobos client for stash}
19
+ spec.description = %q{This contains all the code for using phobos in a ruby project}
20
+ spec.homepage = "https://github.com/stashinvest"
21
+
22
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
23
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
24
+ # if spec.respond_to?(:metadata)
25
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
26
+ # else
27
+ # raise "RubyGems 2.0 or newer is required to protect against " \
28
+ # "public gem pushes."
29
+ # end
30
+
31
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
32
+ f.match(%r{^(test|spec|features)/})
33
+ end
34
+ spec.bindir = "exe"
35
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
+ spec.require_paths = ["lib"]
37
+
38
+ spec.add_development_dependency "bundler", "~> 1.16"
39
+ spec.add_development_dependency "rake", "~> 10.0"
40
+ spec.add_development_dependency "rspec", "~> 3.0"
41
+
42
+ spec.add_dependency "phobos", ">= 1.7.1"
43
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: messaging-stash
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alex Kaufman
8
+ - Nicholas Balsamo
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2018-04-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.16'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.16'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '10.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: phobos
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.7.1
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 1.7.1
70
+ description: This contains all the code for using phobos in a ruby project
71
+ email:
72
+ - alex@stashinvest.com
73
+ - nbalsambo@stashinvest.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - ".circleci/config.yml"
79
+ - ".gitignore"
80
+ - ".rspec"
81
+ - ".travis.yml"
82
+ - Gemfile
83
+ - Gemfile.lock
84
+ - README.md
85
+ - Rakefile
86
+ - bin/console
87
+ - bin/setup
88
+ - lib/messaging/kafka/message_logger.rb
89
+ - lib/messaging/kafka/message_receiver.rb
90
+ - lib/messaging/kafka/message_sender.rb
91
+ - lib/messaging/messages/message.rb
92
+ - lib/messaging/messages/message_receiver.rb
93
+ - lib/messaging/messages/message_sender.rb
94
+ - lib/messaging/stash.rb
95
+ - lib/messaging/stash/version.rb
96
+ - messaging-stash.gemspec
97
+ homepage: https://github.com/stashinvest
98
+ licenses: []
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.7.6
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: Phobos client for stash
120
+ test_files: []