fakeredis 0.5.0 → 0.8.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 +5 -5
- data/.gitignore +3 -0
- data/.travis.yml +14 -5
- data/LICENSE +1 -1
- data/README.md +42 -24
- data/fakeredis.gemspec +1 -1
- data/lib/fakeredis.rb +28 -0
- data/lib/fakeredis/bitop_command.rb +56 -0
- data/lib/fakeredis/command_executor.rb +6 -9
- data/lib/fakeredis/expiring_hash.rb +3 -5
- data/lib/fakeredis/geo_commands.rb +142 -0
- data/lib/fakeredis/geo_set.rb +84 -0
- data/lib/fakeredis/minitest.rb +24 -0
- data/lib/fakeredis/rspec.rb +1 -0
- data/lib/fakeredis/sort_method.rb +3 -3
- data/lib/fakeredis/sorted_set_store.rb +1 -1
- data/lib/fakeredis/transaction_commands.rb +2 -2
- data/lib/fakeredis/version.rb +1 -1
- data/lib/fakeredis/zset.rb +8 -2
- data/lib/redis/connection/memory.rb +650 -82
- data/spec/bitop_command_spec.rb +209 -0
- data/spec/command_executor_spec.rb +15 -0
- data/spec/compatibility_spec.rb +1 -1
- data/spec/connection_spec.rb +21 -21
- data/spec/fakeredis_spec.rb +73 -0
- data/spec/geo_set_spec.rb +164 -0
- data/spec/hashes_spec.rb +138 -57
- data/spec/hyper_log_logs_spec.rb +50 -0
- data/spec/keys_spec.rb +232 -90
- data/spec/lists_spec.rb +91 -35
- data/spec/memory_spec.rb +80 -7
- data/spec/server_spec.rb +38 -24
- data/spec/sets_spec.rb +112 -46
- data/spec/sort_method_spec.rb +6 -0
- data/spec/sorted_sets_spec.rb +482 -150
- data/spec/spec_helper.rb +9 -18
- data/spec/spec_helper_live_redis.rb +4 -4
- data/spec/strings_spec.rb +113 -79
- data/spec/subscription_spec.rb +107 -0
- data/spec/support/shared_examples/bitwise_operation.rb +59 -0
- data/spec/support/shared_examples/sortable.rb +20 -16
- data/spec/transactions_spec.rb +34 -13
- data/spec/upcase_method_name_spec.rb +2 -2
- metadata +23 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7c0b77a04c1c761edb3a6544aca213f373d57bb485d17f9e7e0f2c63bfd7e660
|
4
|
+
data.tar.gz: 57975993a6664bc469ee297a6a36d75ac2e4e429972aee6e50a2f518f93ca580
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba35de9b7e297eca69cb7652d7817f257c447d7533d8c259c47215b15355ec49e5c2584907f0726f97327c443de122bd240cbca6fa194222f114bc99ffd0dbb9
|
7
|
+
data.tar.gz: 8c4e340be842d1fe966a3a4c6f63987bc8af346da74ced914cdc77488e08a6d15101ee6625097337287ec55c19f41118da4a79892b7fdd00bbebff1d103a813a
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,8 +1,17 @@
|
|
1
1
|
language: ruby
|
2
|
+
cache: bundler
|
3
|
+
# Use the faster container based infrastructure
|
4
|
+
# http://blog.travis-ci.com/2014-12-17-faster-builds-with-container-based-infrastructure/
|
5
|
+
sudo: false
|
6
|
+
|
2
7
|
rvm:
|
3
|
-
-
|
4
|
-
-
|
5
|
-
- 2.
|
6
|
-
-
|
7
|
-
- jruby
|
8
|
+
- 2.4
|
9
|
+
- 2.5
|
10
|
+
- 2.6
|
11
|
+
- ruby-head
|
12
|
+
- jruby
|
8
13
|
- rbx-2
|
14
|
+
|
15
|
+
matrix:
|
16
|
+
allow_failures:
|
17
|
+
- rvm: rbx-2
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -15,8 +15,8 @@ Add it to your Gemfile:
|
|
15
15
|
|
16
16
|
## Versions
|
17
17
|
|
18
|
-
FakeRedis currently supports redis-rb v3
|
19
|
-
redis-rb v2.2
|
18
|
+
FakeRedis currently supports redis-rb v3 or later, if you are using
|
19
|
+
redis-rb v2.2 install the version 0.3.x:
|
20
20
|
|
21
21
|
gem install fakeredis -v "~> 0.3.0"
|
22
22
|
|
@@ -29,45 +29,63 @@ or use the branch 0-3-x on your Gemfile:
|
|
29
29
|
|
30
30
|
You can use FakeRedis without any changes:
|
31
31
|
|
32
|
+
```
|
32
33
|
require "fakeredis"
|
33
|
-
|
34
|
+
|
34
35
|
redis = Redis.new
|
35
|
-
|
36
|
+
|
36
37
|
>> redis.set "foo", "bar"
|
37
38
|
=> "OK"
|
38
|
-
|
39
|
+
|
39
40
|
>> redis.get "foo"
|
40
41
|
=> "bar"
|
42
|
+
```
|
41
43
|
|
42
|
-
Read [redis-rb](https://github.com/
|
44
|
+
Read [redis-rb](https://github.com/redis/redis-rb) documentation and
|
43
45
|
[Redis](http://redis.io) homepage for more info about commands
|
44
46
|
|
45
47
|
## Usage with RSpec
|
46
48
|
|
47
|
-
Require this either in your Gemfile or in RSpec's support scripts. So either:
|
49
|
+
Require this either in your Gemfile or in RSpec's support scripts. So either:
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
51
|
+
```ruby
|
52
|
+
# Gemfile
|
53
|
+
group :test do
|
54
|
+
gem "rspec"
|
55
|
+
gem "fakeredis", :require => "fakeredis/rspec"
|
56
|
+
end
|
57
|
+
```
|
54
58
|
|
55
59
|
Or:
|
56
60
|
|
57
|
-
|
58
|
-
|
61
|
+
```ruby
|
62
|
+
# spec/support/fakeredis.rb
|
63
|
+
require 'fakeredis/rspec'
|
64
|
+
```
|
59
65
|
|
60
|
-
##
|
66
|
+
## Usage with Minitest
|
67
|
+
|
68
|
+
Require this either in your Gemfile or in Minitest's support scripts. So
|
69
|
+
either:
|
61
70
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
71
|
+
```ruby
|
72
|
+
# Gemfile
|
73
|
+
group :test do
|
74
|
+
gem "minitest"
|
75
|
+
gem "fakeredis", :require => "fakeredis/minitest"
|
76
|
+
end
|
77
|
+
```
|
78
|
+
|
79
|
+
Or:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
# test/test_helper.rb (or test/minitest_config.rb)
|
83
|
+
require 'fakeredis/minitest'
|
84
|
+
```
|
85
|
+
|
86
|
+
## Acknowledgements
|
70
87
|
|
88
|
+
Thanks to [all contributors](https://github.com/guilleiguaran/fakeredis/graphs/contributors), specially to [Caius Durling](https://github.com/caius) the most active one.
|
71
89
|
|
72
90
|
## Contributing to FakeRedis
|
73
91
|
|
@@ -82,5 +100,5 @@ Or:
|
|
82
100
|
|
83
101
|
## Copyright
|
84
102
|
|
85
|
-
Copyright (c) 2011-
|
103
|
+
Copyright (c) 2011-2018 Guillermo Iguaran. See LICENSE for
|
86
104
|
further details.
|
data/fakeredis.gemspec
CHANGED
@@ -18,6 +18,6 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
|
-
s.add_runtime_dependency(%q<redis>, ["~>
|
21
|
+
s.add_runtime_dependency(%q<redis>, ["~> 4.1"])
|
22
22
|
s.add_development_dependency(%q<rspec>, ["~> 3.0"])
|
23
23
|
end
|
data/lib/fakeredis.rb
CHANGED
@@ -3,4 +3,32 @@ require 'redis/connection/memory'
|
|
3
3
|
|
4
4
|
module FakeRedis
|
5
5
|
Redis = ::Redis
|
6
|
+
|
7
|
+
def self.enable
|
8
|
+
Redis::Connection.drivers << Redis::Connection::Memory unless enabled?
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.enabled?
|
12
|
+
Redis::Connection.drivers.last == Redis::Connection::Memory
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.disable
|
16
|
+
Redis::Connection.drivers.delete_if {|driver| Redis::Connection::Memory == driver }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.disabling
|
20
|
+
return yield unless enabled?
|
21
|
+
|
22
|
+
disable
|
23
|
+
yield
|
24
|
+
enable
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.enabling
|
28
|
+
return yield if enabled?
|
29
|
+
|
30
|
+
enable
|
31
|
+
yield
|
32
|
+
disable
|
33
|
+
end
|
6
34
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module FakeRedis
|
2
|
+
module BitopCommand
|
3
|
+
BIT_OPERATORS = {
|
4
|
+
'or' => :|,
|
5
|
+
'and' => :&,
|
6
|
+
'xor' => :'^',
|
7
|
+
'not' => :~,
|
8
|
+
}
|
9
|
+
|
10
|
+
def bitop(operation, destkey, *keys)
|
11
|
+
if result = apply(operator(operation), keys)
|
12
|
+
set(destkey, result)
|
13
|
+
result.length
|
14
|
+
else
|
15
|
+
0
|
16
|
+
end
|
17
|
+
rescue ArgumentError => _
|
18
|
+
raise_argument_error('bitop')
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def operator(operation)
|
24
|
+
BIT_OPERATORS[operation.to_s.downcase]
|
25
|
+
end
|
26
|
+
|
27
|
+
def apply(operator, keys)
|
28
|
+
case operator
|
29
|
+
when :~
|
30
|
+
raise ArgumentError if keys.count != 1
|
31
|
+
bitwise_not(keys.first)
|
32
|
+
when :&, :|, :'^'
|
33
|
+
raise ArgumentError if keys.empty?
|
34
|
+
bitwise_operation(operator, keys)
|
35
|
+
else
|
36
|
+
raise ArgumentError
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def bitwise_not(key)
|
41
|
+
if value = get(keys.first)
|
42
|
+
value.bytes.map { |byte| ~ byte }.pack('c*')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def bitwise_operation(operation, keys)
|
47
|
+
apply_onto, *values = keys.map { |key| get(key) }.reject(&:nil?)
|
48
|
+
values.reduce(apply_onto) do |memo, value|
|
49
|
+
shorter, longer = [memo, value].sort_by(&:length).map(&:bytes).map(&:to_a)
|
50
|
+
longer.each_with_index.map do |byte, index|
|
51
|
+
byte.send(operation, shorter[index] || 0)
|
52
|
+
end.pack('c*')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,23 +1,20 @@
|
|
1
1
|
module FakeRedis
|
2
2
|
module CommandExecutor
|
3
3
|
def write(command)
|
4
|
-
meffod = command.
|
4
|
+
meffod = command[0].to_s.downcase.to_sym
|
5
|
+
args = command[1..-1]
|
5
6
|
|
6
7
|
if in_multi && !(TRANSACTION_COMMANDS.include? meffod) # queue commands
|
7
|
-
queued_commands << [meffod, *
|
8
|
+
queued_commands << [meffod, *args]
|
8
9
|
reply = 'QUEUED'
|
10
|
+
elsif respond_to?(meffod) && method(meffod).arity.zero?
|
11
|
+
reply = send(meffod)
|
9
12
|
elsif respond_to?(meffod)
|
10
|
-
reply = send(meffod, *
|
13
|
+
reply = send(meffod, *args)
|
11
14
|
else
|
12
15
|
raise Redis::CommandError, "ERR unknown command '#{meffod}'"
|
13
16
|
end
|
14
17
|
|
15
|
-
if reply == true
|
16
|
-
reply = 1
|
17
|
-
elsif reply == false
|
18
|
-
reply = 0
|
19
|
-
end
|
20
|
-
|
21
18
|
replies << reply
|
22
19
|
nil
|
23
20
|
end
|
@@ -34,7 +34,7 @@ module FakeRedis
|
|
34
34
|
|
35
35
|
def expired?(key)
|
36
36
|
key = normalize key
|
37
|
-
expires.include?(key) && expires[key]
|
37
|
+
expires.include?(key) && expires[key] <= Time.now
|
38
38
|
end
|
39
39
|
|
40
40
|
def key?(key)
|
@@ -44,10 +44,8 @@ module FakeRedis
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def values_at(*keys)
|
47
|
-
keys.
|
48
|
-
|
49
|
-
delete(key) if expired?(key)
|
50
|
-
end
|
47
|
+
keys = keys.map { |key| normalize(key) }
|
48
|
+
keys.each { |key| delete(key) if expired?(key) }
|
51
49
|
super
|
52
50
|
end
|
53
51
|
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require "fakeredis/geo_set"
|
2
|
+
|
3
|
+
module FakeRedis
|
4
|
+
module GeoCommands
|
5
|
+
DISTANCE_UNITS = {
|
6
|
+
"m" => 1,
|
7
|
+
"km" => 1000,
|
8
|
+
"ft" => 0.3048,
|
9
|
+
"mi" => 1609.34
|
10
|
+
}
|
11
|
+
|
12
|
+
REDIS_DOUBLE_PRECISION = 4
|
13
|
+
REDIS_GEOHASH_SIZE = 10
|
14
|
+
|
15
|
+
def geoadd(key, *members)
|
16
|
+
raise_argument_error("geoadd") if members.empty? || members.size % 3 != 0
|
17
|
+
|
18
|
+
set = (data[key] ||= GeoSet.new)
|
19
|
+
prev_size = set.size
|
20
|
+
members.each_slice(3) do |member|
|
21
|
+
set.add(*member)
|
22
|
+
end
|
23
|
+
set.size - prev_size
|
24
|
+
end
|
25
|
+
|
26
|
+
def geodist(key, member1, member2, unit = "m")
|
27
|
+
unit = unit.to_s
|
28
|
+
raise_command_error("ERR unsupported unit provided. please use #{DISTANCE_UNITS.keys.join(', ')}") unless DISTANCE_UNITS.include?(unit)
|
29
|
+
|
30
|
+
set = (data[key] || GeoSet.new)
|
31
|
+
point1 = set.get(member1)
|
32
|
+
point2 = set.get(member2)
|
33
|
+
if point1 && point2
|
34
|
+
distance = point1.distance_to(point2)
|
35
|
+
distance_in_units = distance / DISTANCE_UNITS[unit]
|
36
|
+
distance_in_units.round(REDIS_DOUBLE_PRECISION).to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def geohash(key, member)
|
41
|
+
members = Array(member)
|
42
|
+
raise_argument_error("geohash") if members.empty?
|
43
|
+
set = (data[key] || GeoSet.new)
|
44
|
+
members.map do |member|
|
45
|
+
point = set.get(member)
|
46
|
+
point.geohash(REDIS_GEOHASH_SIZE) if point
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def geopos(key, member)
|
51
|
+
return nil unless data[key]
|
52
|
+
|
53
|
+
members = Array(member)
|
54
|
+
set = (data[key] || GeoSet.new)
|
55
|
+
members.map do |member|
|
56
|
+
point = set.get(member)
|
57
|
+
[point.lon.to_s, point.lat.to_s] if point
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def georadius(*args)
|
62
|
+
args = args.dup
|
63
|
+
raise_argument_error("georadius") if args.size < 5
|
64
|
+
key, lon, lat, radius, unit, *rest = args
|
65
|
+
raise_argument_error("georadius") unless DISTANCE_UNITS.has_key?(unit)
|
66
|
+
radius *= DISTANCE_UNITS[unit]
|
67
|
+
|
68
|
+
set = (data[key] || GeoSet.new)
|
69
|
+
center = GeoSet::Point.new(lon, lat, nil)
|
70
|
+
|
71
|
+
do_georadius(set, center, radius, unit, rest)
|
72
|
+
end
|
73
|
+
|
74
|
+
def georadiusbymember(*args)
|
75
|
+
args = args.dup
|
76
|
+
raise_argument_error("georadiusbymember") if args.size < 4
|
77
|
+
key, member, radius, unit, *rest = args
|
78
|
+
raise_argument_error("georadiusbymember") unless DISTANCE_UNITS.has_key?(unit)
|
79
|
+
radius *= DISTANCE_UNITS[unit]
|
80
|
+
|
81
|
+
set = (data[key] || GeoSet.new)
|
82
|
+
center = set.get(member)
|
83
|
+
raise_command_error("ERR could not decode requested zset member") unless center
|
84
|
+
|
85
|
+
do_georadius(set, center, radius, unit, args)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def do_georadius(set, center, radius, unit, args)
|
91
|
+
points = set.points_within_radius(center, radius)
|
92
|
+
|
93
|
+
options = georadius_options(args)
|
94
|
+
|
95
|
+
if options[:asc]
|
96
|
+
points.sort_by! { |p| p.distance_to(center) }
|
97
|
+
elsif options[:desc]
|
98
|
+
points.sort_by! { |p| -p.distance_to(center) }
|
99
|
+
end
|
100
|
+
|
101
|
+
points = points.take(options[:count]) if options[:count]
|
102
|
+
extras = options[:extras]
|
103
|
+
return points.map(&:name) if extras.empty?
|
104
|
+
|
105
|
+
points.map do |point|
|
106
|
+
member = [point.name]
|
107
|
+
|
108
|
+
extras.each do |extra|
|
109
|
+
case extra
|
110
|
+
when "WITHCOORD"
|
111
|
+
member << [point.lon.to_s, point.lat.to_s]
|
112
|
+
when "WITHDIST"
|
113
|
+
distance = point.distance_to(center)
|
114
|
+
distance_in_units = distance / DISTANCE_UNITS[unit]
|
115
|
+
member << distance_in_units.round(REDIS_DOUBLE_PRECISION).to_s
|
116
|
+
when "WITHHASH"
|
117
|
+
member << point.geohash(REDIS_GEOHASH_SIZE)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
member
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def georadius_options(args)
|
126
|
+
options = {}
|
127
|
+
args = args.map { |arg| arg.to_s.upcase }
|
128
|
+
|
129
|
+
if idx = args.index("COUNT")
|
130
|
+
options[:count] = Integer(args[idx + 1])
|
131
|
+
end
|
132
|
+
|
133
|
+
options[:asc] = true if args.include?("ASC")
|
134
|
+
options[:desc] = true if args.include?("DESC")
|
135
|
+
|
136
|
+
extras = args & ["WITHCOORD", "WITHDIST", "WITHHASH"]
|
137
|
+
options[:extras] = extras
|
138
|
+
|
139
|
+
options
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|