rom-kafka 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +9 -0
  4. data/.metrics +9 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +2 -0
  7. data/.travis.yml +34 -0
  8. data/.yardopts +3 -0
  9. data/CHANGELOG.md +3 -0
  10. data/Gemfile +7 -0
  11. data/Guardfile +14 -0
  12. data/LICENSE +21 -0
  13. data/README.md +83 -0
  14. data/Rakefile +34 -0
  15. data/config/metrics/STYLEGUIDE +230 -0
  16. data/config/metrics/cane.yml +5 -0
  17. data/config/metrics/churn.yml +6 -0
  18. data/config/metrics/flay.yml +2 -0
  19. data/config/metrics/metric_fu.yml +14 -0
  20. data/config/metrics/reek.yml +1 -0
  21. data/config/metrics/roodi.yml +24 -0
  22. data/config/metrics/rubocop.yml +71 -0
  23. data/config/metrics/saikuro.yml +3 -0
  24. data/config/metrics/simplecov.yml +6 -0
  25. data/config/metrics/yardstick.yml +37 -0
  26. data/lib/rom-kafka.rb +3 -0
  27. data/lib/rom/kafka.rb +29 -0
  28. data/lib/rom/kafka/brokers.rb +72 -0
  29. data/lib/rom/kafka/brokers/broker.rb +68 -0
  30. data/lib/rom/kafka/connection.rb +22 -0
  31. data/lib/rom/kafka/connection/consumer.rb +105 -0
  32. data/lib/rom/kafka/connection/producer.rb +114 -0
  33. data/lib/rom/kafka/create.rb +75 -0
  34. data/lib/rom/kafka/dataset.rb +132 -0
  35. data/lib/rom/kafka/gateway.rb +165 -0
  36. data/lib/rom/kafka/relation.rb +78 -0
  37. data/lib/rom/kafka/version.rb +13 -0
  38. data/rom-kafka.gemspec +33 -0
  39. data/spec/integration/basic_usage_spec.rb +58 -0
  40. data/spec/integration/keys_usage_spec.rb +34 -0
  41. data/spec/shared/scholars_topic.rb +28 -0
  42. data/spec/spec_helper.rb +20 -0
  43. data/spec/unit/brokers/broker_spec.rb +89 -0
  44. data/spec/unit/brokers_spec.rb +46 -0
  45. data/spec/unit/connection/consumer_spec.rb +90 -0
  46. data/spec/unit/connection/producer_spec.rb +79 -0
  47. data/spec/unit/create_spec.rb +79 -0
  48. data/spec/unit/dataset_spec.rb +165 -0
  49. data/spec/unit/gateway_spec.rb +171 -0
  50. data/spec/unit/relation_spec.rb +96 -0
  51. metadata +219 -0
@@ -0,0 +1,5 @@
1
+ ---
2
+ abc_max: "10"
3
+ line_length: "80"
4
+ no_doc: "y"
5
+ no_readme: "y"
@@ -0,0 +1,6 @@
1
+ ---
2
+ ignore_files:
3
+ - spec
4
+ - config
5
+ minimum_churn_count: 0
6
+ start_date: "1 year ago"
@@ -0,0 +1,2 @@
1
+ ---
2
+ minimum_score: 9
@@ -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,3 @@
1
+ ---
2
+ warn_cyclo: 3
3
+ error_cyclo: 5
@@ -0,0 +1,6 @@
1
+ ---
2
+ output: tmp/coverage
3
+ filters: # The list of paths to be excluded from coverage checkup
4
+ - "spec/"
5
+ - "config/"
6
+ groups: []
@@ -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
@@ -0,0 +1,3 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative "rom/kafka"
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