redis-file 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +7 -0
- data/LICENSE +21 -0
- data/README.md +86 -0
- data/Rakefile +27 -0
- data/lib/redis-file.rb +6 -0
- data/lib/redis-file/expiring_hash.rb +70 -0
- data/lib/redis-file/rspec.rb +23 -0
- data/lib/redis-file/sorted_set_argument_handler.rb +74 -0
- data/lib/redis-file/sorted_set_store.rb +80 -0
- data/lib/redis-file/version.rb +3 -0
- data/lib/redis-file/zset.rb +29 -0
- data/lib/redis/connection/file.rb +960 -0
- data/redis-file.gemspec +23 -0
- data/spec/compatibility_spec.rb +9 -0
- data/spec/connection_spec.rb +85 -0
- data/spec/hashes_spec.rb +182 -0
- data/spec/keys_spec.rb +248 -0
- data/spec/lists_spec.rb +195 -0
- data/spec/server_spec.rb +100 -0
- data/spec/sets_spec.rb +178 -0
- data/spec/sorted_sets_spec.rb +425 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/spec_helper_live_redis.rb +14 -0
- data/spec/strings_spec.rb +236 -0
- data/spec/transactions_spec.rb +19 -0
- data/spec/upcase_method_name_spec.rb +18 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e596eb446bbf827dad2d9251d6802817d9b3763f
|
4
|
+
data.tar.gz: 5491961a1adce14639bc37f9acaeae6b6ebece98
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 787696710e82958c2a07a55fdb2b81973f2260d045ffe8277cc6afde71f2e92eef378ee388e9d76f1450e5fb8c4c87664dc2677d0b983382992d0fecd9562dc6
|
7
|
+
data.tar.gz: 8104ba9421bf5a7bbd58368bc98a75e8c152a03308194ac2c767f7092bfcfdaa69246c0b05b47d87551fdc33c8a433f3b8079f697f123f5f893f500a38776171
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
-b -fn --color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2013 Daniel Loureiro
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/README.md
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
# redis-file [![Build Status](https://secure.travis-ci.org/loureirorg/redis-file.png)](http://travis-ci.org/loureirorg/redis-file)
|
2
|
+
This a Ruby implementation of redis-rb for machines without Redis or development/test environments
|
3
|
+
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install the gem:
|
8
|
+
|
9
|
+
gem install redis-file
|
10
|
+
|
11
|
+
Add it to your Gemfile:
|
12
|
+
|
13
|
+
gem "redis-file"
|
14
|
+
|
15
|
+
|
16
|
+
## Versions
|
17
|
+
|
18
|
+
redis-file currently supports redis-rb v3.0.0 or later, if you are using
|
19
|
+
redis-rb v2.2.x install the version 0.3.x:
|
20
|
+
|
21
|
+
gem install redis-file -v "~> 0.3.0"
|
22
|
+
|
23
|
+
or use the branch 0-3-x on your Gemfile:
|
24
|
+
|
25
|
+
gem "redis-file", :git => "git://github.com/loureirorg/redis-file.git", :branch => "0-3-x"
|
26
|
+
|
27
|
+
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
You can use redis-file without any changes:
|
31
|
+
|
32
|
+
require "redis-file"
|
33
|
+
|
34
|
+
redis = Redis.new
|
35
|
+
|
36
|
+
>> redis.set "foo", "bar"
|
37
|
+
=> "OK"
|
38
|
+
|
39
|
+
>> redis.get "foo"
|
40
|
+
=> "bar"
|
41
|
+
|
42
|
+
Read [redis-rb](https://github.com/ezmobius/redis-rb) documentation and
|
43
|
+
[Redis](http://redis.io) homepage for more info about commands
|
44
|
+
|
45
|
+
## Usage with RSpec
|
46
|
+
|
47
|
+
Require this either in your Gemfile or in RSpec's support scripts. So either:
|
48
|
+
|
49
|
+
# Gemfile
|
50
|
+
group :test do
|
51
|
+
gem "rspec"
|
52
|
+
gem "redis-file", :require => "redis-file/rspec"
|
53
|
+
end
|
54
|
+
|
55
|
+
Or:
|
56
|
+
|
57
|
+
# spec/support/redis-file.rb
|
58
|
+
require 'redis-file/rspec'
|
59
|
+
|
60
|
+
## Acknowledgements
|
61
|
+
|
62
|
+
* [dim](https://github.com/dim)
|
63
|
+
* [czarneckid](https://github.com/czarneckid)
|
64
|
+
* [obrie](https://github.com/obrie)
|
65
|
+
* [jredville](https://github.com/jredville)
|
66
|
+
* [redsquirrel](https://github.com/redsquirrel)
|
67
|
+
* [dpick](https://github.com/dpick)
|
68
|
+
* [caius](https://github.com/caius)
|
69
|
+
* [Travis-CI](http://travis-ci.org/)
|
70
|
+
|
71
|
+
|
72
|
+
## Contributing to redis-file
|
73
|
+
|
74
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
75
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
76
|
+
* Fork the project
|
77
|
+
* Start a feature/bugfix branch
|
78
|
+
* Commit and push until you are happy with your contribution
|
79
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
80
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
81
|
+
|
82
|
+
|
83
|
+
## Copyright
|
84
|
+
|
85
|
+
Copyleft 2013 Daniel Loureiro (based on work of Guillermo Iguaran). See LICENSE for
|
86
|
+
further details.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
$:.push File.expand_path("../lib", __FILE__)
|
5
|
+
require "redis-file/version"
|
6
|
+
|
7
|
+
Bundler::GemHelper.install_tasks
|
8
|
+
|
9
|
+
require 'rspec/core'
|
10
|
+
require 'rspec/core/rake_task'
|
11
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
12
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
13
|
+
spec.ruby_opts="-w"
|
14
|
+
end
|
15
|
+
|
16
|
+
task :default => :spec
|
17
|
+
|
18
|
+
require 'rdoc/task'
|
19
|
+
Rake::RDocTask.new do |rdoc|
|
20
|
+
version = RedisFile::VERSION
|
21
|
+
|
22
|
+
rdoc.rdoc_dir = 'rdoc'
|
23
|
+
rdoc.title = "redis-file #{version}"
|
24
|
+
rdoc.rdoc_files.include('README*')
|
25
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
26
|
+
end
|
27
|
+
|
data/lib/redis-file.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module RedisFile
|
2
|
+
# Represents a normal hash with some additional expiration information
|
3
|
+
# associated with each key
|
4
|
+
class ExpiringHash < Hash
|
5
|
+
attr_reader :expires
|
6
|
+
|
7
|
+
def initialize(*)
|
8
|
+
super
|
9
|
+
@expires = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](key)
|
13
|
+
key = normalize key
|
14
|
+
delete(key) if expired?(key)
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(key, val)
|
19
|
+
key = normalize key
|
20
|
+
expire(key)
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(key)
|
25
|
+
key = normalize key
|
26
|
+
expire(key)
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
def expire(key)
|
31
|
+
key = normalize key
|
32
|
+
expires.delete(key)
|
33
|
+
end
|
34
|
+
|
35
|
+
def expired?(key)
|
36
|
+
key = normalize key
|
37
|
+
expires.include?(key) && expires[key] < Time.now
|
38
|
+
end
|
39
|
+
|
40
|
+
def key?(key)
|
41
|
+
key = normalize key
|
42
|
+
delete(key) if expired?(key)
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
def values_at(*keys)
|
47
|
+
keys.each do |key|
|
48
|
+
key = normalize(key)
|
49
|
+
delete(key) if expired?(key)
|
50
|
+
end
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
def keys
|
55
|
+
super.select do |key|
|
56
|
+
key = normalize(key)
|
57
|
+
if expired?(key)
|
58
|
+
delete(key)
|
59
|
+
false
|
60
|
+
else
|
61
|
+
true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def normalize key
|
67
|
+
key.to_s
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Require this either in your Gemfile or in RSpec's
|
2
|
+
# support scripts. Examples:
|
3
|
+
#
|
4
|
+
# # Gemfile
|
5
|
+
# group :test do
|
6
|
+
# gem "rspec"
|
7
|
+
# gem "redis-file", :require => "redis-file/rspec"
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# # spec/support/redis-file.rb
|
11
|
+
# require 'redis-file/rspec'
|
12
|
+
#
|
13
|
+
|
14
|
+
require 'rspec/core'
|
15
|
+
require 'redis-file'
|
16
|
+
|
17
|
+
RSpec.configure do |c|
|
18
|
+
|
19
|
+
c.before do
|
20
|
+
Redis::Connection::File.reset_all_databases
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module RedisFile
|
2
|
+
# Takes in the variable length array of arguments for a zinterstore/zunionstore method
|
3
|
+
# and parses them into a few attributes for the method to access.
|
4
|
+
#
|
5
|
+
# Handles throwing errors for various scenarios (matches redis):
|
6
|
+
# * Custom weights specified, but not enough or too many given
|
7
|
+
# * Invalid aggregate value given
|
8
|
+
# * Multiple aggregate values given
|
9
|
+
class SortedSetArgumentHandler
|
10
|
+
# [Symbol] The aggregate method to use for the output values. One of %w(sum min max) expected
|
11
|
+
attr_reader :aggregate
|
12
|
+
# [Integer] Number of keys in the argument list
|
13
|
+
attr_accessor :number_of_keys
|
14
|
+
# [Array] The actual keys in the argument list
|
15
|
+
attr_accessor :keys
|
16
|
+
# [Array] integers for weighting the values of each key - one number per key expected
|
17
|
+
attr_accessor :weights
|
18
|
+
|
19
|
+
# Used internally
|
20
|
+
attr_accessor :type
|
21
|
+
|
22
|
+
# Expects all the argments for the method to be passed as an array
|
23
|
+
def initialize args
|
24
|
+
# Pull out known lengths of data
|
25
|
+
self.number_of_keys = args.shift
|
26
|
+
self.keys = args.shift(number_of_keys)
|
27
|
+
# Handle the variable lengths of data (WEIGHTS/AGGREGATE)
|
28
|
+
args.inject(self) {|handler, item| handler.handle(item) }
|
29
|
+
|
30
|
+
# Defaults for unspecified things
|
31
|
+
self.weights ||= Array.new(number_of_keys) { 1 }
|
32
|
+
self.aggregate ||= :sum
|
33
|
+
|
34
|
+
# Validate values
|
35
|
+
raise(Redis::CommandError, "ERR syntax error") unless weights.size == number_of_keys
|
36
|
+
raise(Redis::CommandError, "ERR syntax error") unless [:min, :max, :sum].include?(aggregate)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Only allows assigning a value *once* - raises Redis::CommandError if a second is given
|
40
|
+
def aggregate=(str)
|
41
|
+
raise(Redis::CommandError, "ERR syntax error") if (defined?(@aggregate) && @aggregate)
|
42
|
+
@aggregate = str.to_s.downcase.to_sym
|
43
|
+
end
|
44
|
+
|
45
|
+
# Decides how to handle an item, depending on where we are in the arguments
|
46
|
+
def handle(item)
|
47
|
+
case item
|
48
|
+
when "WEIGHTS"
|
49
|
+
self.type = :weights
|
50
|
+
self.weights = []
|
51
|
+
when "AGGREGATE"
|
52
|
+
self.type = :aggregate
|
53
|
+
when nil
|
54
|
+
# This should never be called, raise a syntax error if we manage to hit it
|
55
|
+
raise(Redis::CommandError, "ERR syntax error")
|
56
|
+
else
|
57
|
+
send "handle_#{type}", item
|
58
|
+
end
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def handle_weights(item)
|
63
|
+
self.weights << item
|
64
|
+
end
|
65
|
+
|
66
|
+
def handle_aggregate(item)
|
67
|
+
self.aggregate = item
|
68
|
+
end
|
69
|
+
|
70
|
+
def inject_block
|
71
|
+
lambda { |handler, item| handler.handle(item) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module RedisFile
|
2
|
+
class SortedSetStore
|
3
|
+
attr_accessor :data, :weights, :aggregate, :keys
|
4
|
+
|
5
|
+
def initialize params, data
|
6
|
+
self.data = data
|
7
|
+
self.weights = params.weights
|
8
|
+
self.aggregate = params.aggregate
|
9
|
+
self.keys = params.keys
|
10
|
+
end
|
11
|
+
|
12
|
+
def hashes
|
13
|
+
@hashes ||= keys.map do |src|
|
14
|
+
case data[src]
|
15
|
+
when ::Set
|
16
|
+
# Every value has a score of 1
|
17
|
+
Hash[data[src].map {|k,v| [k, 1]}]
|
18
|
+
when Hash
|
19
|
+
data[src]
|
20
|
+
else
|
21
|
+
{}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Apply the weightings to the hashes
|
27
|
+
def computed_values
|
28
|
+
unless defined?(@computed_values) && @computed_values
|
29
|
+
# Do nothing if all weights are 1, as n * 1 is n
|
30
|
+
@computed_values = hashes if weights.all? {|weight| weight == 1 }
|
31
|
+
# Otherwise, multiply the values in each hash by that hash's weighting
|
32
|
+
@computed_values ||= hashes.each_with_index.map do |hash, index|
|
33
|
+
weight = weights[index]
|
34
|
+
Hash[hash.map {|k, v| [k, (v * weight)]}]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
@computed_values
|
38
|
+
end
|
39
|
+
|
40
|
+
def aggregate_sum out
|
41
|
+
selected_keys.each do |key|
|
42
|
+
out[key] = computed_values.inject(0) do |n, hash|
|
43
|
+
n + (hash[key] || 0)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def aggregate_min out
|
49
|
+
selected_keys.each do |key|
|
50
|
+
out[key] = computed_values.map {|h| h[key] }.compact.min
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def aggregate_max out
|
55
|
+
selected_keys.each do |key|
|
56
|
+
out[key] = computed_values.map {|h| h[key] }.compact.max
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def selected_keys
|
61
|
+
raise NotImplemented, "subclass needs to implement #selected_keys"
|
62
|
+
end
|
63
|
+
|
64
|
+
def call
|
65
|
+
ZSet.new.tap {|out| send("aggregate_#{aggregate}", out) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class SortedSetIntersectStore < SortedSetStore
|
70
|
+
def selected_keys
|
71
|
+
@values ||= hashes.inject([]) { |r, h| r.empty? ? h.keys : (r & h.keys) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class SortedSetUnionStore < SortedSetStore
|
76
|
+
def selected_keys
|
77
|
+
@values ||= hashes.map(&:keys).flatten.uniq
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module RedisFile
|
2
|
+
class ZSet < Hash
|
3
|
+
|
4
|
+
def []=(key, val)
|
5
|
+
super(key, _floatify(val))
|
6
|
+
end
|
7
|
+
|
8
|
+
# Increments the value of key by val
|
9
|
+
def increment(key, val)
|
10
|
+
self[key] += _floatify(val)
|
11
|
+
end
|
12
|
+
|
13
|
+
def select_by_score min, max
|
14
|
+
min = _floatify(min)
|
15
|
+
max = _floatify(max)
|
16
|
+
reject {|_,v| v < min || v > max }
|
17
|
+
end
|
18
|
+
|
19
|
+
# Originally lifted from redis-rb
|
20
|
+
def _floatify(str)
|
21
|
+
if (( inf = str.to_s.match(/^([+-])?inf/i) ))
|
22
|
+
(inf[1] == "-" ? -1.0 : 1.0) / 0.0
|
23
|
+
else
|
24
|
+
Float str
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|