fakeredis 0.5.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.travis.yml +14 -5
  4. data/LICENSE +1 -1
  5. data/README.md +42 -24
  6. data/fakeredis.gemspec +1 -1
  7. data/lib/fakeredis.rb +28 -0
  8. data/lib/fakeredis/bitop_command.rb +56 -0
  9. data/lib/fakeredis/command_executor.rb +6 -9
  10. data/lib/fakeredis/expiring_hash.rb +3 -5
  11. data/lib/fakeredis/geo_commands.rb +142 -0
  12. data/lib/fakeredis/geo_set.rb +84 -0
  13. data/lib/fakeredis/minitest.rb +24 -0
  14. data/lib/fakeredis/rspec.rb +1 -0
  15. data/lib/fakeredis/sort_method.rb +3 -3
  16. data/lib/fakeredis/sorted_set_store.rb +1 -1
  17. data/lib/fakeredis/transaction_commands.rb +2 -2
  18. data/lib/fakeredis/version.rb +1 -1
  19. data/lib/fakeredis/zset.rb +8 -2
  20. data/lib/redis/connection/memory.rb +650 -82
  21. data/spec/bitop_command_spec.rb +209 -0
  22. data/spec/command_executor_spec.rb +15 -0
  23. data/spec/compatibility_spec.rb +1 -1
  24. data/spec/connection_spec.rb +21 -21
  25. data/spec/fakeredis_spec.rb +73 -0
  26. data/spec/geo_set_spec.rb +164 -0
  27. data/spec/hashes_spec.rb +138 -57
  28. data/spec/hyper_log_logs_spec.rb +50 -0
  29. data/spec/keys_spec.rb +232 -90
  30. data/spec/lists_spec.rb +91 -35
  31. data/spec/memory_spec.rb +80 -7
  32. data/spec/server_spec.rb +38 -24
  33. data/spec/sets_spec.rb +112 -46
  34. data/spec/sort_method_spec.rb +6 -0
  35. data/spec/sorted_sets_spec.rb +482 -150
  36. data/spec/spec_helper.rb +9 -18
  37. data/spec/spec_helper_live_redis.rb +4 -4
  38. data/spec/strings_spec.rb +113 -79
  39. data/spec/subscription_spec.rb +107 -0
  40. data/spec/support/shared_examples/bitwise_operation.rb +59 -0
  41. data/spec/support/shared_examples/sortable.rb +20 -16
  42. data/spec/transactions_spec.rb +34 -13
  43. data/spec/upcase_method_name_spec.rb +2 -2
  44. metadata +23 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 20688b94f039d268518697ede7cdfad15f26cf73
4
- data.tar.gz: ab1982d7e9ed2f7c6b864d2beadd63d6041bb59a
2
+ SHA256:
3
+ metadata.gz: 7c0b77a04c1c761edb3a6544aca213f373d57bb485d17f9e7e0f2c63bfd7e660
4
+ data.tar.gz: 57975993a6664bc469ee297a6a36d75ac2e4e429972aee6e50a2f518f93ca580
5
5
  SHA512:
6
- metadata.gz: 080b44a262bae01a09dcd2660cb35e1a52543cad7d3780fc58224854b6007adb8a0598a0b892560988f7a86c921a1ea1d3b36fa19248542017ffb97ce8caa0ef
7
- data.tar.gz: 687896c96f26f389776216ef6397ce9bb025e9fb04008860dc377f8ba0965245185b02bb4381e7e560a5d76621b8c87ee4989878fbe0bd086e5fc30e2e3c6442
6
+ metadata.gz: ba35de9b7e297eca69cb7652d7817f257c447d7533d8c259c47215b15355ec49e5c2584907f0726f97327c443de122bd240cbca6fa194222f114bc99ffd0dbb9
7
+ data.tar.gz: 8c4e340be842d1fe966a3a4c6f63987bc8af346da74ced914cdc77488e08a6d15101ee6625097337287ec55c19f41118da4a79892b7fdd00bbebff1d103a813a
data/.gitignore CHANGED
@@ -4,3 +4,6 @@ Gemfile.lock
4
4
  pkg/*
5
5
  .rvmrc
6
6
  *.rbc
7
+ .ruby-version
8
+ .ruby-gemset
9
+ bin
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
- - 1.9.2
4
- - 1.9.3
5
- - 2.0.0
6
- - 2.1.1
7
- - jruby-19mode
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
@@ -1,4 +1,4 @@
1
- Copyright (c) 2011-2014 Guillermo Iguaran
1
+ Copyright (c) 2011-2018 Guillermo Iguaran
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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.x.y or later, if you are using
19
- redis-rb v2.2.x install the version 0.3.x:
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/ezmobius/redis-rb) documentation and
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
- # Gemfile
50
- group :test do
51
- gem "rspec"
52
- gem "fakeredis", :require => "fakeredis/rspec"
53
- end
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
- # spec/support/fakeredis.rb
58
- require 'fakeredis/rspec'
61
+ ```ruby
62
+ # spec/support/fakeredis.rb
63
+ require 'fakeredis/rspec'
64
+ ```
59
65
 
60
- ## Acknowledgements
66
+ ## Usage with Minitest
67
+
68
+ Require this either in your Gemfile or in Minitest's support scripts. So
69
+ either:
61
70
 
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/) (Travis-CI also uses Fakeredis in its tests!!!)
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-2014 Guillermo Iguaran. See LICENSE for
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>, ["~> 3.0"])
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.shift.to_s.downcase.to_sym
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, *command]
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, *command)
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] < Time.now
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.each do |key|
48
- key = normalize(key)
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