redis_stream 0.2.1 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e7e670f9058b83d4320a7d90f7bc6d65cbc01eed157f48ff0080a12cb5f1005
4
- data.tar.gz: 3719676857cc946547a6b2198298bb6a05a0d4b100d8c8eeb01097a9a7db7685
3
+ metadata.gz: 4f61cf214e2178d4b843245af1622d226b9636903c17308de97ccfc0bd75bab3
4
+ data.tar.gz: dbc74cb1808d5838fa4d26de5c86669f35d292b207e900f6aaf45d55e62ce06a
5
5
  SHA512:
6
- metadata.gz: 2636062233cd1f964469caf870be2e22b7eaf4b4cb6a187ac452ae693a90248ad83683d5c19ad9df589d466f100dd912ab20c123404dbb2b19d289bd4fd8e099
7
- data.tar.gz: fbe67b94482dc9f1401783afec0e0e478f8b0bcdd68c60ac540360c2cfe51ac211c686e9b994d480c39da5401a01538ce6f12e01866f3fdf207d26705eb5165e
6
+ metadata.gz: a83b7e3985d5f30f3a66e35bd32d8d8dfe55f89ddb1d77af8354981f4419beab642e26732f3c2b0775694a0a23602ee25fc63ae0321b1a9c7891fb5e5c423b03
7
+ data.tar.gz: 57b88bc65f63499cc5c372bd94e14b1a6b3236203479b2f59b98942fc118c252ba542f0fa2003c7e62e00c005f68e2596969794f875b23f267403db7bfd702f7
@@ -1,30 +1,43 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- redis_stream (0.1.0)
4
+ redis_stream (0.3.0)
5
5
  redis (~> 4.1)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  coderay (1.1.2)
11
+ diff-lcs (1.3)
11
12
  method_source (0.9.2)
12
- minitest (5.13.0)
13
13
  pry (0.12.2)
14
14
  coderay (~> 1.1.0)
15
15
  method_source (~> 0.9.0)
16
16
  rake (13.0.1)
17
- redis (4.1.3)
17
+ redis (4.2.1)
18
+ rspec (3.9.0)
19
+ rspec-core (~> 3.9.0)
20
+ rspec-expectations (~> 3.9.0)
21
+ rspec-mocks (~> 3.9.0)
22
+ rspec-core (3.9.2)
23
+ rspec-support (~> 3.9.3)
24
+ rspec-expectations (3.9.2)
25
+ diff-lcs (>= 1.2.0, < 2.0)
26
+ rspec-support (~> 3.9.0)
27
+ rspec-mocks (3.9.1)
28
+ diff-lcs (>= 1.2.0, < 2.0)
29
+ rspec-support (~> 3.9.0)
30
+ rspec-support (3.9.3)
18
31
 
19
32
  PLATFORMS
20
33
  ruby
21
34
 
22
35
  DEPENDENCIES
23
36
  bundler (~> 1.17)
24
- minitest (~> 5.0)
25
37
  pry (~> 0.12)
26
38
  rake (~> 13.0)
27
39
  redis_stream!
40
+ rspec (~> 3.9)
28
41
 
29
42
  BUNDLED WITH
30
43
  1.17.3
data/README.md CHANGED
@@ -5,10 +5,6 @@
5
5
  <a href="https://codeclimate.com/github/tomorrowhq/redis-stream/test_coverage"><img src="https://api.codeclimate.com/v1/badges/73f0d460cf35b758e624/test_coverage" /></a>
6
6
  </p>
7
7
 
8
- ## Status
9
-
10
- Currently project is under active development.
11
-
12
8
  ## Usage
13
9
 
14
10
  ### Setting up
@@ -18,19 +14,9 @@ out of the box if you are using redis. It will be using same `REDIS_URL`
18
14
  environment variable to establish connection with Redis.
19
15
 
