rom-kafka 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +9 -0
- data/.metrics +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +34 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +7 -0
- data/Guardfile +14 -0
- data/LICENSE +21 -0
- data/README.md +83 -0
- data/Rakefile +34 -0
- data/config/metrics/STYLEGUIDE +230 -0
- data/config/metrics/cane.yml +5 -0
- data/config/metrics/churn.yml +6 -0
- data/config/metrics/flay.yml +2 -0
- data/config/metrics/metric_fu.yml +14 -0
- data/config/metrics/reek.yml +1 -0
- data/config/metrics/roodi.yml +24 -0
- data/config/metrics/rubocop.yml +71 -0
- data/config/metrics/saikuro.yml +3 -0
- data/config/metrics/simplecov.yml +6 -0
- data/config/metrics/yardstick.yml +37 -0
- data/lib/rom-kafka.rb +3 -0
- data/lib/rom/kafka.rb +29 -0
- data/lib/rom/kafka/brokers.rb +72 -0
- data/lib/rom/kafka/brokers/broker.rb +68 -0
- data/lib/rom/kafka/connection.rb +22 -0
- data/lib/rom/kafka/connection/consumer.rb +105 -0
- data/lib/rom/kafka/connection/producer.rb +114 -0
- data/lib/rom/kafka/create.rb +75 -0
- data/lib/rom/kafka/dataset.rb +132 -0
- data/lib/rom/kafka/gateway.rb +165 -0
- data/lib/rom/kafka/relation.rb +78 -0
- data/lib/rom/kafka/version.rb +13 -0
- data/rom-kafka.gemspec +33 -0
- data/spec/integration/basic_usage_spec.rb +58 -0
- data/spec/integration/keys_usage_spec.rb +34 -0
- data/spec/shared/scholars_topic.rb +28 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/unit/brokers/broker_spec.rb +89 -0
- data/spec/unit/brokers_spec.rb +46 -0
- data/spec/unit/connection/consumer_spec.rb +90 -0
- data/spec/unit/connection/producer_spec.rb +79 -0
- data/spec/unit/create_spec.rb +79 -0
- data/spec/unit/dataset_spec.rb +165 -0
- data/spec/unit/gateway_spec.rb +171 -0
- data/spec/unit/relation_spec.rb +96 -0
- metadata +219 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
---
|
2
|
+
folders: # The list of folders to be used by any metric.
|
3
|
+
- lib
|
4
|
+
metrics: # The list of allowed metrics. The other metrics are disabled.
|
5
|
+
- cane
|
6
|
+
- churn
|
7
|
+
- flay
|
8
|
+
- flog
|
9
|
+
- reek
|
10
|
+
- roodi
|
11
|
+
- saikuro
|
12
|
+
format: html
|
13
|
+
output: tmp/metric_fu
|
14
|
+
verbose: false
|
@@ -0,0 +1 @@
|
|
1
|
+
---
|
@@ -0,0 +1,24 @@
|
|
1
|
+
---
|
2
|
+
AssignmentInConditionalCheck:
|
3
|
+
CaseMissingElseCheck:
|
4
|
+
ClassLineCountCheck:
|
5
|
+
line_count: 200
|
6
|
+
ClassNameCheck:
|
7
|
+
pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
|
8
|
+
ClassVariableCheck:
|
9
|
+
CyclomaticComplexityBlockCheck:
|
10
|
+
complexity: 3
|
11
|
+
CyclomaticComplexityMethodCheck:
|
12
|
+
complexity: 5
|
13
|
+
EmptyRescueBodyCheck:
|
14
|
+
ForLoopCheck:
|
15
|
+
MethodLineCountCheck:
|
16
|
+
line_count: 6
|
17
|
+
MethodNameCheck:
|
18
|
+
pattern: !ruby/regexp /^[\||\^|\&|\!]$|^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
|
19
|
+
ModuleLineCountCheck:
|
20
|
+
line_count: 200
|
21
|
+
ModuleNameCheck:
|
22
|
+
pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
|
23
|
+
ParameterNumberCheck:
|
24
|
+
parameter_count: 5
|
@@ -0,0 +1,71 @@
|
|
1
|
+
---
|
2
|
+
AllCops:
|
3
|
+
Exclude:
|
4
|
+
- '**/db/schema.rb'
|
5
|
+
|
6
|
+
Lint/HandleExceptions:
|
7
|
+
Exclude:
|
8
|
+
- '**/*_spec.rb'
|
9
|
+
|
10
|
+
Lint/RescueException:
|
11
|
+
Exclude:
|
12
|
+
- '**/*_spec.rb'
|
13
|
+
|
14
|
+
Style/AccessorMethodName:
|
15
|
+
Exclude:
|
16
|
+
- '**/*_spec.rb'
|
17
|
+
|
18
|
+
Style/AsciiComments:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Style/ClassAndModuleChildren:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Style/Documentation:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Style/EmptyLinesAroundBlockBody:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Style/EmptyLinesAroundClassBody:
|
31
|
+
Enabled: false
|
32
|
+
|
33
|
+
Style/EmptyLinesAroundMethodBody:
|
34
|
+
Enabled: false
|
35
|
+
|
36
|
+
Style/EmptyLinesAroundModuleBody:
|
37
|
+
Enabled: false
|
38
|
+
|
39
|
+
Style/EmptyLineBetweenDefs:
|
40
|
+
Enabled: false
|
41
|
+
|
42
|
+
Style/FileName:
|
43
|
+
Enabled: false
|
44
|
+
|
45
|
+
Style/NumericLiterals:
|
46
|
+
Enabled: false
|
47
|
+
|
48
|
+
Style/RaiseArgs:
|
49
|
+
EnforcedStyle: compact
|
50
|
+
|
51
|
+
Style/SingleLineMethods:
|
52
|
+
Exclude:
|
53
|
+
- '**/*_spec.rb'
|
54
|
+
|
55
|
+
Style/SingleSpaceBeforeFirstArg:
|
56
|
+
Enabled: false
|
57
|
+
|
58
|
+
Style/SpecialGlobalVars:
|
59
|
+
Exclude:
|
60
|
+
- '**/Gemfile'
|
61
|
+
- '**/*.gemspec'
|
62
|
+
|
63
|
+
Style/StringLiterals:
|
64
|
+
EnforcedStyle: double_quotes
|
65
|
+
|
66
|
+
Style/StringLiteralsInInterpolation:
|
67
|
+
EnforcedStyle: double_quotes
|
68
|
+
|
69
|
+
Style/TrivialAccessors:
|
70
|
+
Exclude:
|
71
|
+
- '**/*_spec.rb'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
---
|
2
|
+
# Settings added by the 'hexx-suit' gem
|
3
|
+
output: "tmp/yardstick/output.log"
|
4
|
+
path: "lib/**/*.rb"
|
5
|
+
rules:
|
6
|
+
ApiTag::Presence:
|
7
|
+
enabled: true
|
8
|
+
exclude: []
|
9
|
+
ApiTag::Inclusion:
|
10
|
+
enabled: true
|
11
|
+
exclude: []
|
12
|
+
ApiTag::ProtectedMethod:
|
13
|
+
enabled: true
|
14
|
+
exclude: []
|
15
|
+
ApiTag::PrivateMethod:
|
16
|
+
enabled: false
|
17
|
+
exclude: []
|
18
|
+
ExampleTag:
|
19
|
+
enabled: true
|
20
|
+
exclude: []
|
21
|
+
ReturnTag:
|
22
|
+
enabled: true
|
23
|
+
exclude: []
|
24
|
+
Summary::Presence:
|
25
|
+
enabled: true
|
26
|
+
exclude: []
|
27
|
+
Summary::Length:
|
28
|
+
enabled: true
|
29
|
+
exclude: []
|
30
|
+
Summary::Delimiter:
|
31
|
+
enabled: true
|
32
|
+
exclude: []
|
33
|
+
Summary::SingleLine:
|
34
|
+
enabled: true
|
35
|
+
exclude: []
|
36
|
+
threshold: 100
|
37
|
+
verbose: false
|
data/lib/rom-kafka.rb
ADDED
data/lib/rom/kafka.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "attributes_dsl"
|
3
|
+
require "poseidon"
|
4
|
+
require "rom"
|
5
|
+
|
6
|
+
# Ruby Object Mapper
|
7
|
+
#
|
8
|
+
# @see http://rom-rb.org/
|
9
|
+
#
|
10
|
+
module ROM
|
11
|
+
|
12
|
+
# Apache Kafka support for ROM
|
13
|
+
#
|
14
|
+
# @see http://kafka.apache.org/
|
15
|
+
#
|
16
|
+
module Kafka
|
17
|
+
|
18
|
+
require_relative "kafka/brokers"
|
19
|
+
require_relative "kafka/connection"
|
20
|
+
require_relative "kafka/dataset"
|
21
|
+
require_relative "kafka/gateway"
|
22
|
+
require_relative "kafka/relation"
|
23
|
+
require_relative "kafka/create"
|
24
|
+
|
25
|
+
end # module Kafka
|
26
|
+
|
27
|
+
register_adapter(:kafka, Kafka)
|
28
|
+
|
29
|
+
end # module ROM
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative "brokers/broker"
|
4
|
+
|
5
|
+
module ROM::Kafka
|
6
|
+
|
7
|
+
# Value object describing a collection of brokers (host:port)
|
8
|
+
#
|
9
|
+
# Knows how to extract brokers from address lines and options
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# brokers = Brokers.new(
|
13
|
+
# "localhost:9092",
|
14
|
+
# "127.0.0.1",
|
15
|
+
# hosts: ["127.0.0.2:9094"],
|
16
|
+
# port: 9093,
|
17
|
+
# unknown_key: :foo # will be ignored by the initializer
|
18
|
+
# )
|
19
|
+
#
|
20
|
+
# brokers.to_a
|
21
|
+
# # => ["localhost:9092", "127.0.0.2:9093", "127.0.0.3:9094"]
|
22
|
+
#
|
23
|
+
# @author Andrew Kozin <Andrew.Kozin@gmail.com>
|
24
|
+
#
|
25
|
+
class Brokers
|
26
|
+
|
27
|
+
include Equalizer.new(:to_a)
|
28
|
+
|
29
|
+
# @!method initialize(lines, options)
|
30
|
+
# Initializes an immutable collection from address lines and/or options
|
31
|
+
#
|
32
|
+
# The initializer is options-tolerant: it just ignores unknown options.
|
33
|
+
#
|
34
|
+
# @param [#to_s, Array<#to_s>] lines
|
35
|
+
# @param [Hash] options
|
36
|
+
#
|
37
|
+
# @option options [#to_s, Array<#to_s>] :hosts
|
38
|
+
# @option options [#to_i] :port
|
39
|
+
#
|
40
|
+
def initialize(*lines)
|
41
|
+
hosts, port = extract_hosts_and_port(lines)
|
42
|
+
@brokers = extract_brokers(hosts, port)
|
43
|
+
|
44
|
+
IceNine.deep_freeze(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns array of string representations of brokers
|
48
|
+
#
|
49
|
+
# @return [Array<String>]
|
50
|
+
#
|
51
|
+
def to_a
|
52
|
+
@brokers.map(&:to_s)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def extract_hosts_and_port(lines)
|
58
|
+
options = lines.last.instance_of?(Hash) ? lines.pop : {}
|
59
|
+
port = options[:port]
|
60
|
+
hosts = (lines + Array[options[:hosts]]).compact.flatten
|
61
|
+
|
62
|
+
[hosts, port]
|
63
|
+
end
|
64
|
+
|
65
|
+
def extract_brokers(hosts, port)
|
66
|
+
brokers = hosts.map { |host| Broker.new(host: host, port: port) }
|
67
|
+
brokers.any? ? brokers : [Broker.new]
|
68
|
+
end
|
69
|
+
|
70
|
+
end # class Brokers
|
71
|
+
|
72
|
+
end # module ROM::Kafka
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module ROM::Kafka
|
4
|
+
|
5
|
+
class Brokers
|
6
|
+
|
7
|
+
# Describes an address to a brocker
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# broker = Broker.new host: "localhost:9092"
|
11
|
+
# broker.to_s # => "localhost:9092"
|
12
|
+
#
|
13
|
+
# broker = Broker.new host: "localhost", port: 9092
|
14
|
+
# broker.to_s # => "localhost:9092"
|
15
|
+
#
|
16
|
+
# broker = Broker.new host: "localhost:9092", port: 9093
|
17
|
+
# broker.to_s # => "localhost:9092"
|
18
|
+
#
|
19
|
+
# @author Andrew Kozin <Andrew.Kozin@gmail.com>
|
20
|
+
#
|
21
|
+
class Broker
|
22
|
+
|
23
|
+
include Equalizer.new(:port, :host)
|
24
|
+
|
25
|
+
# Regex to extract host from address line
|
26
|
+
HOST = %r{^\w+(\:\/\/)?\S+(?=\:)|\S+}.freeze
|
27
|
+
|
28
|
+
# Regex to extract port from address line
|
29
|
+
PORT = /(?!\:)\d{4,5}$/.freeze
|
30
|
+
|
31
|
+
# @!attribute [r] host
|
32
|
+
#
|
33
|
+
# @return [String] the host of the broker
|
34
|
+
#
|
35
|
+
attr_reader :host
|
36
|
+
|
37
|
+
# @!attribute [r] port
|
38
|
+
#
|
39
|
+
# @return [Integer] the port of the broker
|
40
|
+
#
|
41
|
+
attr_reader :port
|
42
|
+
|
43
|
+
# Initializes a value object from host line and port
|
44
|
+
#
|
45
|
+
# @option options [#to_s] :host ("localhost")
|
46
|
+
# @option options [#to_i] :port (9092)
|
47
|
+
#
|
48
|
+
def initialize(options = {})
|
49
|
+
line = options.fetch(:host) { "localhost" }
|
50
|
+
@host = line[HOST]
|
51
|
+
@port = (line[PORT] || options.fetch(:port) { 9092 }).to_i
|
52
|
+
|
53
|
+
IceNine.deep_freeze(self)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the string representation of the broker in "host:port" format
|
57
|
+
#
|
58
|
+
# @return [String]
|
59
|
+
#
|
60
|
+
def to_s
|
61
|
+
"#{host}:#{port}"
|
62
|
+
end
|
63
|
+
|
64
|
+
end # class Broker
|
65
|
+
|
66
|
+
end # class Brokers
|
67
|
+
|
68
|
+
end # module ROM::Kafka
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module ROM::Kafka
|
4
|
+
|
5
|
+
# Describes the connection to Kafka cluster
|
6
|
+
#
|
7
|
+
# This is a base abstract class for producer and concumer connections.
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
#
|
11
|
+
# @author Andrew Kozin <Andrew.Kozin@gmail.com>
|
12
|
+
#
|
13
|
+
class Connection
|
14
|
+
|
15
|
+
extend AttributesDSL
|
16
|
+
|
17
|
+
require_relative "connection/producer"
|
18
|
+
require_relative "connection/consumer"
|
19
|
+
|
20
|
+
end # class Connection
|
21
|
+
|
22
|
+
end # module ROM::Kafka
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module ROM::Kafka
|
4
|
+
|
5
|
+
class Connection
|
6
|
+
|
7
|
+
# The consumer-specific connection to Kafka cluster
|
8
|
+
#
|
9
|
+
# It is wrapped around `Poseidon::Consumer` driver, and responsible for
|
10
|
+
# adopting poseidon API to ROM::Dataset via [#initializer] and [#each]
|
11
|
+
# methods.
|
12
|
+
#
|
13
|
+
# ROM::Kafka consumer deals with tuples, hiding poseidon-specific
|
14
|
+
# implementation of fetched messages from the rest of the gem.
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
#
|
18
|
+
class Consumer < Connection
|
19
|
+
|
20
|
+
include Enumerable
|
21
|
+
|
22
|
+
# The 'poseidon'-specific class describing consumers
|
23
|
+
#
|
24
|
+
# @return [Class]
|
25
|
+
#
|
26
|
+
DRIVER = Poseidon::PartitionConsumer
|
27
|
+
|
28
|
+
# Attributes acceptable by the `Poseidon::Consumer` driver
|
29
|
+
attribute :min_bytes
|
30
|
+
attribute :max_bytes
|
31
|
+
attribute :max_wait_ms
|
32
|
+
|
33
|
+
# @!attribute [r] connection
|
34
|
+
#
|
35
|
+
# @return [ROM::Kafka::Connection::Consumer::DRIVER] driver to Kafka
|
36
|
+
#
|
37
|
+
attr_reader :connection
|
38
|
+
|
39
|
+
# Initializes a consumer connection
|
40
|
+
#
|
41
|
+
# The initializer is attributes-agnostic. This means it doesn't validate
|
42
|
+
# attributes, but skips unused.
|
43
|
+
#
|
44
|
+
# @option opts [#to_s] :client_id
|
45
|
+
# A required unique id used to indentify the Kafka client.
|
46
|
+
# @option opts [Array<String>] :brokers
|
47
|
+
# A list of seed brokers to find a lead broker to fetch messages from.
|
48
|
+
# @option opts [String] :topic
|
49
|
+
# A name of the topic to fetch messages from.
|
50
|
+
# @option opts [Integer] :partition
|
51
|
+
# A number of partition to fetch messages from.
|
52
|
+
# @option opts [Integer] :offset
|
53
|
+
# An initial offset to start fetching from.
|
54
|
+
# @option opts [Integer] :min_bytes (1)
|
55
|
+
# A smallest amount of data the server should send.
|
56
|
+
# (By default send us data as soon as it is ready).
|
57
|
+
# @option opts [Integer] :max_bytes (1_048_576)
|
58
|
+
# A maximum number of bytes to fetch by consumer (1MB by default).
|
59
|
+
# @option opts [Integer] :max_wait_ms (100)
|
60
|
+
# How long to block until the server sends data.
|
61
|
+
# NOTE: This is only enforced if min_bytes is > 0.
|
62
|
+
#
|
63
|
+
def initialize(opts)
|
64
|
+
super # takes declared attributes from options
|
65
|
+
args = opts.values_at(:client_id, :brokers, :topic, :partition, :offset)
|
66
|
+
@connection = DRIVER.consumer_for_partition(*args, attributes)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Fetches a single portion of messages and converts them to tuple
|
70
|
+
#
|
71
|
+
# @return [Array<Hash{Symbol => String, Integer}>]
|
72
|
+
#
|
73
|
+
def fetch
|
74
|
+
result = @connection.fetch
|
75
|
+
result.map(&method(:tuple))
|
76
|
+
end
|
77
|
+
|
78
|
+
# Iterates through Kafka messages
|
79
|
+
#
|
80
|
+
# Fetches the next portion of messages until no messages given
|
81
|
+
#
|
82
|
+
# @return [Enumerator<Array<Hash{Symbol => String, Integer}>>]
|
83
|
+
#
|
84
|
+
# @yieldparam [Hash{Symbol => String, Integer}] tuple
|
85
|
+
#
|
86
|
+
def each
|
87
|
+
return to_enum unless block_given?
|
88
|
+
loop do
|
89
|
+
tuples = fetch
|
90
|
+
break unless tuples.any?
|
91
|
+
tuples.each { |tuple| yield(tuple) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def tuple(msg)
|
98
|
+
{ value: msg.value, topic: msg.topic, key: msg.key, offset: msg.offset }
|
99
|
+
end
|
100
|
+
|
101
|
+
end # class Consumer
|
102
|
+
|
103
|
+
end # class Connection
|
104
|
+
|
105
|
+
end # module ROM::Kafka
|