kafka-rb 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +52 -0
- data/Rakefile +61 -0
- data/lib/kafka/io.rb +30 -0
- data/lib/kafka/message.rb +25 -0
- data/lib/kafka/producer.rb +49 -0
- data/lib/kafka.rb +9 -0
- data/lib/test.rb +32 -0
- data/spec/io_spec.rb +64 -0
- data/spec/kafka_spec.rb +7 -0
- data/spec/message_spec.rb +44 -0
- data/spec/producer_spec.rb +95 -0
- data/spec/spec_helper.rb +4 -0
- metadata +92 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Alejandro Crosa
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# kafka-rb
|
2
|
+
kafka-rb allows you to produce messages to the Kafka distributed publish/subscribe messaging service.
|
3
|
+
|
4
|
+
## Requirements
|
5
|
+
You need to have access to your Kafka instance and be able to connect through TCP. You can obtain a copy and instructions on how to setup kafka at https://github.com/kafka-dev/kafka
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
sudo gem install kafka-rb
|
9
|
+
|
10
|
+
(the code works fine with JRuby, Ruby 1.8x and Ruby 1.9.x)
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
### Sending a simple message
|
15
|
+
|
16
|
+
require 'kafka-rb'
|
17
|
+
|
18
|
+
producer = Kafka::Producer.new
|
19
|
+
|
20
|
+
message = Kafka::Message.new("some random message content")
|
21
|
+
|
22
|
+
producer.send(message)
|
23
|
+
|
24
|
+
### sending a sequence of messages
|
25
|
+
|
26
|
+
require 'kafka-rb'
|
27
|
+
|
28
|
+
producer = Kafka::Producer.new
|
29
|
+
|
30
|
+
message1 = Kafka::Message.new("some random message content")
|
31
|
+
|
32
|
+
message2 = Kafka::Message.new("some more content")
|
33
|
+
|
34
|
+
producer.send([message1, message2])
|
35
|
+
|
36
|
+
### batching a bunch of messages using the block syntax
|
37
|
+
|
38
|
+
require 'kafka-rb'
|
39
|
+
|
40
|
+
producer = Kafka::Producer.new
|
41
|
+
|
42
|
+
producer.batch do |messages|
|
43
|
+
|
44
|
+
puts "Batching a send of multiple messages.."
|
45
|
+
|
46
|
+
messages << Kafka::Message.new("first message to send")
|
47
|
+
|
48
|
+
messages << Kafka::Message.new("second message to send")
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
* they will be sent all at once, after the block execution
|
data/Rakefile
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rubygems/specification'
|
4
|
+
require 'date'
|
5
|
+
require 'spec/rake/spectask'
|
6
|
+
|
7
|
+
GEM = 'kafka-rb'
|
8
|
+
GEM_NAME = 'Kafka Client Producer'
|
9
|
+
GEM_VERSION = '0.0.1'
|
10
|
+
AUTHORS = ['Alejandro Crosa']
|
11
|
+
EMAIL = "alejandrocrosa@gmail.com"
|
12
|
+
HOMEPAGE = "http://github.com/acrosa/kafka-rb"
|
13
|
+
SUMMARY = "A Ruby client for the Kafka distributed publish/subscribe messaging service"
|
14
|
+
DESCRIPTION = "kafka-rb allows you to produce messages to the Kafka distributed publish/subscribe messaging service."
|
15
|
+
|
16
|
+
spec = Gem::Specification.new do |s|
|
17
|
+
s.name = GEM
|
18
|
+
s.version = GEM_VERSION
|
19
|
+
s.platform = Gem::Platform::RUBY
|
20
|
+
s.has_rdoc = true
|
21
|
+
s.extra_rdoc_files = ["LICENSE"]
|
22
|
+
s.summary = SUMMARY
|
23
|
+
s.description = DESCRIPTION
|
24
|
+
s.authors = AUTHORS
|
25
|
+
s.email = EMAIL
|
26
|
+
s.homepage = HOMEPAGE
|
27
|
+
s.add_development_dependency "rspec"
|
28
|
+
s.require_path = 'lib'
|
29
|
+
s.autorequire = GEM
|
30
|
+
s.files = %w(LICENSE README.md Rakefile) + Dir.glob("{lib,tasks,spec}/**/*")
|
31
|
+
end
|
32
|
+
|
33
|
+
task :default => :spec
|
34
|
+
|
35
|
+
desc "Run specs"
|
36
|
+
Spec::Rake::SpecTask.new do |t|
|
37
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
38
|
+
t.spec_opts = %w(-fs --color)
|
39
|
+
end
|
40
|
+
|
41
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
42
|
+
pkg.gem_spec = spec
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "install the gem locally"
|
46
|
+
task :install => [:package] do
|
47
|
+
sh %{sudo gem install pkg/#{GEM_NAME}-#{GEM_VERSION}}
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "create a gemspec file"
|
51
|
+
task :make_spec do
|
52
|
+
File.open("#{GEM}.gemspec", "w") do |file|
|
53
|
+
file.puts spec.to_ruby
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "Run all examples with RCov"
|
58
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
59
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
60
|
+
t.rcov = true
|
61
|
+
end
|
data/lib/kafka/io.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Kafka
|
2
|
+
module IO
|
3
|
+
attr_accessor :socket, :host, :port
|
4
|
+
|
5
|
+
def connect(host, port)
|
6
|
+
raise ArgumentError, "No host or port specified" unless host && port
|
7
|
+
self.host = host
|
8
|
+
self.port = port
|
9
|
+
self.socket = TCPSocket.new(host, port)
|
10
|
+
end
|
11
|
+
|
12
|
+
def reconnect
|
13
|
+
self.disconnect
|
14
|
+
self.socket = self.connect(self.host, self.port)
|
15
|
+
end
|
16
|
+
|
17
|
+
def disconnect
|
18
|
+
self.socket.close rescue nil
|
19
|
+
self.socket = nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def write(data)
|
23
|
+
self.reconnect unless self.socket
|
24
|
+
self.socket.write(data)
|
25
|
+
rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED
|
26
|
+
self.reconnect
|
27
|
+
self.socket.write(data) # retry
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Kafka
|
2
|
+
|
3
|
+
# A message. The format of an N byte message is the following:
|
4
|
+
# 1 byte "magic" identifier to allow format changes
|
5
|
+
# 4 byte CRC32 of the payload
|
6
|
+
# N - 5 byte payload
|
7
|
+
class Message
|
8
|
+
MAGIC_IDENTIFIER_DEFAULT = 0
|
9
|
+
attr_accessor :magic, :checksum, :payload
|
10
|
+
|
11
|
+
def initialize(payload = nil, magic = MAGIC_IDENTIFIER_DEFAULT)
|
12
|
+
self.magic = magic
|
13
|
+
self.payload = payload
|
14
|
+
self.checksum = self.calculate_checksum
|
15
|
+
end
|
16
|
+
|
17
|
+
def calculate_checksum
|
18
|
+
Zlib.crc32(self.payload)
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid?
|
22
|
+
self.checksum == Zlib.crc32(self.payload)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Kafka
|
2
|
+
class Producer
|
3
|
+
|
4
|
+
include Kafka::IO
|
5
|
+
|
6
|
+
PRODUCE_REQUEST_ID = 0
|
7
|
+
|
8
|
+
attr_accessor :topic, :partition
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
self.topic = options[:topic] || "test"
|
12
|
+
self.partition = options[:partition] || 0
|
13
|
+
self.host = options[:host] || "localhost"
|
14
|
+
self.port = options[:port] || 9092
|
15
|
+
self.connect(self.host, self.port)
|
16
|
+
end
|
17
|
+
|
18
|
+
def encode(message)
|
19
|
+
[message.magic].pack("C") + [message.calculate_checksum].pack("N") + message.payload.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
def encode_request(topic, partition, messages)
|
23
|
+
message_set = Array(messages).collect { |message|
|
24
|
+
encoded_message = self.encode(message)
|
25
|
+
[encoded_message.length].pack("N") + encoded_message
|
26
|
+
}.join("")
|
27
|
+
|
28
|
+
request = [PRODUCE_REQUEST_ID].pack("n")
|
29
|
+
topic = [topic.length].pack("n") + topic
|
30
|
+
partition = [partition].pack("N")
|
31
|
+
messages = [message_set.length].pack("N") + message_set
|
32
|
+
|
33
|
+
data = request + topic + partition + messages
|
34
|
+
|
35
|
+
return [data.length].pack("N") + data
|
36
|
+
end
|
37
|
+
|
38
|
+
def send(messages)
|
39
|
+
self.write(self.encode_request(self.topic, self.partition, messages))
|
40
|
+
end
|
41
|
+
|
42
|
+
def batch(&block)
|
43
|
+
batch = Kafka::Batch.new
|
44
|
+
block.call( batch )
|
45
|
+
self.send(batch.messages)
|
46
|
+
batch.messages.clear
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/kafka.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'zlib'
|
3
|
+
require File.join(File.dirname(__FILE__), "kafka", "io")
|
4
|
+
require File.join(File.dirname(__FILE__), "kafka", "batch")
|
5
|
+
require File.join(File.dirname(__FILE__), "kafka", "message")
|
6
|
+
require File.join(File.dirname(__FILE__), "kafka", "producer")
|
7
|
+
|
8
|
+
module Kafka
|
9
|
+
end
|
data/lib/test.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
$KCODE = 'UTF-8'
|
2
|
+
|
3
|
+
require 'zlib'
|
4
|
+
|
5
|
+
PRODUCE_REQUEST_ID = 0
|
6
|
+
|
7
|
+
def encode_message(message)
|
8
|
+
# <MAGIC_BYTE: char> <CRC32: int> <PAYLOAD: bytes>
|
9
|
+
data = [0].pack("C").to_s + [Zlib.crc32(message)].pack('N').to_s + message
|
10
|
+
# print ("CHECKSUM " + Zlib.crc32(message).to_s)
|
11
|
+
# print ("MES " + data.length.to_s)
|
12
|
+
return data
|
13
|
+
end
|
14
|
+
# encode_message("ale")
|
15
|
+
|
16
|
+
def encode_produce_request(topic, partition, messages)
|
17
|
+
encoded = messages.collect { |m| encode_message(m) }
|
18
|
+
message_set = encoded.collect { |e| puts "Message size #{e.length}"; [e.length].pack("N") + e }.join("")
|
19
|
+
|
20
|
+
puts "MESSAGE"
|
21
|
+
puts message_set.inspect
|
22
|
+
|
23
|
+
data = [PRODUCE_REQUEST_ID].pack("n") + \
|
24
|
+
[topic.length].pack("n") + topic.to_s + \
|
25
|
+
[partition].pack("N") + \
|
26
|
+
[message_set.length].pack("N") + message_set
|
27
|
+
puts "DATA " + message_set.length.to_s
|
28
|
+
return [data.length].pack("N") + data
|
29
|
+
end
|
30
|
+
|
31
|
+
socket = TCPSocket.open("localhost", 9092)
|
32
|
+
socket.write encode_produce_request("test", 0, ["ale"])
|
data/spec/io_spec.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class IOTest
|
4
|
+
include Kafka::IO
|
5
|
+
end
|
6
|
+
|
7
|
+
describe IO do
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
@mocked_socket = mock(TCPSocket)
|
11
|
+
TCPSocket.stub!(:new).and_return(@mocked_socket) # don't use a real socket
|
12
|
+
@io = IOTest.new
|
13
|
+
@io.connect("somehost", 9093)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "default methods" do
|
17
|
+
it "has a socket, a host and a port" do
|
18
|
+
[:socket, :host, :port].each do |m|
|
19
|
+
@io.should respond_to(m.to_sym)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "raises an exception if no host and port is specified" do
|
24
|
+
lambda {
|
25
|
+
io = IOTest.new
|
26
|
+
io.connect
|
27
|
+
}.should raise_error(ArgumentError)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should remember the port and host on connect" do
|
31
|
+
@io.connect("somehost", 9093)
|
32
|
+
@io.host.should eql("somehost")
|
33
|
+
@io.port.should eql(9093)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should write to a socket" do
|
37
|
+
data = "some data"
|
38
|
+
@mocked_socket.should_receive(:write).with(data).and_return(9)
|
39
|
+
@io.write(data).should eql(9)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should disconnect" do
|
43
|
+
@io.should respond_to(:disconnect)
|
44
|
+
@mocked_socket.should_receive(:close).and_return(nil)
|
45
|
+
@io.disconnect
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should reconnect" do
|
49
|
+
@mocked_socket.should_receive(:close)
|
50
|
+
@io.should_receive(:connect)
|
51
|
+
@io.reconnect
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should reconnect on a broken pipe error" do
|
55
|
+
[Errno::ECONNABORTED, Errno::EPIPE, Errno::ECONNRESET].each do |error|
|
56
|
+
@mocked_socket.should_receive(:write).exactly(:twice).and_raise(error)
|
57
|
+
@mocked_socket.should_receive(:close).exactly(:once).and_return(nil)
|
58
|
+
lambda {
|
59
|
+
@io.write("some data to send")
|
60
|
+
}.should raise_error(error)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/spec/kafka_spec.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Message do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@message = Message.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "Kafka Message" do
|
10
|
+
it "should have a default magic number" do
|
11
|
+
Message::MAGIC_IDENTIFIER_DEFAULT.should eql(0)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have a magic field, a checksum and a payload" do
|
15
|
+
[:magic, :checksum, :payload].each do |field|
|
16
|
+
@message.should respond_to(field.to_sym)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should set a default value of zero" do
|
21
|
+
@message.magic.should eql(Kafka::Message::MAGIC_IDENTIFIER_DEFAULT)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should allow to set a custom magic number" do
|
25
|
+
@message = Message.new("ale", 1)
|
26
|
+
@message.magic.should eql(1)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should calculate the checksum (crc32 of a given message)" do
|
30
|
+
@message.payload = "ale"
|
31
|
+
@message.calculate_checksum.should eql(1120192889)
|
32
|
+
@message.payload = "alejandro"
|
33
|
+
@message.calculate_checksum.should eql(2865078607)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should say if the message is valid using the crc32 signature" do
|
37
|
+
@message.payload = "alejandro"
|
38
|
+
@message.checksum = 2865078607
|
39
|
+
@message.valid?.should eql(true)
|
40
|
+
@message.checksum = 0
|
41
|
+
@message.valid?.should eql(false)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe Producer do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@mocked_socket = mock(TCPSocket)
|
7
|
+
TCPSocket.stub!(:new).and_return(@mocked_socket) # don't use a real socket
|
8
|
+
@producer = Producer.new
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "Kafka Producer" do
|
12
|
+
it "should have a PRODUCE_REQUEST_ID" do
|
13
|
+
Producer::PRODUCE_REQUEST_ID.should eql(0)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should have a topic and a partition" do
|
17
|
+
@producer.should respond_to(:topic)
|
18
|
+
@producer.should respond_to(:partition)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should set a topic and partition on initialize" do
|
22
|
+
@producer = Producer.new({ :host => "localhost", :port => 9092, :topic => "testing" })
|
23
|
+
@producer.topic.should eql("testing")
|
24
|
+
@producer.partition.should eql(0)
|
25
|
+
@producer = Producer.new({ :topic => "testing", :partition => 3 })
|
26
|
+
@producer.partition.should eql(3)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should set default host and port if none is specified" do
|
30
|
+
@producer = Producer.new
|
31
|
+
@producer.host.should eql("localhost")
|
32
|
+
@producer.port.should eql(9092)
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "Message Encoding" do
|
36
|
+
it "should encode a message" do
|
37
|
+
message = Kafka::Message.new("alejandro")
|
38
|
+
full_message = [message.magic].pack("C") + [message.calculate_checksum].pack("N") + message.payload
|
39
|
+
@producer.encode(message).should eql(full_message)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should encode an empty message" do
|
43
|
+
message = Kafka::Message.new()
|
44
|
+
full_message = [message.magic].pack("C") + [message.calculate_checksum].pack("N") + message.payload.to_s
|
45
|
+
@producer.encode(message).should eql(full_message)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "Request Encoding" do
|
50
|
+
it "should binary encode an empty request" do
|
51
|
+
bytes = @producer.encode_request("test", 0, [])
|
52
|
+
bytes.length.should eql(20)
|
53
|
+
bytes.should eql("\000\000\000\020\000\000\000\004test\000\000\000\000\000\000\000\000")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should binary encode a request with a message, using a specific wire format" do
|
57
|
+
message = Kafka::Message.new("ale")
|
58
|
+
bytes = @producer.encode_request("test", 3, message)
|
59
|
+
data_size = bytes[0, 4].unpack("N").shift
|
60
|
+
request_id = bytes[4, 2].unpack("n").shift
|
61
|
+
topic_length = bytes[6, 2].unpack("n").shift
|
62
|
+
topic = bytes[8, 4]
|
63
|
+
partition = bytes[12, 4].unpack("N").shift
|
64
|
+
messages_length = bytes[16, 4].unpack("N").shift
|
65
|
+
messages = bytes[20, messages_length]
|
66
|
+
|
67
|
+
bytes.length.should eql(32)
|
68
|
+
data_size.should eql(28)
|
69
|
+
request_id.should eql(0)
|
70
|
+
topic_length.should eql(4)
|
71
|
+
topic.should eql("test")
|
72
|
+
partition.should eql(3)
|
73
|
+
messages_length.should eql(12)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should send messages" do
|
79
|
+
@producer.should_receive(:write).and_return(32)
|
80
|
+
message = Kafka::Message.new("ale")
|
81
|
+
@producer.send(message).should eql(32)
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "Message Batching" do
|
85
|
+
it "should batch messages and send them at once" do
|
86
|
+
message1 = Kafka::Message.new("one")
|
87
|
+
message2 = Kafka::Message.new("two")
|
88
|
+
@producer.should_receive(:send).with([message1, message2]).exactly(:once).and_return(nil)
|
89
|
+
@producer.batch do |messages|
|
90
|
+
messages << message1
|
91
|
+
messages << message2
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kafka-rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Alejandro Crosa
|
14
|
+
autorequire: kafka-rb
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-01-07 00:00:00 -08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rspec
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
description: kafka-rb allows you to produce messages to the Kafka distributed publish/subscribe messaging service.
|
36
|
+
email: alejandrocrosa@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- LICENSE
|
43
|
+
files:
|
44
|
+
- LICENSE
|
45
|
+
- README.md
|
46
|
+
- Rakefile
|
47
|
+
- lib/kafka/io.rb
|
48
|
+
- lib/kafka/message.rb
|
49
|
+
- lib/kafka/producer.rb
|
50
|
+
- lib/kafka.rb
|
51
|
+
- lib/test.rb
|
52
|
+
- spec/io_spec.rb
|
53
|
+
- spec/kafka_spec.rb
|
54
|
+
- spec/message_spec.rb
|
55
|
+
- spec/producer_spec.rb
|
56
|
+
- spec/spec_helper.rb
|
57
|
+
has_rdoc: true
|
58
|
+
homepage: http://github.com/acrosa/kafka-rb
|
59
|
+
licenses: []
|
60
|
+
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
none: false
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
hash: 3
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 1.3.7
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: A Ruby client for the Kafka distributed publish/subscribe messaging service
|
91
|
+
test_files: []
|
92
|
+
|