20
16
  ```ruby
21
- require 'redis_stream'
22
-
23
- client = RedisStream.new
24
- ```
25
-
26
- When you need to have control over redis connection you can pass redis instance
27
- in initializer.
28
-
29
- ```ruby
30
- require 'redis_stream'
17
+ # Gemfile
31
18
 
32
- redis = Redis.new # set it up as you need
33
- client = RedisStream.new(redis)
19
+ gem "redis_stream", "~> 0.3.0"
34
20
  ```
35
21
 
36
22
  ### Adding messages to the stream
@@ -38,38 +24,36 @@ client = RedisStream.new(redis)
38
24
  ```ruby
39
25
  require 'redis_stream'
40
26
 
41
- client = RedisStream.new
42
-
43
- weather_stream = client.stream('weather')
27
+ daily_temperature = RedisStream.stream(name: "daily_temperature")
44
28
 
45
- weather_stream.add({ temp: 20.0 })
46
- weather_stream.add({ temp: 21.0 })
47
- weather_stream.add({ temp: 22.0 })
29
+ daily_temperature << 20.0
30
+ daily_temperature << 21.0
31
+ daily_temperature << 22.0
48
32
 
49
- puts weather_stream.len
33
+ puts daily_temperature.size
50
34
  #=> 3
51
35
  ```
52
36
 
53
37
  ### Consuming messages from the stream
54
38
 
55
- ```ruby
56
- require 'redis_stream'
57
-
58
- client = RedisStream.new
59
- messages_stream = client.stream('messages')
60
-
61
- messages_stream.add({ msg: 'Message 1' })
62
- messages_stream.add({ msg: 'Message 2' })
39
+ Stream implements most of operations that can be performed on ruby Array.
63
40
 
64
- messages_stream.each_message do |message|
41
+ ```ruby
42
+ daily_temperature.each do |temperature|
65
43
  puts message
66
44
  end
67
- # => { "msg" => Message 1 }
68
- # => { "msg" => Message 2 }
45
+ # => 20.0
46
+ # => 21.0
47
+ # => 22.0
48
+
49
+ puts daily_temperature.last
50
+ # => 22.0
69
51
 
70
- puts messages_stream.last
71
- # => { "msg" => Message 2 }
52
+ puts daily_temperature.first
53
+ # => 20.0
72
54
 
