redis-client-extensions 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5e12a53c22b3753cbf66fce5f000c1a46978669e
4
+ data.tar.gz: 90684cb72c11c0bfa4a1cc8e53db489f6489aa00
5
+ SHA512:
6
+ metadata.gz: f54b97287fdc8df43828e3276f196bd30e76e34263121cf820b6e96d90eb64b795a5b56174bbb7d44ccdc316c3d4cfe019b2e80933f2f7a97a5cfceecbf23a76
7
+ data.tar.gz: a5f38f645e1be7397ab0e3d8d38027ffb601349d9cf7c8bda0b628679bcc9d038410e1276e3aa0f91256738a38ca0630e43ec84b740b4b6c393645242fc26686
data/.gitignore ADDED
@@ -0,0 +1,45 @@
1
+ # Numerous always-ignore extensions
2
+ *.diff
3
+ *.err
4
+ *.orig
5
+ *.log
6
+ *.rej
7
+ *.swo
8
+ *.swp
9
+ *.vi
10
+ *~
11
+ *.sass-cache
12
+
13
+ # OS or Editor folders
14
+ .DS_Store
15
+ .cache
16
+ .project
17
+ .settings
18
+ .tmproj
19
+ nbproject
20
+ Thumbs.db
21
+
22
+ # Dreamweaver added files
23
+ _notes
24
+ dwsync.xml
25
+
26
+ # Komodo
27
+ *.komodoproject
28
+ .komodotools
29
+
30
+ # Folders to ignore
31
+ .hg
32
+ .svn
33
+ .CVS
34
+ intermediate
35
+ publish
36
+ .idea
37
+ .bundle
38
+ db/*.sqlite3
39
+ log/*.log
40
+ tmp/
41
+ .sass-cache/
42
+ dump.rdb
43
+ *.gem
44
+ Makefile
45
+
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.0
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ redis-client-extensions (0.0.1)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.5)
10
+ mock_redis (0.13.2)
11
+ redis (3.0.7)
12
+ rspec (3.0.0)
13
+ rspec-core (~> 3.0.0)
14
+ rspec-expectations (~> 3.0.0)
15
+ rspec-mocks (~> 3.0.0)
16
+ rspec-core (3.0.2)
17
+ rspec-support (~> 3.0.0)
18
+ rspec-expectations (3.0.2)
19
+ diff-lcs (>= 1.2.0, < 2.0)
20
+ rspec-support (~> 3.0.0)
21
+ rspec-mocks (3.0.2)
22
+ rspec-support (~> 3.0.0)
23
+ rspec-support (3.0.2)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ mock_redis (~> 0.13.2)
30
+ redis (~> 3.0.7)
31
+ redis-client-extensions!
32
+ rspec (~> 3.0.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Andrew Berls
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # RedisClientExtensions
2
+
3
+ A set of useful core extensions to the [redis-rb](https://github.com/redis/redis-rb) client library, extracted from
4
+ [KnowMyRankings](https://www.knowmyrankings.com/).
5
+
6
+
7
+
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'redis-client-extensions'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install redis-client-extensions
22
+
23
+
24
+ The extensions will add themselves to the Redis client automatically - in our Rails app, we have
25
+ an initializer that looks something like
26
+
27
+ ```ruby
28
+ require 'redis-client-extensions'
29
+
30
+ $redis = Redis.new(...)
31
+ ```
32
+
33
+ The extensions also work with [MockRedis](https://github.com/causes/mock_redis) out of the box
34
+ if it is installed.
35
+
36
+ Note that the library is coded against Ruby 2.1+.
37
+
38
+ ## Usage
39
+
40
+ Currently added functions are:
41
+
42
+ - `hmultiset(hkey, hash)` - convenience method for storing a Ruby hash into a redis hash
43
+
44
+ ```ruby
45
+ $redis.hmultiset("my-hash", { name: "tom", color: "red" })
46
+ $redis.hget("my-hash", "name") # => "tom"
47
+ $redis.hget("my-hash", "color") # => "red"
48
+ ```
49
+
50
+ - `find_keys_in_batches(options)` - A wrapper for [SCAN](http://redis.io/commands/scan) to iterate over keys matching a pattern.
51
+ Options are passed through to SCAN.
52
+
53
+ ```ruby
54
+ $redis.find_keys_in_batches(match: "mypattern*", count: 100) do |keys|
55
+ puts "Got batch of #{keys.count} keys"
56
+ puts "Values for this batch: #{$redis.mget(keys)}"
57
+ end
58
+ ```
59
+
60
+ - `get_i(key)` - Parse an Integer stored at a key
61
+
62
+ ```ruby
63
+ $redis.get_i('price') # => 9
64
+ ```
65
+
66
+ - `cache_fetch(key, expires_in:, &block)` - Get or set a value (to be stored with [Marshal](http://www.ruby-doc.org/core-2.1.2/Marshal.html)) expiring in some number of seconds
67
+
68
+ ```ruby
69
+ # Initial cache miss, block evaluated to store value
70
+ ret = $redis.cache_fetch('my-key', expires_in: 60) do
71
+ 'cached-value'
72
+ end
73
+ # => 'cached-value'
74
+ #
75
+ # Calling again retrieves cached value, block will not be called
76
+ ret = $redis.cache_fetch('my-key', expires_in: 60) do
77
+ 'something-else' # Not called!
78
+ end
79
+ # => 'cached-value'
80
+ ```
81
+
82
+ - `mdump(key, val)` - Serialize and store a value at `key` using Marshal
83
+
84
+ ```ruby
85
+ $redis.mdump('my-key', [1,2,3]) # => 'OK'
86
+ ```
87
+
88
+ - `mload(key)` - Retrieve a Marshalled value from `key`
89
+
90
+ ```ruby
91
+ $redis.mdump('my-key', [1,2,3])
92
+ $redis.mload('my-key') # => [1,2,3]
93
+ $redis.mload('does-not-exist') # => nil
94
+ ```
95
+
96
+
97
+ ## Contributing
98
+
99
+ 1. Fork it ( http://github.com/<my-github-username>/redis-client-extensions/fork )
100
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
101
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
102
+ 4. Push to the branch (`git push origin my-new-feature`)
103
+ 5. Create new Pull Request
@@ -0,0 +1,106 @@
1
+ require 'redis'
2
+
3
+ module RedisClientExtensions
4
+
5
+ # Store a Ruby hash into a redis hash
6
+ #
7
+ # Examples:
8
+ # $redis.hmultiset("my-key", { name: "tom", color: "red" })
9
+ # $redis.hget("my-key", "name") # => "tom"
10
+ # $redis.hget("my-key", "color") # => "red"
11
+ #
12
+ # Returns 'OK'
13
+ def hmultiset(hkey, hash)
14
+ hmset(hkey, *(hash.map { |k, v| [k, v] }.flatten))
15
+ 'OK'
16
+ end
17
+
18
+
19
+ # Wrapper for #scan to iterate over a set of keys matching a pattern
20
+ #
21
+ # options - Hash of options
22
+ # :match - String pattern to match against, ex: "mypattern*"
23
+ # :count - (Optional) Integer hint for number of elements to return
24
+ # each iteration
25
+ #
26
+ # <block> - Proc to handle each batch of keys
27
+ # Will be invoked with Array[String] of key names
28
+ #
29
+ # Ex:
30
+ #
31
+ # $redis.find_keys_in_batches(match: "mypattern*", count: 100) do |keys|
32
+ # puts "Got batch of #{keys.count} keys"
33
+ # puts "Values for this batch: #{$redis.mget(keys)}"
34
+ # end
35
+ #
36
+ # See Redis docs for SCAN.
37
+ #
38
+ # Returns nothing
39
+ def find_keys_in_batches(options={})
40
+ cursor = 0
41
+ while (cursor, keys = scan(cursor, options); cursor.to_i != 0)
42
+ yield keys
43
+ end
44
+
45
+ yield keys # Leftovers from final iteration
46
+ end
47
+
48
+
49
+ # Parse the value stored at `key` as an Integer,
50
+ # or return nil if it doesn't exist
51
+ #
52
+ # key - String key
53
+ #
54
+ # Return Integer or nil
55
+ def get_i(key)
56
+ val = get(key)
57
+ val.nil? ? nil : val.to_i
58
+ end
59
+
60
+
61
+ # Get/set a value cached in a key
62
+ # Values are marshalled to preserve class
63
+ #
64
+ # key - String key name
65
+ # expires_in: Integer TTL of the key, in seconds
66
+ # block - Proc to compute the value to be cached on miss
67
+ #
68
+ # Returns cached value or result of evaluating <block>
69
+ def cache_fetch(key, expires_in:, &block)
70
+ if ret = mload(key)
71
+ ret
72
+ else
73
+ val = block.call
74
+ mdump(key, val)
75
+ expire(key, expires_in)
76
+ val
77
+ end
78
+ end
79
+
80
+
81
+ # Load a Marshalled value stored at <key>
82
+ # Returns nil if key does not exist
83
+ #
84
+ # Returns class of marshalled value
85
+ def mload(key)
86
+ if val = get(key)
87
+ Marshal.load(val)
88
+ end
89
+ end
90
+
91
+
92
+ # Store a marshalled value at <key>
93
+ #
94
+ # key - String key
95
+ # val = Any value
96
+ #
97
+ # Returns 'OK'
98
+ def mdump(key, val)
99
+ set(key, Marshal.dump(val))
100
+ end
101
+
102
+ end
103
+
104
+
105
+ Redis.send(:include, RedisClientExtensions)
106
+ defined?(MockRedis) and MockRedis.send(:include, RedisClientExtensions)
@@ -0,0 +1,3 @@
1
+ module RedisClientExtensions
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'redis-client-extensions/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "redis-client-extensions"
8
+ spec.version = RedisClientExtensions::VERSION
9
+ spec.authors = ["Andrew Berls"]
10
+ spec.email = ["andrew.berls@gmail.com"]
11
+ spec.summary = %q{Useful extensions to the redis-rb client library}
12
+ spec.description = %q{A set of core extensions to the redis-rb client library}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency 'mock_redis', '~> 0.13.2'
22
+ spec.add_development_dependency 'redis', '~> 3.0.7'
23
+ spec.add_development_dependency 'rspec', '~> 3.0.0'
24
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ describe RedisClientExtensions do
4
+ let(:redis) { MockRedis.new }
5
+
6
+ context '#hmultiset' do
7
+ let(:key) { 'myhash' }
8
+ let(:hash) do
9
+ {
10
+ one: 'a',
11
+ two: 'b',
12
+ three: 'c'
13
+ }
14
+ end
15
+
16
+ it 'sets a redis hash value for each value in the hash' do
17
+ redis.hmultiset(key, hash)
18
+
19
+ expect(redis.hget(key, 'one')).to eq 'a'
20
+ expect(redis.hget(key, 'two')).to eq 'b'
21
+ expect(redis.hget(key, 'three')).to eq 'c'
22
+ end
23
+ end
24
+
25
+
26
+ context '.get_i' do
27
+ it 'parses a value as an integer' do
28
+ redis.set('mykey', 1)
29
+
30
+ expect(redis.get_i('mykey')).to eq 1
31
+ end
32
+
33
+ it 'returns nil for nonexistent keys' do
34
+ expect(redis.get_i('does-not-exist')).to be_nil
35
+ end
36
+ end
37
+
38
+
39
+ # No scan in MockRedis. Leaving this test for posterity.
40
+ #
41
+ # context '#find_keys_in_batches' do
42
+ # before do
43
+ # redis.set('test:a', 'one')
44
+ # redis.set('test:b', 'two')
45
+ # redis.set('test:c', 'three')
46
+ # redis.set('test:d', 'four')
47
+ # end
48
+ #
49
+ # it 'iterates over each key matching a pattern' do
50
+ # result = []
51
+ # redis.find_keys_in_batches.each do |keys|
52
+ # result.concat(redis.mget(keys))
53
+ # end
54
+ #
55
+ # expect(result).to eq ['one', 'two', 'three', 'four']
56
+ # end
57
+ # end
58
+
59
+
60
+ context '#cache_fetch' do
61
+ let(:key) { "cache-fetch-test" }
62
+
63
+ after(:each) { redis.del(key) }
64
+
65
+ it 'stores a value into the cache on miss' do
66
+ expect(redis).to receive(:set).with(key, Marshal.dump('cached-value'))
67
+ .and_return('cached-value')
68
+ expect(redis).to receive(:expire).with(key, 60)
69
+
70
+ redis.cache_fetch(key, expires_in: 60) do
71
+ 'cached-value'
72
+ end
73
+ end
74
+
75
+ it 'retrieves cached values' do
76
+ redis.set(key, Marshal.dump('cached-value'))
77
+ expect(redis).to_not receive(:set)
78
+ expect(redis).to_not receive(:expire)
79
+
80
+ val = redis.cache_fetch(key, expires_in: 300) do
81
+ 'wont_be_called'
82
+ end
83
+ expect(val).to eq 'cached-value'
84
+ end
85
+ end
86
+
87
+
88
+ context '#mload' do
89
+ let(:key) { 'mload-test' }
90
+
91
+ it 'retrieves a marshalled value' do
92
+ redis.set(key, Marshal.dump([1,2,3]))
93
+ expect(redis.mload(key)).to eq [1,2,3]
94
+ end
95
+
96
+ it 'returns nil for keys that do not exist' do
97
+ expect(redis.mload('does-not-exist')).to be_nil
98
+ end
99
+ end
100
+
101
+
102
+ context '#mdump' do
103
+ let(:key) { 'medump-test' }
104
+
105
+ it 'stores a marshalled value' do
106
+ expect(redis.mdump(key, [1,2,3])).to eq 'OK'
107
+
108
+ expect(Marshal.load(redis.get(key))).to eq [1,2,3]
109
+ end
110
+ end
111
+
112
+ end
113
+
@@ -0,0 +1,4 @@
1
+ lib = File.expand_path('../../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'mock_redis'
4
+ require 'redis-client-extensions'
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-client-extensions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Berls
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mock_redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.13.2
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.13.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: redis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.7
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.0.7
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.0.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.0.0
55
+ description: A set of core extensions to the redis-rb client library
56
+ email:
57
+ - andrew.berls@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".ruby-version"
65
+ - Gemfile
66
+ - Gemfile.lock
67
+ - LICENSE.txt
68
+ - README.md
69
+ - lib/redis-client-extensions.rb
70
+ - lib/redis-client-extensions/version.rb
71
+ - redis-client-extensions.gemspec
72
+ - spec/redis_client_extensions_spec.rb
73
+ - spec/spec_helper.rb
74
+ homepage: ''
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.2.0
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Useful extensions to the redis-rb client library
98
+ test_files:
99
+ - spec/redis_client_extensions_spec.rb
100
+ - spec/spec_helper.rb