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.
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