73
- puts messages_stream.first
74
- # => { "msg" => Message 2 }
55
+ average = daily_temperature.sum / daily_temperature.length
56
+ min = daily_temperature.min
57
+ max = daily_temperature.max
58
+ puts "Average: #{average} Min: #{min} Max: #{max}"
75
59
  ```
@@ -2,13 +2,22 @@ require 'redis'
2
2
  require 'redis_stream/version'
3
3
  require 'redis_stream/group'
4
4
  require 'redis_stream/stream'
5
+ require 'redis_stream/consumer'
5
6
  require 'redis_stream/client'
6
7
 
7
8
  module RedisStream
8
9
  class Error < StandardError; end
9
10
 
10
- def self.new(redis: nil)
11
- redis = redis.nil? ? Redis.new : redis
12
- Client.new(redis)
11
+ # @param name <String> Name of a stream
12
+ # @return <RedisStream::Stream>
13
+ def self.stream(name:)
14
+ Stream.new(name: name)
15
+ end
16
+
17
+ # @param name <String> Name of a group
18
+ # @param stream <RedisStream::Stream> Stream that group should consume
19
+ # @return <RedisStream::Group>
20
+ def self.group(name:, stream:)
21
+ Group.new(name: name, stream: stream)
13
22
  end
14
23
  end
@@ -0,0 +1,35 @@
1
+ module RedisStream
2
+ class Consumer
3
+ include Enumerable
4
+
5
+ attr_reader :name, :group, :stream
6
+
7
+ def initialize(name:, group:, stream:)
8
+ @name = name
9
+ @group = group
10
+ @stream = stream
11
+ end
12
+
13
+ def each(&block)
14
+ while
15
+ result = read_next
16
+ break if result.empty?
17
+
18
+ message = result[stream.name].first
19
+ id, content = message
20
+ block.call(load(content))
21
+ Redis.current.xack(stream.name, name, id)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def read_next
28
+ Redis.current.xreadgroup(group.name, name, stream.name, '>', count: 1)
29
+ end
30
+
31
+ def load(message_content)
32
+ Marshal.load(message_content["value"])
33
+ end
34
+ end
35
+ end
@@ -1,54 +1,36 @@
1
1
  module RedisStream
2
2
  class Group
3
- attr_reader :key, :name
3
+ attr_reader :name, :stream
4
4
 
5
- def initialize(redis:, key:, name:)
6
- @redis = redis
7
- @key = key
5
+ # @param name <String>
6
+ # @param stream <RedisStream::Stream>
7
+ # @return RedisStream::Group
8
+ def initialize(name:, stream:)
8
9
  @name = name
9
- @last_delivered_id = '0'
10
- end
11
-
12
- # Creates group and stream if group does not exist.
13
- # @return [Group]
14
- def create
15
- if info.nil?
16
- @redis.xgroup(:create, key, name, @last_delivered_id, mkstream: true)
17
- end
18
-
19
- self
20
- end
10
+ @stream = stream
21
11
 
22
- # @return [Group]
23
- def destroy
24
- @redis.xgroup(:destroy, key, name)
25
- self
12
+ create_group
26
13
  end
27
14
 
28
- def info
29
- find_me(@redis.xinfo(:groups, key))
30
- rescue Redis::CommandError
31
- nil
15
+ # @param name <String> Consumer name
16
+ # @return <RedisStream::Consumer>
17
+ def consumer(name)
18
+ RedisStream::Consumer.new(name: name, group: self, stream: stream)
32
19
  end
33
20
 
34
- def each_message(consumer: 'c1', ack: true)
35
- while
36
- result = @redis.xreadgroup(name, consumer, key, '>', count: 1)
37
- break if result.empty?
38
-
39
- messages = result[key]
40
- messages.each do |message|
41
- id, entry = message
42
- yield(entry)
43
- @redis.xack(key, name, id) if ack
44
- end
45
- end
21
+ # Resets group's next id on the stream
22
+ def reset(id = "0")
23
+ Redis.current.xgroup(:setid, stream.name, name, id)
24
+ self
46
25
  end
47
26
 
48
27
  private
49
28
 
50
- def find_me(groups)
51
- groups.find { |group| group['name'] == name }
29
+ def create_group
30
+ Redis.current.xgroup(
31
+ :create, stream.name, name, "$", mkstream: true)
32
+ rescue Redis::CommandError
33
+ nil
52
34
  end
53
35
  end
54
36
  end
@@ -1,53 +1,62 @@
1
1
  module RedisStream
2
2
  class Stream
3
- attr_reader :key
3
+ include Enumerable
4
4
 
5
- def initialize(key:, redis:)
6
- @key = key
7
- @redis = redis
8
- end
5
+ attr_reader :name
9
6
 
10
- def add(entry)
11
- @redis.xadd(key, entry)
7
+ def initialize(name:)
8
+ @name = name
9
+ @values = []
12
10
  end
13
11
 
14
- def len
15
- @redis.xlen(key)
12
+ def <<(value)
13
+ Redis.current.xadd(name, dump(value))
14
+ self
16
15
  end
16
+ alias_method :push, :<<
17
17
 
18
18
  def clear
19
- @redis.xtrim(key, 0)
19
+ Redis.current.xtrim(name, 0)
20
20
  end
21
21
 
22
- def group(name:)
23
- RedisStream::Group.new(redis: @redis, key: key, name: name)
22
+ def last(count = 1)
23
+ messages = Redis.current.xrevrange(name, '+', '-', count: count)
24
+ messages.reverse!
25
+
26
+ result = messages.map do |message|
27
+ _id, content = message
28
+ load(content)
29
+ end
30
+
31
+ count == 1 ? result.first : result
24
32
  end
25
33
 
26
- def each_message
27
- last_id = '0'
34
+ def length
35
+ Redis.current.xlen(name)
36
+ end
37
+ alias_method :size, :length
38
+
39
+ def each(&block)
40
+ current_message_id = "0"
28
41
 
29
42
  while
30
- result = @redis.xread(key, last_id, count: 1)
43
+ result = Redis.current.xread(name, current_message_id, count: 1)
31
44
  break if result.empty?
32
45
 
33
- messages = result[key]
34
- messages.each do |message|
35
- last_id, entry = message
36
- yield(entry)
37
- end
46
+ message = result[name].first
47
+ current_message_id, message_content = message
48
+ block.call(load(message_content))
38
49
  end
39
50
  end
40
51
 
41
- # XREVRANGE [name] + - COUNT 1
42
- def last
43
- _id, entry = @redis.xrevrange(key, '+', '-', count: 1).first
44
- entry
52
+ private
53
+
54
+ def dump(value)
55
+ { value: Marshal.dump(value) }
45
56
  end
46
57
 
47
- # XRANGE [name] - + COUNT 1
48
- def first
49
- _id, entry = @redis.xrange(key, '-', '+', count: 1).first
50
- entry
58
+ def load(message_content)
59
+ Marshal.load(message_content["value"])
51
60
  end
52
61
  end
53
62
  end
@@ -1,3 +1,3 @@
1
1
  module RedisStream
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ['choixer@gmail.com']
11
11
 
12
12
  spec.summary = %q{Interface around Redis Streams for Ruby}
13
- spec.description = %q{..}
13
+ spec.description = %q{Make Redis Streams feel like a first-class citizen in Ruby}
14
14
  spec.homepage = 'https://github.com/antonvolkoff/redis-stream'
15
15
 
16
16
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
@@ -28,17 +28,19 @@ Gem::Specification.new do |spec|
28
28
 
29
29
  # Specify which files should be added to the gem when it is released.
30
30
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
31
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
32
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
31
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
32
+ `git ls-files -z`.split("\x0").reject do |f|
33
+ f.match(%r{^(test|spec|features|examples)/})
34
+ end
33
35
  end
34
36
  # spec.bindir = "exe"
35
37
  # spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
36
38
  spec.require_paths = ['lib']
37
39
 
38
- spec.add_dependency 'redis', '~> 4.1'
40
+ spec.add_dependency "redis", "~> 4.1"
39
41
 
40
- spec.add_development_dependency 'bundler', '~> 1.17'
41
- spec.add_development_dependency 'rake', '~> 13.0'
42
- spec.add_development_dependency 'minitest', '~> 5.0'
43
- spec.add_development_dependency 'pry', '~> 0.12'
42
+ spec.add_development_dependency "bundler", "~> 1.17"
43
+ spec.add_development_dependency "rake", "~> 13.0"
44
+ spec.add_development_dependency "pry", "~> 0.12"
45
+ spec.add_development_dependency "rspec", "~> 3.9"
44
46
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis_stream
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anton Volkov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-18 00:00:00.000000000 Z
11
+ date: 2020-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -53,34 +53,34 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '13.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: minitest
56
+ name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '5.0'
61
+ version: '0.12'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '5.0'
68
+ version: '0.12'
69
69
  - !ruby/object:Gem::Dependency
70
- name: pry
70
+ name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0.12'
75
+ version: '3.9'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0.12'
83
- description: ".."
82
+ version: '3.9'
83
+ description: Make Redis Streams feel like a first-class citizen in Ruby
84
84
  email:
85
85
  - choixer@gmail.com
86
86
  executables: []
@@ -99,6 +99,7 @@ files:
99
99
  - docker-compose.yml
100
100
  - lib/redis_stream.rb
101
101
  - lib/redis_stream/client.rb
102
+ - lib/redis_stream/consumer.rb
102
103
  - lib/redis_stream/group.rb
103
104
  - lib/redis_stream/stream.rb
104
105
  - lib/redis_stream/version.rb