rom-kafka 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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