emque-producing 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +64 -0
- data/LICENSE.txt +22 -0
- data/README.md +98 -0
- data/Rakefile +10 -0
- data/emque-producing.gemspec +33 -0
- data/lib/emque-producing.rb +1 -0
- data/lib/emque/producing.rb +8 -0
- data/lib/emque/producing/configuration.rb +24 -0
- data/lib/emque/producing/logging.rb +25 -0
- data/lib/emque/producing/message/message.rb +146 -0
- data/lib/emque/producing/producing.rb +43 -0
- data/lib/emque/producing/publisher/base.rb +23 -0
- data/lib/emque/producing/publisher/kafka.rb +25 -0
- data/lib/emque/producing/publisher/rabbitmq.rb +58 -0
- data/lib/emque/producing/version.rb +5 -0
- data/spec/bin/kafka-run-class.sh +67 -0
- data/spec/kafka_spec_helper.rb +33 -0
- data/spec/producing/configuration_spec.rb +21 -0
- data/spec/producing/message/message_spec.rb +88 -0
- data/spec/producing/producing_spec.rb +5 -0
- data/spec/producing/publisher_spec.rb +20 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/test_cluster.rb +201 -0
- metadata +191 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cc3013b37568f755a5f258d0c18b3a8e346ac219
|
4
|
+
data.tar.gz: 28aad21a17a39be148e7d22611df8b4d992c85f6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 997635efddaa40fe279c246a3862917666b4c4d154b6809c9c69e7fe6e3a4281ffac27e8a1638c8556467f764be2cdb62a9b8d67edaf90331541e2d581b733da
|
7
|
+
data.tar.gz: 5cebfd302c30b90c1863555cd5b7fce581d67d1278310a05d6ad13f4bf71e573bf9a50311204da6a74e7108de3d7f735654db85905639bf621b14f982e68dfda
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
# Gemfile.lock
|
30
|
+
# .ruby-version
|
31
|
+
# .ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
emque-producing (0.0.2)
|
5
|
+
oj (~> 2.10.2)
|
6
|
+
virtus (~> 1.0.3)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
amq-protocol (1.9.2)
|
12
|
+
axiom-types (0.1.1)
|
13
|
+
descendants_tracker (~> 0.0.4)
|
14
|
+
ice_nine (~> 0.11.0)
|
15
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
16
|
+
bunny (1.4.1)
|
17
|
+
amq-protocol (>= 1.9.2)
|
18
|
+
coderay (1.1.0)
|
19
|
+
coercible (1.0.0)
|
20
|
+
descendants_tracker (~> 0.0.1)
|
21
|
+
descendants_tracker (0.0.4)
|
22
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
23
|
+
diff-lcs (1.2.5)
|
24
|
+
equalizer (0.0.9)
|
25
|
+
ice_nine (0.11.0)
|
26
|
+
method_source (0.8.2)
|
27
|
+
oj (2.10.2)
|
28
|
+
poseidon (0.0.4)
|
29
|
+
pry (0.9.12.4)
|
30
|
+
coderay (~> 1.0)
|
31
|
+
method_source (~> 0.8)
|
32
|
+
slop (~> 3.4)
|
33
|
+
rake (10.1.1)
|
34
|
+
rspec (3.1.0)
|
35
|
+
rspec-core (~> 3.1.0)
|
36
|
+
rspec-expectations (~> 3.1.0)
|
37
|
+
rspec-mocks (~> 3.1.0)
|
38
|
+
rspec-core (3.1.5)
|
39
|
+
rspec-support (~> 3.1.0)
|
40
|
+
rspec-expectations (3.1.2)
|
41
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
42
|
+
rspec-support (~> 3.1.0)
|
43
|
+
rspec-mocks (3.1.2)
|
44
|
+
rspec-support (~> 3.1.0)
|
45
|
+
rspec-support (3.1.1)
|
46
|
+
slop (3.4.7)
|
47
|
+
thread_safe (0.3.4)
|
48
|
+
virtus (1.0.3)
|
49
|
+
axiom-types (~> 0.1)
|
50
|
+
coercible (~> 1.0)
|
51
|
+
descendants_tracker (~> 0.0, >= 0.0.3)
|
52
|
+
equalizer (~> 0.0, >= 0.0.9)
|
53
|
+
|
54
|
+
PLATFORMS
|
55
|
+
ruby
|
56
|
+
|
57
|
+
DEPENDENCIES
|
58
|
+
bundler (~> 1.0)
|
59
|
+
bunny (~> 1.4.1)
|
60
|
+
emque-producing!
|
61
|
+
poseidon (= 0.0.4)
|
62
|
+
pry
|
63
|
+
rake
|
64
|
+
rspec (~> 3.1)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 TeamSnap
|
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,98 @@
|
|
1
|
+
[ ![Codeship Status for
|
2
|
+
teamsnap/emque-producing](https://www.codeship.io/projects/2ca7fd90-1785-0132-5f9d-7ab39a5c8237/status)](https://www.codeship.io/projects/34115)
|
3
|
+
|
4
|
+
# Emque Producing
|
5
|
+
|
6
|
+
Define and send messages with Ruby to a variety of [message brokers](http://en.wikipedia.org/wiki/Message_broker).
|
7
|
+
Currently supported message brokers are [RabbitMQ](https://www.rabbitmq.com) and
|
8
|
+
[Kafka](http://kafka.apache.org/).
|
9
|
+
|
10
|
+
This is a library that pairs nicely with [Emque
|
11
|
+
Consuming](https://www.github.com/teamsnap/emque-consuming), a framework for
|
12
|
+
consuming and routing messages to your code.
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add these lines to your application's Gemfile, depending on your message broker:
|
17
|
+
|
18
|
+
# for RabbitMQ, bunny is used
|
19
|
+
gem "emque-producing"
|
20
|
+
gem "bunny", "~> 1.4.1"
|
21
|
+
|
22
|
+
or
|
23
|
+
|
24
|
+
# for Kafka, poseidon is used
|
25
|
+
gem "emque-producing"
|
26
|
+
gem "poseidon", "0.0.4"
|
27
|
+
|
28
|
+
And then execute:
|
29
|
+
|
30
|
+
$ bundle
|
31
|
+
|
32
|
+
Or install it yourself as:
|
33
|
+
|
34
|
+
$ gem install emque-producing
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
# configure (likely in a Rails initializer)
|
39
|
+
require 'emque-producing'
|
40
|
+
Emque::Producing.configure do |c|
|
41
|
+
c.app_name = "app"
|
42
|
+
c.publishing_adapter = :rabbitmq
|
43
|
+
c.rabbitmq_options = { :url => "amqp://guest:guest@localhost:5672" }
|
44
|
+
#c.kafka_options = { :seed_brokers => [localhost:9092],
|
45
|
+
# :producer_options => {} }
|
46
|
+
c.error_handlers << Proc.new {|ex,context|
|
47
|
+
# notify/log
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
# create a message class
|
52
|
+
class MyMessage
|
53
|
+
include Emque::Producing::Message
|
54
|
+
topic "topic1"
|
55
|
+
message_type "mymessage.new"
|
56
|
+
|
57
|
+
attribute :first_property, Integer, :required => true
|
58
|
+
attribute :another_property, String, :required => true
|
59
|
+
end
|
60
|
+
|
61
|
+
# produce message
|
62
|
+
message = MyMessage.new({:first_property => 1, :another_property => "another"})
|
63
|
+
message.publish
|
64
|
+
|
65
|
+
## Requirements
|
66
|
+
|
67
|
+
* Ruby 1.9.3 or higher
|
68
|
+
* RabbitMQ 3.x
|
69
|
+
* Bunny 1.4.x
|
70
|
+
* Kafka 0.8.1
|
71
|
+
* Poseidon 0.0.4
|
72
|
+
|
73
|
+
## Tests
|
74
|
+
|
75
|
+
To run tests...
|
76
|
+
|
77
|
+
```
|
78
|
+
rspec
|
79
|
+
```
|
80
|
+
|
81
|
+
If you would like to test the gem as part of your client, you can update the
|
82
|
+
configuration option `publish_messages` to false like so:
|
83
|
+
```ruby
|
84
|
+
Emque::Producing.configure do |c|
|
85
|
+
c.publish_messages = false
|
86
|
+
...other options
|
87
|
+
end
|
88
|
+
```
|
89
|
+
This will prevent Emque from actually attempting to make the connection to your
|
90
|
+
adapter which may be convenient in the case of CI environments.
|
91
|
+
|
92
|
+
## Contributing
|
93
|
+
|
94
|
+
1. Fork it ( http://github.com/teamsnap/emquemessages/fork )
|
95
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
96
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
97
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
98
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib/', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'emque/producing/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "emque-producing"
|
8
|
+
spec.version = Emque::Producing::VERSION
|
9
|
+
spec.platform = Gem::Platform::RUBY
|
10
|
+
spec.authors = ["Emily Dobervich", "Ryan Williams"]
|
11
|
+
spec.email = ["emily@teamsnap.com", "ryan.williams@teamsnap.com"]
|
12
|
+
spec.summary = %q{Define and send messages to a variety of message brokers}
|
13
|
+
spec.description = %q{Define and send messages to a variety of message brokers}
|
14
|
+
spec.homepage = ""
|
15
|
+
spec.license = "MIT"
|
16
|
+
spec.required_ruby_version = '>= 1.9.3'
|
17
|
+
|
18
|
+
# Manifest
|
19
|
+
spec.files = `git ls-files`.split("\n")
|
20
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "oj", "~> 2.10.2"
|
25
|
+
spec.add_dependency "virtus", "~> 1.0.3"
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.0"
|
28
|
+
spec.add_development_dependency "rake"
|
29
|
+
spec.add_development_dependency "rspec", "~> 3.1"
|
30
|
+
spec.add_development_dependency "pry"
|
31
|
+
spec.add_development_dependency "poseidon", "0.0.4"
|
32
|
+
spec.add_development_dependency "bunny", "~> 1.4.1"
|
33
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "emque/producing"
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require "virtus"
|
2
|
+
require "oj"
|
3
|
+
require "emque/producing/version"
|
4
|
+
require "emque/producing/producing"
|
5
|
+
require "emque/producing/configuration"
|
6
|
+
require "emque/producing/logging"
|
7
|
+
require "emque/producing/publisher/base"
|
8
|
+
require "emque/producing/message/message"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Emque
|
2
|
+
module Producing
|
3
|
+
class Configuration
|
4
|
+
attr_accessor :app_name
|
5
|
+
attr_accessor :publishing_adapter
|
6
|
+
attr_accessor :kafka_options
|
7
|
+
attr_accessor :rabbitmq_options
|
8
|
+
attr_accessor :error_handlers
|
9
|
+
attr_accessor :log_publish_message
|
10
|
+
attr_accessor :publish_messages
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@app_name = ""
|
14
|
+
@publishing_adapter = :rabbitmq
|
15
|
+
@error_handlers = []
|
16
|
+
@log_publish_message = false
|
17
|
+
@publish_messages = true
|
18
|
+
@kafka_options = { :seed_brokers => ["localhost:9092"],
|
19
|
+
:producer_options => {} }
|
20
|
+
@rabbitmq_options = { :url => "amqp://guest:guest@localhost:5672" }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module Emque
|
4
|
+
module Producing
|
5
|
+
class Logging
|
6
|
+
def self.initialize_logger(log_target = STDOUT)
|
7
|
+
@logger = Logger.new(log_target)
|
8
|
+
@logger.level = Logger::INFO
|
9
|
+
@logger
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.logger
|
13
|
+
defined?(@logger) ? @logger : initialize_logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.logger=(log)
|
17
|
+
@logger = log || Logger.new("/dev/null")
|
18
|
+
end
|
19
|
+
|
20
|
+
def logger
|
21
|
+
Emque::Producing::Logging.logger
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Emque
|
4
|
+
module Producing
|
5
|
+
module Message
|
6
|
+
InvalidMessageError = Class.new(StandardError)
|
7
|
+
MessagesNotSentError = Class.new(StandardError)
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def topic(name)
|
11
|
+
@topic = name
|
12
|
+
end
|
13
|
+
|
14
|
+
def read_topic
|
15
|
+
@topic
|
16
|
+
end
|
17
|
+
|
18
|
+
def message_type(name)
|
19
|
+
@message_type = name
|
20
|
+
end
|
21
|
+
|
22
|
+
def read_message_type
|
23
|
+
@message_type
|
24
|
+
end
|
25
|
+
|
26
|
+
def private_attribute(name, coercion=nil, opts={})
|
27
|
+
@private_attrs ||= []
|
28
|
+
@private_attrs << name
|
29
|
+
attribute(name, coercion, opts)
|
30
|
+
end
|
31
|
+
|
32
|
+
def private_attrs
|
33
|
+
Array(@private_attrs)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.included(base)
|
38
|
+
base.extend(ClassMethods)
|
39
|
+
base.send(:include, Virtus.model)
|
40
|
+
base.send(:attribute, :partition_key, String, :default => nil, :required => false)
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_metadata
|
44
|
+
{
|
45
|
+
:metadata =>
|
46
|
+
{
|
47
|
+
:host => host_name,
|
48
|
+
:app => app_name,
|
49
|
+
:topic => topic,
|
50
|
+
:created_at => formatted_time,
|
51
|
+
:uuid => uuid,
|
52
|
+
:type => message_type,
|
53
|
+
:partition_key => partition_key
|
54
|
+
}
|
55
|
+
}.merge(public_attributes)
|
56
|
+
end
|
57
|
+
|
58
|
+
def topic
|
59
|
+
self.class.read_topic
|
60
|
+
end
|
61
|
+
|
62
|
+
def message_type
|
63
|
+
self.class.read_message_type
|
64
|
+
end
|
65
|
+
|
66
|
+
def valid?
|
67
|
+
invalid_attributes.empty? && topic && message_type
|
68
|
+
end
|
69
|
+
|
70
|
+
def invalid_attributes
|
71
|
+
invalid_attrs = self.class.attribute_set.inject([]) do |attrs, attr|
|
72
|
+
attrs << attr.name if attr.required? && self.attributes.fetch(attr.name).nil?
|
73
|
+
attrs
|
74
|
+
end
|
75
|
+
Array(invalid_attrs) - self.class.private_attrs
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_json
|
79
|
+
data = self.add_metadata
|
80
|
+
Oj.dump(data, :mode => :compat)
|
81
|
+
end
|
82
|
+
|
83
|
+
def publish(publisher=Emque::Producing.publisher)
|
84
|
+
log "publishing...", true
|
85
|
+
if valid?
|
86
|
+
log "valid...", true
|
87
|
+
if Emque::Producing.configuration.publish_messages
|
88
|
+
sent = publisher.publish(topic, message_type, to_json, partition_key)
|
89
|
+
log "sent #{sent}"
|
90
|
+
raise MessagesNotSentError.new unless sent
|
91
|
+
end
|
92
|
+
else
|
93
|
+
log "failed...", true
|
94
|
+
raise InvalidMessageError.new(invalid_message)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def invalid_message
|
101
|
+
if !topic
|
102
|
+
"A topic is required"
|
103
|
+
elsif !message_type
|
104
|
+
"A message type is required"
|
105
|
+
else
|
106
|
+
"Required attributes #{invalid_attributes} are missing."
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def host_name
|
111
|
+
Socket.gethostbyname(Socket.gethostname).first
|
112
|
+
end
|
113
|
+
|
114
|
+
def formatted_time
|
115
|
+
DateTime.now.new_offset(0).to_time.utc.iso8601
|
116
|
+
end
|
117
|
+
|
118
|
+
def uuid
|
119
|
+
SecureRandom.uuid
|
120
|
+
end
|
121
|
+
|
122
|
+
def app_name
|
123
|
+
Emque::Producing.configuration.app_name || raise("Messages must have an app name configured.")
|
124
|
+
end
|
125
|
+
|
126
|
+
def log(message, include_message = false)
|
127
|
+
if Emque::Producing.configuration.log_publish_message
|
128
|
+
message = "#{message} #{to_json}" if include_message
|
129
|
+
Emque::Producing.logger.info("MESSAGE LOG: #{message}")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def public_attributes
|
134
|
+
public = self.class.attribute_set.select do |attr|
|
135
|
+
attr && !self.class.private_attrs.include?(attr.name)
|
136
|
+
end.map(&:name)
|
137
|
+
slice_attributes(*public)
|
138
|
+
end
|
139
|
+
|
140
|
+
def slice_attributes(*keys)
|
141
|
+
keys.map!(&:to_sym)
|
142
|
+
attributes.select { |key, value| keys.include?(key) }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Emque
|
2
|
+
module Producing
|
3
|
+
class << self
|
4
|
+
attr_accessor :publisher
|
5
|
+
attr_writer :configuration
|
6
|
+
|
7
|
+
def configure
|
8
|
+
yield(configuration)
|
9
|
+
end
|
10
|
+
|
11
|
+
def configuration
|
12
|
+
@configuration ||= Emque::Producing::Configuration.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def host_name
|
16
|
+
Socket.gethostbyname(Socket.gethostname).first
|
17
|
+
end
|
18
|
+
|
19
|
+
def publisher
|
20
|
+
return @publisher unless @publisher.nil?
|
21
|
+
|
22
|
+
if (configuration.publishing_adapter == :kafka)
|
23
|
+
require "emque/producing/publisher/kafka"
|
24
|
+
@publisher = Emque::Producing::Publisher::Kafka.new
|
25
|
+
elsif (configuration.publishing_adapter == :rabbitmq)
|
26
|
+
require "emque/producing/publisher/rabbitmq"
|
27
|
+
@publisher = Emque::Producing::Publisher::RabbitMq.new
|
28
|
+
else
|
29
|
+
raise "No publisher configured"
|
30
|
+
end
|
31
|
+
@publisher
|
32
|
+
end
|
33
|
+
|
34
|
+
def logger
|
35
|
+
Emque::Producing::Logging.logger
|
36
|
+
end
|
37
|
+
|
38
|
+
def logger=(log)
|
39
|
+
Emque::Producing::Logging.logger = log
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Emque
|
2
|
+
module Producing
|
3
|
+
module Publisher
|
4
|
+
class Base
|
5
|
+
def host_name
|
6
|
+
Socket.gethostbyname(Socket.gethostname).first
|
7
|
+
end
|
8
|
+
|
9
|
+
def handle_error(e)
|
10
|
+
Emque::Producing.configuration.error_handlers.each do |handler|
|
11
|
+
begin
|
12
|
+
handler.call(e, nil)
|
13
|
+
rescue => ex
|
14
|
+
Emque::Producing.logger.error "Producer error hander raised an error"
|
15
|
+
Emque::Producing.logger.error ex
|
16
|
+
Emque::Producing.logger.error Array(ex.backtrace).join("\n")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "poseidon"
|
2
|
+
|
3
|
+
module Emque
|
4
|
+
module Producing
|
5
|
+
module Publisher
|
6
|
+
class Kafka < Emque::Producing::Publisher::Base
|
7
|
+
def initialize
|
8
|
+
@producer = Poseidon::Producer.new(
|
9
|
+
Emque::Producing.configuration.kafka_options[:seed_brokers],
|
10
|
+
"producer_#{host_name}_#{Process.pid}",
|
11
|
+
Emque::Producing.configuration.kafka_options[:producer_options])
|
12
|
+
end
|
13
|
+
|
14
|
+
def publish(topic, message_type, message, key = nil)
|
15
|
+
begin
|
16
|
+
msg = Poseidon::MessageToSend.new(topic, message, key)
|
17
|
+
@producer.send_messages([msg])
|
18
|
+
rescue => e
|
19
|
+
handle_error(e)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "bunny"
|
2
|
+
require "thread"
|
3
|
+
|
4
|
+
module Emque
|
5
|
+
module Producing
|
6
|
+
module Publisher
|
7
|
+
class RabbitMq < Emque::Producing::Publisher::Base
|
8
|
+
CONN = Bunny
|
9
|
+
.new(Emque::Producing.configuration.rabbitmq_options[:url])
|
10
|
+
.tap { |conn|
|
11
|
+
conn.start
|
12
|
+
}
|
13
|
+
|
14
|
+
CHANNEL_POOL = Queue
|
15
|
+
.new
|
16
|
+
.tap { |queue|
|
17
|
+
20.times { |i| queue << CONN.create_channel }
|
18
|
+
}
|
19
|
+
|
20
|
+
def publish(topic, message_type, message, key = nil)
|
21
|
+
ch = CHANNEL_POOL.pop
|
22
|
+
ch.open if ch.closed?
|
23
|
+
begin
|
24
|
+
exchange = ch.fanout(topic, :durable => true, :auto_delete => false)
|
25
|
+
|
26
|
+
# Assumes all messages are mandatory in order to let callers know if
|
27
|
+
# the message was not sent. Uses publisher confirms to wait.
|
28
|
+
ch.confirm_select
|
29
|
+
sent = true
|
30
|
+
exchange.on_return do |return_info, properties, content|
|
31
|
+
sent = false
|
32
|
+
end
|
33
|
+
|
34
|
+
exchange.publish(
|
35
|
+
message,
|
36
|
+
:mandatory => true,
|
37
|
+
:persistent => true,
|
38
|
+
:type => message_type,
|
39
|
+
:app_id => Emque::Producing.configuration.app_name,
|
40
|
+
:content_type => "application/json")
|
41
|
+
|
42
|
+
success = ch.wait_for_confirms
|
43
|
+
unless success
|
44
|
+
Emque::Producing.logger.warn("RabbitMQ Publisher: message was nacked")
|
45
|
+
ch.nacked_set.each do |n|
|
46
|
+
Emque::Producing.logger.warn("message id: #{n}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
return sent
|
51
|
+
ensure
|
52
|
+
CHANNEL_POOL << ch unless ch.nil?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# Licensed to the Apache Software Foundation (ASF) under one or more
|
3
|
+
# contributor license agreements. See the NOTICE file distributed with
|
4
|
+
# this work for additional information regarding copyright ownership.
|
5
|
+
# The ASF licenses this file to You under the Apache License, Version 2.0
|
6
|
+
# (the "License"); you may not use this file except in compliance with
|
7
|
+
# the License. You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
if [ $# -lt 1 ];
|
18
|
+
then
|
19
|
+
echo "USAGE: $0 classname [opts]"
|
20
|
+
exit 1
|
21
|
+
fi
|
22
|
+
|
23
|
+
if [-z "$SCALA_VERSION" ]; then
|
24
|
+
SCALA_VERSION=2.8.0
|
25
|
+
fi
|
26
|
+
|
27
|
+
# assume all dependencies have been packaged into one jar with sbt-assembly's task "assembly-package-dependency"
|
28
|
+
for file in $KAFKA_PATH/core/target/scala-$SCALA_VERSION/*.jar;
|
29
|
+
do
|
30
|
+
CLASSPATH=$CLASSPATH:$file
|
31
|
+
done
|
32
|
+
|
33
|
+
for file in $KAFKA_PATH/perf/target/scala-$SCALA_VERSION/kafka*.jar;
|
34
|
+
do
|
35
|
+
CLASSPATH=$CLASSPATH:$file
|
36
|
+
done
|
37
|
+
|
38
|
+
# classpath addition for release
|
39
|
+
for file in $KAFKA_PATH/libs/*.jar;
|
40
|
+
do
|
41
|
+
CLASSPATH=$CLASSPATH:$file
|
42
|
+
done
|
43
|
+
|
44
|
+
for file in $KAFKA_PATH/kafka*.jar;
|
45
|
+
do
|
46
|
+
CLASSPATH=$CLASSPATH:$file
|
47
|
+
done
|
48
|
+
|
49
|
+
if [ -z "$KAFKA_JMX_OPTS" ]; then
|
50
|
+
KAFKA_JMX_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false "
|
51
|
+
fi
|
52
|
+
|
53
|
+
if [ -z "$KAFKA_OPTS" ]; then
|
54
|
+
KAFKA_OPTS="-Xmx512M -server -Dlog4j.configuration=file:$KAFKA_PATH/config/log4j.properties"
|
55
|
+
fi
|
56
|
+
|
57
|
+
if [ $JMX_PORT ]; then
|
58
|
+
KAFKA_JMX_OPTS="$KAFKA_JMX_OPTS -Dcom.sun.management.jmxremote.port=$JMX_PORT "
|
59
|
+
fi
|
60
|
+
|
61
|
+
if [ -z "$JAVA_HOME" ]; then
|
62
|
+
JAVA="java"
|
63
|
+
else
|
64
|
+
JAVA="$JAVA_HOME/bin/java"
|
65
|
+
fi
|
66
|
+
|
67
|
+
exec $JAVA $KAFKA_OPTS $KAFKA_JMX_OPTS -cp $CLASSPATH "$@"
|
@@ -0,0 +1,33 @@
|
|
1
|
+
ROOT_DIRECTORY = File.absolute_path(File.dirname(__FILE__) + "/../")
|
2
|
+
|
3
|
+
require "test_cluster"
|
4
|
+
require "spec_helper"
|
5
|
+
|
6
|
+
unless File.directory?(File.join(ROOT_DIRECTORY, "kafka_2.8.0-0.8.1"))
|
7
|
+
puts "\033[0;32m"
|
8
|
+
puts "*" * 83
|
9
|
+
puts "Downloading kafka"
|
10
|
+
puts "*" * 83
|
11
|
+
puts "\033[0;0m"
|
12
|
+
|
13
|
+
system(
|
14
|
+
"cd #{ROOT_DIRECTORY} && curl https://archive.apache.org/dist/kafka/0.8.1/kafka_2.8.0-0.8.1.tgz | tar xz"
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
ENV["KAFKA_PATH"] = File.join(ROOT_DIRECTORY, "kafka_2.8.0-0.8.1")
|
19
|
+
ENV["SCALA_VERSION"] = "2.8.0"
|
20
|
+
|
21
|
+
RSpec.configure do |config|
|
22
|
+
config.before(:suite) do
|
23
|
+
JavaRunner.remove_tmp
|
24
|
+
JavaRunner.set_kafka_path!
|
25
|
+
$tc = TestCluster.new
|
26
|
+
$tc.start
|
27
|
+
sleep 5
|
28
|
+
end
|
29
|
+
|
30
|
+
config.after(:suite) do
|
31
|
+
$tc.stop
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Emque::Producing::Configuration do
|
4
|
+
subject { Emque::Producing::Configuration.new }
|
5
|
+
|
6
|
+
it "provides default values" do
|
7
|
+
expect(subject.app_name).to eq ""
|
8
|
+
expect(subject.error_handlers).to eq []
|
9
|
+
expect(subject.kafka_options[:seed_brokers]).to eq ["localhost:9092"]
|
10
|
+
end
|
11
|
+
|
12
|
+
it "allows app_name to be overwritten" do
|
13
|
+
subject.app_name = "my app"
|
14
|
+
expect(subject.app_name).to eq "my app"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "allows seed_brokers to be overwritten" do
|
18
|
+
subject.kafka_options[:seed_brokers] = ["kafka1:9092", "kafka2:9092"]
|
19
|
+
expect(subject.kafka_options[:seed_brokers]).to eq ["kafka1:9092", "kafka2:9092"]
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "virtus"
|
3
|
+
require "emque/producing/message/message"
|
4
|
+
|
5
|
+
class TestMessage
|
6
|
+
include Emque::Producing::Message
|
7
|
+
|
8
|
+
topic "queue"
|
9
|
+
message_type "queue.new"
|
10
|
+
|
11
|
+
attribute :test_id, Integer, :required => true
|
12
|
+
private_attribute :extra, String, :default => "value"
|
13
|
+
end
|
14
|
+
|
15
|
+
class MessageNoTopic
|
16
|
+
include Emque::Producing::Message
|
17
|
+
message_type "testing"
|
18
|
+
end
|
19
|
+
|
20
|
+
class MessageNoType
|
21
|
+
include Emque::Producing::Message
|
22
|
+
topic "testing"
|
23
|
+
end
|
24
|
+
|
25
|
+
describe Emque::Producing::Message do
|
26
|
+
before do
|
27
|
+
Emque::Producing.configure do |c|
|
28
|
+
c.app_name = "apiv3"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#to_json" do
|
33
|
+
it "creates the metadata" do
|
34
|
+
message = TestMessage.new(:test_id => 1)
|
35
|
+
metadata = message.add_metadata[:metadata]
|
36
|
+
expect(metadata[:app]).to eql("apiv3")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "can be transformed to json" do
|
40
|
+
message = Oj.load(TestMessage.new().to_json)
|
41
|
+
expect(message["metadata"]["app"]).to eql("apiv3")
|
42
|
+
end
|
43
|
+
|
44
|
+
it "includes valid attributes in json" do
|
45
|
+
produced_message = TestMessage.new(:test_id => 1)
|
46
|
+
json = produced_message.to_json
|
47
|
+
consumed_message = Oj.load(json)
|
48
|
+
expect(consumed_message["test_id"]).to eql(1)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#retry" do
|
53
|
+
pending
|
54
|
+
end
|
55
|
+
|
56
|
+
it "validates the message for missing attributes" do
|
57
|
+
message = TestMessage.new()
|
58
|
+
expect(message).to_not be_valid
|
59
|
+
end
|
60
|
+
|
61
|
+
it "raises a useful message when trying to send an invalid message" do
|
62
|
+
message = TestMessage.new()
|
63
|
+
expected_error = Emque::Producing::Message::InvalidMessageError
|
64
|
+
expect{message.publish(->{})}.to raise_error(expected_error)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "validates that the message has a topic" do
|
68
|
+
message = MessageNoTopic.new
|
69
|
+
expected_error = Emque::Producing::Message::InvalidMessageError
|
70
|
+
expect{message.publish(->{})}.to raise_error(expected_error, "A topic is required")
|
71
|
+
end
|
72
|
+
|
73
|
+
it "validates that the message has a message type" do
|
74
|
+
message = MessageNoType.new
|
75
|
+
expected_error = Emque::Producing::Message::InvalidMessageError
|
76
|
+
expect{message.publish(->{})}.to raise_error(expected_error, "A message type is required")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "applys a uuid per message" do
|
80
|
+
message = TestMessage.new()
|
81
|
+
expect(message.add_metadata[:metadata][:uuid]).to_not be_nil
|
82
|
+
end
|
83
|
+
|
84
|
+
it "has the sub type in the metadata" do
|
85
|
+
message = TestMessage.new()
|
86
|
+
expect(message.add_metadata[:metadata][:type]).to eql("queue.new")
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "emque/producing/publisher/kafka"
|
3
|
+
|
4
|
+
describe Emque::Producing::Publisher do
|
5
|
+
describe "#publish" do
|
6
|
+
context "when error handler raises an exception" do
|
7
|
+
it "handles the exception" do
|
8
|
+
expect_any_instance_of(Poseidon::Producer). to receive(:send_messages).and_raise
|
9
|
+
Emque::Producing.configure do |c|
|
10
|
+
c.error_handlers << Proc.new {|ex,context|
|
11
|
+
raise "something"
|
12
|
+
}
|
13
|
+
end
|
14
|
+
Emque::Producing.logger = nil
|
15
|
+
publisher = Emque::Producing::Publisher::Kafka.new
|
16
|
+
publisher.publish("mytopic", "message.type", "mymessage")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
$TESTING = true
|
2
|
+
|
3
|
+
require "pry"
|
4
|
+
require "emque-producing"
|
5
|
+
|
6
|
+
ENV["EMQUE_ENV"] = "test"
|
7
|
+
|
8
|
+
module VerifyAndResetHelpers
|
9
|
+
def verify(object)
|
10
|
+
RSpec::Mocks.proxy_for(object).verify
|
11
|
+
end
|
12
|
+
|
13
|
+
def reset(object)
|
14
|
+
RSpec::Mocks.proxy_for(object).reset
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.order = "random"
|
20
|
+
|
21
|
+
config.include VerifyAndResetHelpers
|
22
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# from https://github.com/bpot/poseidon
|
2
|
+
|
3
|
+
require 'daemon_controller'
|
4
|
+
|
5
|
+
class TestCluster
|
6
|
+
attr_reader :broker, :zookeeper
|
7
|
+
def initialize
|
8
|
+
@zookeeper = ZookeeperRunner.new
|
9
|
+
@broker = BrokerRunner.new(0, 9092)
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
@zookeeper.start
|
14
|
+
@broker.start
|
15
|
+
end
|
16
|
+
|
17
|
+
def stop
|
18
|
+
@zookeeper.stop
|
19
|
+
@broker.stop
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class JavaRunner
|
24
|
+
def self.remove_tmp
|
25
|
+
FileUtils.rm_rf("#{ROOT_DIRECTORY}/tmp")
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.set_kafka_path!
|
29
|
+
JavaRunner.kafka_path = File.join(ROOT_DIRECTORY, "kafka-0.8.0-src")
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.kafka_path=(kafka_path)
|
33
|
+
@kafka_path = kafka_path
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.kafka_path
|
37
|
+
@kafka_path
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :pid
|
41
|
+
def initialize(id, start_cmd, port, properties = {})
|
42
|
+
@id = id
|
43
|
+
@properties = properties
|
44
|
+
@pid = nil
|
45
|
+
@start_cmd = start_cmd
|
46
|
+
@port = port
|
47
|
+
end
|
48
|
+
|
49
|
+
def start
|
50
|
+
write_properties
|
51
|
+
run
|
52
|
+
end
|
53
|
+
|
54
|
+
def stop
|
55
|
+
daemon_controller.stop
|
56
|
+
end
|
57
|
+
|
58
|
+
def without_process
|
59
|
+
stop
|
60
|
+
begin
|
61
|
+
yield
|
62
|
+
ensure
|
63
|
+
start
|
64
|
+
sleep 5
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def daemon_controller
|
71
|
+
@dc ||= DaemonController.new(
|
72
|
+
:identifier => @id,
|
73
|
+
:start_command => "#{@start_cmd} #{config_path} >>#{log_path} 2>&1 & echo $! > #{pid_path}",
|
74
|
+
:ping_command => [:tcp, '127.0.0.1', @port],
|
75
|
+
:pid_file => pid_path,
|
76
|
+
:log_file => log_path,
|
77
|
+
:start_timeout => 25
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def run
|
82
|
+
FileUtils.mkdir_p(log_dir)
|
83
|
+
FileUtils.mkdir_p(pid_dir)
|
84
|
+
daemon_controller.start
|
85
|
+
end
|
86
|
+
|
87
|
+
def write_properties
|
88
|
+
FileUtils.mkdir_p(config_dir)
|
89
|
+
File.open(config_path, "w+") do |f|
|
90
|
+
@properties.each do |k,v|
|
91
|
+
f.puts "#{k}=#{v}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def pid_path
|
97
|
+
"#{pid_dir}/#{@id}.pid"
|
98
|
+
end
|
99
|
+
|
100
|
+
def pid_dir
|
101
|
+
"#{file_path}/pid"
|
102
|
+
end
|
103
|
+
|
104
|
+
def log_path
|
105
|
+
"#{log_dir}/#{@id}.log"
|
106
|
+
end
|
107
|
+
|
108
|
+
def log_dir
|
109
|
+
"#{file_path}/log"
|
110
|
+
end
|
111
|
+
|
112
|
+
def config_path
|
113
|
+
"#{config_dir}/#{@id}.properties"
|
114
|
+
end
|
115
|
+
|
116
|
+
def config_dir
|
117
|
+
"#{file_path}/config"
|
118
|
+
end
|
119
|
+
|
120
|
+
def file_path
|
121
|
+
ROOT_DIRECTORY + "/tmp/"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class BrokerRunner
|
126
|
+
DEFAULT_PROPERTIES = {
|
127
|
+
"broker.id" => 0,
|
128
|
+
"port" => 9092,
|
129
|
+
"num.network.threads" => 2,
|
130
|
+
"num.io.threads" => 2,
|
131
|
+
"socket.send.buffer.bytes" => 1048576,
|
132
|
+
"socket.receive.buffer.bytes" => 1048576,
|
133
|
+
"socket.request.max.bytes" => 104857600,
|
134
|
+
"log.dir" => "#{ROOT_DIRECTORY}/tmp/kafka-logs",
|
135
|
+
"num.partitions" => 1,
|
136
|
+
"log.flush.interval.messages" => 10000,
|
137
|
+
"log.flush.interval.ms" => 1000,
|
138
|
+
"log.retention.hours" => 168,
|
139
|
+
"log.segment.bytes" => 536870912,
|
140
|
+
"log.cleanup.interval.mins" => 1,
|
141
|
+
"zookeeper.connect" => "localhost:2181",
|
142
|
+
"zookeeper.connection.timeout.ms" => 1000000,
|
143
|
+
"kafka.metrics.polling.interval.secs" => 5,
|
144
|
+
"kafka.metrics.reporters" => "kafka.metrics.KafkaCSVMetricsReporter",
|
145
|
+
"kafka.csv.metrics.dir" => "#{ROOT_DIRECTORY}/tmp/kafka_metrics",
|
146
|
+
"kafka.csv.metrics.reporter.enabled" => "false",
|
147
|
+
}
|
148
|
+
|
149
|
+
def initialize(id, port, partition_count = 1)
|
150
|
+
@id = id
|
151
|
+
@port = port
|
152
|
+
@jr = JavaRunner.new("broker_#{id}",
|
153
|
+
"#{ROOT_DIRECTORY}/spec/bin/kafka-run-class.sh kafka.Kafka",
|
154
|
+
port,
|
155
|
+
DEFAULT_PROPERTIES.merge(
|
156
|
+
"broker.id" => id,
|
157
|
+
"port" => port,
|
158
|
+
"log.dir" => "#{ROOT_DIRECTORY}/tmp/kafka-logs_#{id}",
|
159
|
+
"num.partitions" => partition_count
|
160
|
+
))
|
161
|
+
end
|
162
|
+
|
163
|
+
def pid
|
164
|
+
@jr.pid
|
165
|
+
end
|
166
|
+
|
167
|
+
def start
|
168
|
+
@jr.start
|
169
|
+
end
|
170
|
+
|
171
|
+
def stop
|
172
|
+
@jr.stop
|
173
|
+
end
|
174
|
+
|
175
|
+
def without_process
|
176
|
+
@jr.without_process { yield }
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
class ZookeeperRunner
|
181
|
+
def initialize
|
182
|
+
@jr = JavaRunner.new("zookeeper",
|
183
|
+
"#{ROOT_DIRECTORY}/spec/bin/kafka-run-class.sh org.apache.zookeeper.server.quorum.QuorumPeerMain",
|
184
|
+
2181,
|
185
|
+
:dataDir => "#{ROOT_DIRECTORY}/tmp/zookeeper",
|
186
|
+
:clientPort => 2181,
|
187
|
+
:maxClientCnxns => 0)
|
188
|
+
end
|
189
|
+
|
190
|
+
def pid
|
191
|
+
@jr.pid
|
192
|
+
end
|
193
|
+
|
194
|
+
def start
|
195
|
+
@jr.start
|
196
|
+
end
|
197
|
+
|
198
|
+
def stop
|
199
|
+
@jr.stop
|
200
|
+
end
|
201
|
+
end
|
metadata
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: emque-producing
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Emily Dobervich
|
8
|
+
- Ryan Williams
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-02-06 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: oj
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 2.10.2
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 2.10.2
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: virtus
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 1.0.3
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 1.0.3
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: bundler
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '1.0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '1.0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rake
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rspec
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '3.1'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '3.1'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: pry
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: poseidon
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - '='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 0.0.4
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - '='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 0.0.4
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: bunny
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - "~>"
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 1.4.1
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - "~>"
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 1.4.1
|
126
|
+
description: Define and send messages to a variety of message brokers
|
127
|
+
email:
|
128
|
+
- emily@teamsnap.com
|
129
|
+
- ryan.williams@teamsnap.com
|
130
|
+
executables: []
|
131
|
+
extensions: []
|
132
|
+
extra_rdoc_files: []
|
133
|
+
files:
|
134
|
+
- ".gitignore"
|
135
|
+
- Gemfile
|
136
|
+
- Gemfile.lock
|
137
|
+
- LICENSE.txt
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- emque-producing.gemspec
|
141
|
+
- lib/emque-producing.rb
|
142
|
+
- lib/emque/producing.rb
|
143
|
+
- lib/emque/producing/configuration.rb
|
144
|
+
- lib/emque/producing/logging.rb
|
145
|
+
- lib/emque/producing/message/message.rb
|
146
|
+
- lib/emque/producing/producing.rb
|
147
|
+
- lib/emque/producing/publisher/base.rb
|
148
|
+
- lib/emque/producing/publisher/kafka.rb
|
149
|
+
- lib/emque/producing/publisher/rabbitmq.rb
|
150
|
+
- lib/emque/producing/version.rb
|
151
|
+
- spec/bin/kafka-run-class.sh
|
152
|
+
- spec/kafka_spec_helper.rb
|
153
|
+
- spec/producing/configuration_spec.rb
|
154
|
+
- spec/producing/message/message_spec.rb
|
155
|
+
- spec/producing/producing_spec.rb
|
156
|
+
- spec/producing/publisher_spec.rb
|
157
|
+
- spec/spec_helper.rb
|
158
|
+
- spec/test_cluster.rb
|
159
|
+
homepage: ''
|
160
|
+
licenses:
|
161
|
+
- MIT
|
162
|
+
metadata: {}
|
163
|
+
post_install_message:
|
164
|
+
rdoc_options: []
|
165
|
+
require_paths:
|
166
|
+
- lib
|
167
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
168
|
+
requirements:
|
169
|
+
- - ">="
|
170
|
+
- !ruby/object:Gem::Version
|
171
|
+
version: 1.9.3
|
172
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
173
|
+
requirements:
|
174
|
+
- - ">="
|
175
|
+
- !ruby/object:Gem::Version
|
176
|
+
version: '0'
|
177
|
+
requirements: []
|
178
|
+
rubyforge_project:
|
179
|
+
rubygems_version: 2.2.2
|
180
|
+
signing_key:
|
181
|
+
specification_version: 4
|
182
|
+
summary: Define and send messages to a variety of message brokers
|
183
|
+
test_files:
|
184
|
+
- spec/bin/kafka-run-class.sh
|
185
|
+
- spec/kafka_spec_helper.rb
|
186
|
+
- spec/producing/configuration_spec.rb
|
187
|
+
- spec/producing/message/message_spec.rb
|
188
|
+
- spec/producing/producing_spec.rb
|
189
|
+
- spec/producing/publisher_spec.rb
|
190
|
+
- spec/spec_helper.rb
|
191
|
+
- spec/test_cluster.rb
|