rdkafka 0.1.11 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8d6da9327e4ab83441c784ccb89783e0928821e6
4
- data.tar.gz: b6812cb4eee125203fd73efa1904b63bb0670d31
3
+ metadata.gz: 33e13eee28a6f2356635744a0490e06d236afd9d
4
+ data.tar.gz: 23a74b8693241447df18042c3cf0482a03ec9c82
5
5
  SHA512:
6
- metadata.gz: b155e6b7d3358ba451ba88ce6bde6cf327ff7db26275b76b1c24d6010b0f015eef7df26acf5362230872163ea62444919164e3f1e79f04d8c3093932b53e6e60
7
- data.tar.gz: 01d58aed27bc1f385c2d3bcb22e964ceff47c0582c3bccc3e6af6f88d2c4806ea3c3d0035d0b52d45522a3924722a506d97e1986b4f7babe558619cbfc9ed3ba
6
+ metadata.gz: 2c130f0bfa4a968186cf51490e7453e81ac879c2ff3fa082707332235a6d98fc94e25c6278bac249f05a62afbea2cc70534bd05fd24ee07e1754418cf909d156
7
+ data.tar.gz: 5c5d186816373f5cd28a5d7cc583a79cfded06522d4707da855373e06ce57ff90903c639a1232e535abfa692f27a395e699d4f6b47792a97b9777ada224c8e44
data/.gitignore CHANGED
@@ -4,3 +4,4 @@ ext/tmp
4
4
  ext/librdkafka.*
5
5
  *.gem
6
6
  .yardoc
7
+ doc
data/.travis.yml ADDED
@@ -0,0 +1,22 @@
1
+ language: ruby
2
+
3
+ sudo: false
4
+
5
+ rvm:
6
+ - 2.1
7
+ - 2.2
8
+ - 2.3
9
+ - 2.4
10
+
11
+ before_install:
12
+ - wget http://www.us.apache.org/dist/kafka/0.11.0.1/kafka_2.12-0.11.0.1.tgz -O kafka.tgz
13
+ - mkdir -p kafka && tar xzf kafka.tgz -C kafka --strip-components 1
14
+ - nohup bash -c "cd kafka && bin/zookeeper-server-start.sh config/zookeeper.properties &"
15
+ - nohup bash -c "cd kafka && bin/kafka-server-start.sh config/server.properties &"
16
+
17
+ before_script:
18
+ - cd ext && bundle exec rake && cd ..
19
+ - bundle exec rake create_topics
20
+
21
+ script:
22
+ - bundle exec rspec
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --no-private
2
+ --markup=markdown
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # 0.2.0
2
+ * Some refactoring and add inline documentation
3
+
4
+ # 0.1.x
5
+ * Initial working version including producing and consuming
data/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # Rdkafka
2
2
 
