redis-client-extensions 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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