3
+ [![Build Status](https://travis-ci.org/thijsc/rdkafka-ruby.svg?branch=master)](https://travis-ci.org/thijsc/rdkafka-ruby)
3
4
  [![Gem Version](https://badge.fury.io/rb/rdkafka.svg)](https://badge.fury.io/rb/rdkafka)
4
5
 
5
6
  The `rdkafka` gem is a modern Kafka client library for Ruby based on
@@ -11,7 +12,45 @@ This gem only provides a high-level Kafka consumer. If you are running
11
12
  an older version of Kafka and/or need the legacy simple consumer we
12
13
  suggest using the [Hermann](https://github.com/reiseburo/hermann) gem.
13
14
 
14
- Documentation is available on [RubyDoc](http://www.rubydoc.info/github/thijsc/rdkafka-ruby/master).
15
+ ## Installation
16
+
17
+ This gem downloads and compiles librdkafka when it is installed. If you
18
+ have any problems installing the gem please open an issue.
19
+
20
+ ## Usage
21
+
22
+ See the [documentation](http://www.rubydoc.info/github/thijsc/rdkafka-ruby/master) for full details on how to use this gem. Two quick examples:
23
+
24
+ ### Consuming messages
25
+
26
+ ```ruby
27
+ config = {
28
+ :"bootstrap.servers" => "localhost:9092",
29
+ :"group.id" => "ruby-test"
30
+ }
31
+ consumer = Rdkafka::Config.new(config).consumer
32
+ consumer.subscribe("ruby-test-topic")
33
+
34
+ consumer.each do |message|
35
+ puts "Message received: #{message}"
36
+ end
37
+ ```
38
+
39
+ ### Producing messages
40
+
41
+ ```ruby
42
+ config = {:"bootstrap.servers" => "localhost:9092"}
43
+ producer = Rdkafka::Config.new(config).producer
44
+
45
+ 100.times do |i|
46
+ puts "Producing message #{i}"
47
+ producer.produce(
48
+ topic: "ruby-test-topic",
49
+ payload: "Payload #{i}",
50
+ key: "Key #{i}"
51
+ ).wait
52
+ end
53
+ ```
15
54
 
16
55
  ## Development
17
56
 
data/Rakefile CHANGED
@@ -2,8 +2,13 @@ require "./lib/rdkafka"
2
2
 
3
3
  task :create_topics do
4
4
  puts "Creating test topics"
5
- `kafka-topics --create --topic=produce_test_topic --zookeeper=127.0.0.1:2181 --partitions=3 --replication-factor=1`
6
- `kafka-topics --create --topic=rake_test_topic --zookeeper=127.0.0.1:2181 --partitions=3 --replication-factor=1`
5
+ kafka_topics = if ENV['TRAVIS']
6
+ 'kafka/bin/kafka-topics.sh'
7
+ else
8
+ 'kafka-topics'
9
+ end
10
+ `#{kafka_topics} --create --topic=produce_test_topic --zookeeper=127.0.0.1:2181 --partitions=3 --replication-factor=1`
11
+ `#{kafka_topics} --create --topic=rake_test_topic --zookeeper=127.0.0.1:2181 --partitions=3 --replication-factor=1`
7
12
  end
8
13
 
9
14
  task :produce_messages do
data/lib/rdkafka.rb CHANGED
@@ -2,7 +2,9 @@ require "rdkafka/version"
2
2
 
3
3
  require "rdkafka/config"
4
4
  require "rdkafka/consumer"
5
+ require "rdkafka/consumer/message"
5
6
  require "rdkafka/error"
6
7
  require "rdkafka/ffi"
7
- require "rdkafka/message"
8
8
  require "rdkafka/producer"
9
+ require "rdkafka/producer/delivery_handle"
10
+ require "rdkafka/producer/delivery_report"
@@ -5,38 +5,72 @@ module Rdkafka
5
5
  # the consumer and producer methods to create a client. Documentation of the available
6
6
  # configuration options is available on https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md.
7
7
  class Config
8
+ # @private
8
9
  @@logger = Logger.new(STDOUT)
9
10
 
11
+ # Returns the current logger, by default this is a logger to stdout.
12
+ #
13
+ # @return [Logger]
10
14
  def self.logger
11
15
  @@logger
12
16
  end
13
17
 
18
+ # Set the logger that will be used for all logging output by this library.
19
+ #
20
+ # @param logger [Logger] The logger to be used
21
+ #
22
+ # @return [nil]
14
23
  def self.logger=(logger)
24
+ raise NoLoggerError if logger.nil?
15
25
  @@logger=logger
16
26
  end
17
27
 
28
+ # Default config that can be overwritten.
18
29
  DEFAULT_CONFIG = {
19
30
  # Request api version so advanced features work
20
31
  :"api.version.request" => true
21
32
  }.freeze
22
33
 
34
+ # Required config that cannot be overwritten.
23
35
  REQUIRED_CONFIG = {
24
- # Enable log queues so we get callbacks in our own threads
36
+ # Enable log queues so we get callbacks in our own Ruby threads
25
37
  :"log.queue" => true
26
38
  }.freeze
27
39
 
40
+ # Returns a new config with the provided options which are merged with {DEFAULT_CONFIG}.
41
+ #
42
+ # @param config_hash [Hash<String,Symbol => String>] The config options for rdkafka
43
+ #
44
+ # @return [Config]
28
45
  def initialize(config_hash = {})
29
46
  @config_hash = DEFAULT_CONFIG.merge(config_hash)
30
47
  end
31
48
 
49
+ # Set a config option.
50
+ #
51
+ # @param key [String] The config option's key
52
+ # @param value [String] The config option's value
53
+ #
54
+ # @return [nil]
32
55
  def []=(key, value)
33
56
  @config_hash[key] = value
34
57
  end
35
58
 
59
+ # Get a config option with the specified key
60
+ #
61
+ # @param key [String] The config option's key
62
+ #
63
+ # @return [String, nil] The config option or `nil` if it is not present
36
64
  def [](key)
37
65
  @config_hash[key]
38
66
  end
39
67
 
68
+ # Create a consumer with this configuration.
69
+ #
70
+ # @raise [ConfigError] When the configuration contains invalid options
71
+ # @raise [ClientCreationError] When the native client cannot be created
72
+ #
73
+ # @return [Consumer] The created consumer
40
74
  def consumer
41
75
  kafka = native_kafka(native_config, :rd_kafka_consumer)
42
76
  # Redirect the main queue to the consumer
@@ -45,6 +79,12 @@ module Rdkafka
45
79
  Rdkafka::Consumer.new(kafka)
46
80
  end
47
81
 
82
+ # Create a producer with this configuration.
83
+ #
84
+ # @raise [ConfigError] When the configuration contains invalid options
85
+ # @raise [ClientCreationError] When the native client cannot be created
86
+ #
87
+ # @return [Producer] The created producer
48
88
  def producer
49
89
  # Create Kafka config
50
90
  config = native_config
@@ -54,9 +94,15 @@ module Rdkafka
54
94
  Rdkafka::Producer.new(native_kafka(config, :rd_kafka_producer))
55
95
  end
56
96
 
97
+ # Error that is returned by the underlying rdkafka error if an invalid configuration option is present.
57
98
  class ConfigError < RuntimeError; end
99
+
100
+ # Error that is returned by the underlying rdkafka library if the client cannot be created.
58
101
  class ClientCreationError < RuntimeError; end
59
102
 
103
+ # Error that is raised when trying to set a nil logger
104
+ class NoLoggerError < RuntimeError; end
105
+
60
106
  private
61
107
 
62
108
  # This method is only intented to be used to create a client,
@@ -1,15 +1,31 @@
1
1
  module Rdkafka
2
+ # A consumer of Kafka messages. It uses the high-level consumer approach where the Kafka
3
+ # brokers automatically assign partitions and load balance partitions over consumers that
4
+ # have the same `:"group.id"` set in their configuration.
5
+ #
6
+ # To create a consumer set up a {Config} and call {Config#consumer consumer} on that. It is
7
+ # mandatory to set `:"group.id"` in the configuration.
2
8
  class Consumer
3
9
  include Enumerable
4
10
 
11
+ # @private
5
12
  def initialize(native_kafka)
6
13
  @native_kafka = native_kafka
7
14
  end
8
15
 
16
+ # Close this consumer
17
+ # @return [nil]
9
18
  def close
10
19
  Rdkafka::FFI.rd_kafka_consumer_close(@native_kafka)
11
20
  end
12
21
 
22
+ # Subscribe to one or more topics
23
+ #
24
+ # @param topics [Array<String>] One or more topic names
25
+ #
26
+ # @raise [RdkafkaError] When subscribing fails
27
+ #
28
+ # @return [nil]
13
29
  def subscribe(*topics)
14
30
  # Create topic partition list with topics and no partition set
15
31
  tpl = Rdkafka::FFI.rd_kafka_topic_partition_list_new(topics.length)
@@ -30,6 +46,13 @@ module Rdkafka
30
46
  Rdkafka::FFI.rd_kafka_topic_partition_list_destroy(tpl)
31
47
  end
32
48
 
49
+ # Commit the current offsets of this consumer
50
+ #
51
+ # @param async [Boolean] Whether to commit async or wait for the commit to finish
52
+ #
53
+ # @raise [RdkafkaError] When comitting fails
54
+ #
55
+ # @return [nil]
33
56
  def commit(async=false)
34
57
  response = Rdkafka::FFI.rd_kafka_commit(@native_kafka, nil, async)
35
58
  if response != 0
@@ -37,6 +60,13 @@ module Rdkafka
37
60
  end
38
61
  end
39
62
 
63
+ # Poll for the next message on one of the subscribed topics
64
+ #
65
+ # @param timeout_ms [Integer] Timeout of this poll
66
+ #
67
+ # @raise [RdkafkaError] When polling fails
68
+ #
69
+ # @return [Message, nil] A message or nil if there was no new message within the timeout
40
70
  def poll(timeout_ms)
41
71
  message_ptr = Rdkafka::FFI.rd_kafka_consumer_poll(@native_kafka, timeout_ms)
42
72
  if message_ptr.null?
@@ -49,7 +79,7 @@ module Rdkafka
49
79
  raise Rdkafka::RdkafkaError.new(native_message[:err])
50
80
  end
51
81
  # Create a message to pass out
52
- Rdkafka::Message.new(native_message)
82
+ Rdkafka::Consumer::Message.new(native_message)
53
83
  end
54
84
  ensure
55
85
  # Clean up rdkafka message if there is one
@@ -58,6 +88,13 @@ module Rdkafka
58
88
  end
59
89
  end
60
90
 
91
+ # Poll for new messages and yield for each received one
92
+ #
93
+ # @raise [RdkafkaError] When polling fails
94
+ #
95
+ # @yieldparam message [Message] Received message
96
+ #
97
+ # @return [nil]
61
98
  def each(&block)
62
99
  loop do
63
100
  message = poll(250)
@@ -0,0 +1,51 @@
1
+ module Rdkafka
2
+ class Consumer
3
+ # A message that was consumed from a topic.
4
+ class Message
5
+ # The topic this message was consumed from
6
+ # @return [String]
7
+ attr_reader :topic
8
+
9
+ # The partition this message was consumed from
10
+ # @return [Integer]
11
+ attr_reader :partition
12
+
13
+ # This message's payload
14
+ # @return [String, nil]
15
+ attr_reader :payload
16
+
17
+ # This message's key
18
+ # @return [String, nil]
19
+ attr_reader :key
20
+
21
+ # This message's offset in it's partition
22
+ # @return [Integer]
23
+ attr_reader :offset
24
+
25
+ # This message's timestamp, if provided by the broker
26
+ # @return [Integer, nil]
27
+ attr_reader :timestamp
28
+
29
+ # @private
30
+ def initialize(native_message)
31
+ unless native_message[:rkt].null?
32
+ @topic = FFI.rd_kafka_topic_name(native_message[:rkt])
33
+ end
34
+ @partition = native_message[:partition]
35
+ unless native_message[:payload].null?
36
+ @payload = native_message[:payload].read_string(native_message[:len])
37
+ end
38
+ unless native_message[:key].null?
39
+ @key = native_message[:key].read_string(native_message[:key_len])
40
+ end
41
+ @offset = native_message[:offset]
42
+ @timestamp = FFI.rd_kafka_message_timestamp(native_message, nil)
43
+ end
44
+
45
+ # @return [String]
46
+ def to_s
47
+ "Message in '#{topic}' with key '#{key}', payload '#{payload}', partition #{partition}, offset #{offset}, timestamp #{timestamp}"
48
+ end
49
+ end
50
+ end
51
+ end
data/lib/rdkafka/error.rb CHANGED
@@ -1,12 +1,18 @@
1
1
  module Rdkafka
2
+ # Error returned by the underlying rdkafka library.
2
3
  class RdkafkaError < RuntimeError
4
+ # The underlying raw error response
5
+ # @return [Integer]
3
6
  attr_reader :rdkafka_response
4
7
 
8
+ # @private
5
9
  def initialize(response)
6
10
  raise TypeError.new("Response has to be an integer") unless response.is_a? Integer
7
11
  @rdkafka_response = response
8
12
  end
9
13
 
14
+ # This error's code, for example `:partition_eof`, `:msg_size_too_large`.
15
+ # @return [Symbol]
10
16
  def code
11
17
  code = Rdkafka::FFI.rd_kafka_err2name(@rdkafka_response).downcase
12
18
  if code[0] == "_"
@@ -16,10 +22,14 @@ module Rdkafka
16
22
  end
17
23
  end
18
24
 
25
+ # Human readable representation of this error.
26
+ # @return [String]
19
27
  def to_s
20
28
  "#{Rdkafka::FFI.rd_kafka_err2str(@rdkafka_response)} (#{code})"
21
29
  end
22
30
 
31
+ # Whether this error indicates the partition is EOF.
32
+ # @return [Boolean]
23
33
  def is_partition_eof?
24
34
  code == :partition_eof
25
35
  end
data/lib/rdkafka/ffi.rb CHANGED
@@ -2,6 +2,7 @@ require "ffi"
2
2
  require "logger"
3
3
 
4
4
  module Rdkafka
5
+ # @private
5
6
  module FFI
6
7
  extend ::FFI::Library
7
8
 
@@ -144,7 +145,7 @@ module Rdkafka
144
145
  :void, [:pointer, :pointer, :pointer]
145
146
  ) do |client_ptr, message_ptr, opaque_ptr|
146
147
  message = Message.new(message_ptr)
147
- delivery_handle = Rdkafka::DeliveryHandle.new(message[:_private])
148
+ delivery_handle = Rdkafka::Producer::DeliveryHandle.new(message[:_private])
148
149
  delivery_handle[:pending] = false
149
150
  delivery_handle[:response] = message[:err]
150
151
  delivery_handle[:partition] = message[:partition]
@@ -1,5 +1,7 @@
1
1
  module Rdkafka
2
+ # A producer for Kafka messages. To create a producer set up a {Config} and call {Config#producer producer} on that.
2
3
  class Producer
4
+ # @private
3
5
  def initialize(native_kafka)
4
6
  @closing = false
5
7
  @native_kafka = native_kafka
@@ -16,6 +18,7 @@ module Rdkafka
16
18
  @polling_thread.abort_on_exception = true
17
19
  end
18
20
 
21
+ # Close this producer and wait for the internal poll queue to empty.
19
22
  def close
20
23
  # Indicate to polling thread that we're closing
21
24
  @closing = true
@@ -23,6 +26,20 @@ module Rdkafka
23
26
  @polling_thread.join
24
27
  end
25
28
 
29
+ # Produces a message to a Kafka topic. The message is added to rdkafka's queue, call {DeliveryHandle#wait wait} on the returned delivery handle to make sure it is delivered.
30
+ #
31
+ # When no partition is specified the underlying Kafka library picks a partition based on the key. If no key is specified, a random partition will be used.
32
+ # When a timestamp is provided this is used instead of the autogenerated timestamp.
33
+ #
34
+ # @param topic [String] The topic to produce to
35
+ # @param payload [String] The message's payload
36
+ # @param key [String] The message's key
37
+ # @param partition [Integer] Optional partition to produce to
38
+ # @param timestamp [Integer] Optional timestamp of this message
39
+ #
40
+ # @raise [RdkafkaError] When adding the message to rdkafka's queue failed
41
+ #
42
+ # @return [DeliveryHandle] Delivery handle that can be used to wait for the result of producing this message
26
43
  def produce(topic:, payload: nil, key: nil, partition: nil, timestamp: nil)
27
44
  # Start by checking and converting the input
28
45
 
@@ -74,48 +91,4 @@ module Rdkafka
74
91
  delivery_handle
75
92
  end
76
93
  end
77
-
78
- class WaitTimeoutError < RuntimeError; end
79
-
80
- class DeliveryHandle < ::FFI::Struct
81
- layout :pending, :bool,
82
- :response, :int,
83
- :partition, :int,
84
- :offset, :int64
85
-
86
- def pending?
87
- self[:pending]
88
- end
89
-
90
- # Wait for the delivery report
91
- def wait(timeout_in_seconds=60)
92
- timeout = if timeout_in_seconds
93
- Time.now.to_i + timeout_in_seconds
94
- else
95
- nil
96
- end
97
- loop do
98
- if pending?
99
- if timeout && timeout <= Time.now.to_i
100
- raise WaitTimeoutError.new("Waiting for delivery timed out after #{timeout_in_seconds} seconds")
101
- end
102
- sleep 0.1
103
- next
104
- elsif self[:response] != 0
105
- raise RdkafkaError.new(self[:response])
106
- else
107
- return DeliveryReport.new(self[:partition], self[:offset])
108
- end
109
- end
110
- end
111
- end
112
-
113
- class DeliveryReport
114
- attr_reader :partition, :offset
115
-
116
- def initialize(partition, offset)
117
- @partition = partition
118
- @offset = offset
119
- end
120
- end
121
94
  end
@@ -0,0 +1,54 @@
1
+ module Rdkafka
2
+ class Producer
3
+ # Handle to wait for a delivery report which is returned when
4
+ # producing a message.
5
+ class DeliveryHandle < ::FFI::Struct
6
+ layout :pending, :bool,
7
+ :response, :int,
8
+ :partition, :int,
9
+ :offset, :int64
10
+
11
+ # Whether the delivery handle is still pending.
12
+ #
13
+ # @return [Boolean]
14
+ def pending?
15
+ self[:pending]
16
+ end
17
+
18
+ # Wait for the delivery report or raise an error if this takes longer than the timeout.
19
+ # If there is a timeout this does not mean the message is not delivered, rdkafka might still be working on delivering the message.
20
+ # In this case it is possible to call wait again.
21
+ #
22
+ # @param timeout_in_seconds [Integer, nil] Number of seconds to wait before timing out. If this is nil it does not time out.
23
+ #
24
+ # @raise [RdkafkaError] When delivering the message failed
25
+ # @raise [WaitTimeoutError] When the timeout has been reached and the handle is still pending
26
+ #
27
+ # @return [DeliveryReport]
28
+ def wait(timeout_in_seconds=60)
29
+ timeout = if timeout_in_seconds
30
+ Time.now.to_i + timeout_in_seconds
31
+ else
32
+ nil
33
+ end
34
+ loop do
35
+ if pending?
36
+ if timeout && timeout <= Time.now.to_i
37
+ raise WaitTimeoutError.new("Waiting for delivery timed out after #{timeout_in_seconds} seconds")
38
+ end
39
+ sleep 0.1
40
+ next
41
+ elsif self[:response] != 0
42
+ raise RdkafkaError.new(self[:response])
43
+ else
44
+ return DeliveryReport.new(self[:partition], self[:offset])
45
+ end
46
+ end
47
+ end
48
+
49
+ # Error that is raised when waiting for a delivery handle to complete
50
+ # takes longer than the specified timeout.
51
+ class WaitTimeoutError < RuntimeError; end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,21 @@
1
+ module Rdkafka
2
+ class Producer
3
+ # Delivery report for a succesfully produced message.
4
+ class DeliveryReport
5
+ # The partition this message was produced to.
6
+ # @return [Integer]
7
+ attr_reader :partition
8
+
9
+ # The offset of the produced message.
10
+ # @return [Integer]
11
+ attr_reader :offset
12
+
13
+ private
14
+
15
+ def initialize(partition, offset)
16
+ @partition = partition
17
+ @offset = offset
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,4 +1,4 @@
1
1
  module Rdkafka
2
- VERSION = "0.1.11"
2
+ VERSION = "0.2.0"
3
3
  LIBRDKAFKA_VERSION = "0.11.0"
4
4
  end
@@ -1,36 +1,57 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Rdkafka::Config do
4
- it "should store configuration" do
5
- config = Rdkafka::Config.new
6
- config[:"key"] = 'value'
7
- expect(config[:"key"]).to eq 'value'
8
- end
4
+ context "logger" do
5
+ it "should have a default logger" do
6
+ expect(Rdkafka::Config.logger).to be_a Logger
7
+ end
9
8
 
10
- it "should use default configuration" do
11
- config = Rdkafka::Config.new
12
- expect(config[:"api.version.request"]).to eq true
13
- end
9
+ it "should set the logger" do
10
+ logger = Logger.new(STDOUT)
11
+ expect(Rdkafka::Config.logger).not_to eq logger
12
+ Rdkafka::Config.logger = logger
13
+ expect(Rdkafka::Config.logger).to eq logger
14
+ end
14
15
 
15
- it "should create a consumer with valid config" do
16
- expect(rdkafka_config.consumer).to be_a Rdkafka::Consumer
16
+ it "should not accept a nil logger" do
17
+ expect {
18
+ Rdkafka::Config.logger = nil
19
+ }.to raise_error(Rdkafka::Config::NoLoggerError)
20
+ end
17
21
  end
18
22
 
19
- it "should raise an error when creating a consumer with invalid config" do
20
- config = Rdkafka::Config.new('invalid.key' => 'value')
21
- expect {
22
- config.consumer
23
- }.to raise_error(Rdkafka::Config::ConfigError, "No such configuration property: \"invalid.key\"")
24
- end
23
+ context "configuration" do
24
+ it "should store configuration" do
25
+ config = Rdkafka::Config.new
26
+ config[:"key"] = 'value'
27
+ expect(config[:"key"]).to eq 'value'
28
+ end
25
29
 
26
- it "should create a producer with valid config" do
27
- expect(rdkafka_config.producer).to be_a Rdkafka::Producer
28
- end
30
+ it "should use default configuration" do
31
+ config = Rdkafka::Config.new
32
+ expect(config[:"api.version.request"]).to eq true
33
+ end
34
+
35
+ it "should create a consumer with valid config" do
36
+ expect(rdkafka_config.consumer).to be_a Rdkafka::Consumer
37
+ end
38
+
39
+ it "should raise an error when creating a consumer with invalid config" do
40
+ config = Rdkafka::Config.new('invalid.key' => 'value')
41
+ expect {
42
+ config.consumer
43
+ }.to raise_error(Rdkafka::Config::ConfigError, "No such configuration property: \"invalid.key\"")
44
+ end
45
+
46
+ it "should create a producer with valid config" do
47
+ expect(rdkafka_config.producer).to be_a Rdkafka::Producer
48
+ end
29
49
 
30
- it "should raise an error when creating a producer with invalid config" do
31
- config = Rdkafka::Config.new('invalid.key' => 'value')
32
- expect {
33
- config.producer
34
- }.to raise_error(Rdkafka::Config::ConfigError, "No such configuration property: \"invalid.key\"")
50
+ it "should raise an error when creating a producer with invalid config" do
51
+ config = Rdkafka::Config.new('invalid.key' => 'value')
52
+ expect {
53
+ config.producer
54
+ }.to raise_error(Rdkafka::Config::ConfigError, "No such configuration property: \"invalid.key\"")
55
+ end
35
56
  end
36
57
  end
@@ -1,6 +1,6 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Rdkafka::Message do
3
+ describe Rdkafka::Consumer::Message do
4
4
  let(:native_topic) do
5
5
  Rdkafka::FFI.rd_kafka_topic_new(
6
6
  native_client,
@@ -29,7 +29,7 @@ describe Rdkafka::Message do
29
29
  end
30
30
  end
31
31
  end
32
- subject { Rdkafka::Message.new(native_message) }
32
+ subject { Rdkafka::Consumer::Message.new(native_message) }
33
33
 
34
34
  it "should have a topic" do
35
35
  expect(subject.topic).to eq "topic_name"
@@ -72,6 +72,10 @@ describe Rdkafka::Message do
72
72
  end
73
73
 
74
74
  it "should have a timestamp" do
75
- expect(subject.timestamp).to be > 0
75
+ # There is no effective way to mock this this, just
76
+ # make sure it doesn't crash.
77
+ expect {
78
+ subject.timestamp
79
+ }.not_to raise_error
76
80
  end
77
81
  end
@@ -0,0 +1,63 @@
1
+ require "spec_helper"
2
+
3
+ describe Rdkafka::Producer::DeliveryHandle do
4
+ let(:response) { 0 }
5
+ subject do
6
+ Rdkafka::Producer::DeliveryHandle.new.tap do |handle|
7
+ handle[:pending] = pending
8
+ handle[:response] = response
9
+ handle[:partition] = 2
10
+ handle[:offset] = 100
11
+ end
12
+ end
13
+
14
+ describe "#pending?" do
15
+ context "when true" do
16
+ let(:pending) { true }
17
+
18
+ it "should be true" do
19
+ expect(subject.pending?).to be true
20
+ end
21
+ end
22
+
23
+ context "when not true" do
24
+ let(:pending) { false }
25
+
26
+ it "should be false" do
27
+ expect(subject.pending?).to be false
28
+ end
29
+ end
30
+ end
31
+
32
+ describe "#wait" do
33
+ let(:pending) { true }
34
+
35
+ it "should wait until the timeout and then raise an error" do
36
+ expect {
37
+ subject.wait(0.1)
38
+ }.to raise_error Rdkafka::Producer::DeliveryHandle::WaitTimeoutError
39
+ end
40
+
41
+ context "when not pending anymore and no error" do
42
+ let(:pending) { false }
43
+
44
+ it "should return a delivery report" do
45
+ report = subject.wait
46
+
47
+ expect(report.partition).to eq(2)
48
+ expect(report.offset).to eq(100)
49
+ end
50
+ end
51
+
52
+ context "when not pending anymore and there was an error" do
53
+ let(:pending) { false }
54
+ let(:response) { 20 }
55
+
56
+ it "should raise an rdkafka error" do
57
+ expect {
58
+ subject.wait
59
+ }.to raise_error Rdkafka::RdkafkaError
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ describe Rdkafka::Producer::DeliveryReport do
4
+ subject { Rdkafka::Producer::DeliveryReport.new(2, 100) }
5
+
6
+ it "should get the partition" do
7
+ expect(subject.partition).to eq 2
8
+ end
9
+
10
+ it "should get the offset" do
11
+ expect(subject.offset).to eq 100
12
+ end
13
+ end
@@ -63,6 +63,7 @@ describe Rdkafka::Producer do
63
63
  delivery_report: report
64
64
  )
65
65
 
66
+ expect(message.partition).to eq 1
66
67
  expect(message.payload.force_encoding("utf-8")).to eq "Τη γλώσσα μου έδωσαν ελληνική"
67
68
  expect(message.key).to eq "key utf8"
68
69
  end
@@ -70,7 +71,7 @@ describe Rdkafka::Producer do
70
71
  it "should produce a message with a timestamp" do
71
72
  handle = producer.produce(
72
73
  topic: "produce_test_topic",
73
- payload: "Payload 1",
74
+ payload: "Payload timestamp",
74
75
  key: "key timestamp",
75
76
  timestamp: 1505069646000
76
77
  )
@@ -88,6 +89,7 @@ describe Rdkafka::Producer do
88
89
  delivery_report: report
89
90
  )
90
91
 
92
+ expect(message.partition).to eq 2
91
93
  expect(message.key).to eq "key timestamp"
92
94
  expect(message.timestamp).to eq 1505069646000
93
95
  end
@@ -100,6 +102,6 @@ describe Rdkafka::Producer do
100
102
  )
101
103
  expect {
102
104
  handle.wait(0)
103
- }.to raise_error Rdkafka::WaitTimeoutError
105
+ }.to raise_error Rdkafka::Producer::DeliveryHandle::WaitTimeoutError
104
106
  end
105
107
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rdkafka
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.11
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thijs Cadier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-10 00:00:00.000000000 Z
11
+ date: 2017-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -89,6 +89,9 @@ extensions:
89
89
  extra_rdoc_files: []
90
90
  files:
91
91
  - ".gitignore"
92
+ - ".travis.yml"
93
+ - ".yardopts"
94
+ - CHANGELOG.md
92
95
  - Gemfile
93
96
  - LICENSE
94
97
  - README.md
@@ -97,17 +100,21 @@ files:
97
100
  - lib/rdkafka.rb
98
101
  - lib/rdkafka/config.rb
99
102
  - lib/rdkafka/consumer.rb
103
+ - lib/rdkafka/consumer/message.rb
100
104
  - lib/rdkafka/error.rb
101
105
  - lib/rdkafka/ffi.rb
102
- - lib/rdkafka/message.rb
103
106
  - lib/rdkafka/producer.rb
107
+ - lib/rdkafka/producer/delivery_handle.rb
108
+ - lib/rdkafka/producer/delivery_report.rb
104
109
  - lib/rdkafka/version.rb
105
110
  - rdkafka.gemspec
106
111
  - spec/rdkafka/config_spec.rb
112
+ - spec/rdkafka/consumer/message_spec.rb
107
113
  - spec/rdkafka/consumer_spec.rb
108
114
  - spec/rdkafka/error_spec.rb
109
115
  - spec/rdkafka/ffi_spec.rb
110
- - spec/rdkafka/message_spec.rb
116
+ - spec/rdkafka/producer/delivery_handle_spec.rb
117
+ - spec/rdkafka/producer/delivery_report_spec.rb
111
118
  - spec/rdkafka/producer_spec.rb
112
119
  - spec/spec_helper.rb
113
120
  homepage: https://github.com/thijsc/rdkafka-ruby
@@ -137,9 +144,11 @@ summary: Kafka client library wrapping librdkafka using the ffi gem and futures
137
144
  concurrent-ruby for Kafka 0.10+
138
145
  test_files:
139
146
  - spec/rdkafka/config_spec.rb
147
+ - spec/rdkafka/consumer/message_spec.rb
140
148
  - spec/rdkafka/consumer_spec.rb
141
149
  - spec/rdkafka/error_spec.rb
142
150
  - spec/rdkafka/ffi_spec.rb
143
- - spec/rdkafka/message_spec.rb
151
+ - spec/rdkafka/producer/delivery_handle_spec.rb
152
+ - spec/rdkafka/producer/delivery_report_spec.rb
144
153
  - spec/rdkafka/producer_spec.rb
145
154
  - spec/spec_helper.rb
@@ -1,24 +0,0 @@
1
- module Rdkafka
2
- class Message
3
- attr_reader :topic, :partition, :payload, :key, :offset, :timestamp
4
-
5
- def initialize(native_message)
6
- unless native_message[:rkt].null?
7
- @topic = FFI.rd_kafka_topic_name(native_message[:rkt])
8
- end
9
- @partition = native_message[:partition]
10
- unless native_message[:payload].null?
11
- @payload = native_message[:payload].read_string(native_message[:len])
12
- end
13
- unless native_message[:key].null?
14
- @key = native_message[:key].read_string(native_message[:key_len])
15
- end
16
- @offset = native_message[:offset]
17
- @timestamp = FFI.rd_kafka_message_timestamp(native_message, nil)
18
- end
19
-
20
- def to_s
21
- "Message in '#{topic}' with key '#{key}', payload '#{payload}', partition '#{partition}', offset '#{offset}'"
22
- end
23
- end
24
- end