fakeredis 0.7.0 → 0.9.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/.github/workflows/codeql.yml +74 -0
- data/.github/workflows/ruby.yml +33 -0
- data/.gitignore +0 -1
- data/Gemfile +0 -6
- data/LICENSE +1 -1
- data/README.md +4 -3
- data/fakeredis.gemspec +2 -2
- data/lib/fakeredis/command_executor.rb +0 -6
- data/lib/fakeredis/expiring_hash.rb +2 -4
- data/lib/fakeredis/geo_commands.rb +142 -0
- data/lib/fakeredis/geo_set.rb +84 -0
- data/lib/fakeredis/minitest.rb +1 -1
- data/lib/fakeredis/sort_method.rb +0 -1
- data/lib/fakeredis/version.rb +1 -1
- data/lib/fakeredis/zset.rb +2 -2
- data/lib/fakeredis.rb +16 -0
- data/lib/redis/connection/memory.rb +261 -62
- data/spec/bitop_command_spec.rb +71 -71
- data/spec/fakeredis_spec.rb +52 -0
- data/spec/geo_set_spec.rb +164 -0
- data/spec/hashes_spec.rb +46 -5
- data/spec/hyper_log_logs_spec.rb +50 -0
- data/spec/keys_spec.rb +92 -34
- data/spec/lists_spec.rb +20 -3
- data/spec/memory_spec.rb +4 -8
- data/spec/server_spec.rb +18 -4
- data/spec/sets_spec.rb +23 -2
- data/spec/sorted_sets_spec.rb +180 -2
- data/spec/spec_helper.rb +5 -19
- data/spec/strings_spec.rb +42 -2
- data/spec/subscription_spec.rb +31 -31
- data/spec/support/shared_examples/bitwise_operation.rb +12 -12
- data/spec/transactions_spec.rb +14 -1
- metadata +20 -21
- data/.travis.yml +0 -22
- data/gemfiles/redisrb-master.gemfile +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 472bc70d16d42ddd98a542825b81fdd5d8f7af18a8b704f5022046895ca5a867
|
4
|
+
data.tar.gz: 7f0b48ebe4cfa88e99b9d949be3e0bd6816dbc3c0629062d42e0e1fce92a03fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcef2e419257364269044dbd0d8b21d0aacdaeb613d91e83abaef77a18a6c5551e72ded9e90ac334d17760aaf80e4c0fec9fd72f15e3a80efe63c7be5b3f2903
|
7
|
+
data.tar.gz: 6076a543910d3eec4a6fb8912ce1be5b44bea9fa71cbd76fd62f8b0a130eb97d85a4a2390d05ef5bd2a1eae699c487b01de537f9781c8aa39553c198f53c9618
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# For most projects, this workflow file will not need changing; you simply need
|
2
|
+
# to commit it to your repository.
|
3
|
+
#
|
4
|
+
# You may wish to alter this file to override the set of languages analyzed,
|
5
|
+
# or to provide custom queries or build logic.
|
6
|
+
#
|
7
|
+
# ******** NOTE ********
|
8
|
+
# We have attempted to detect the languages in your repository. Please check
|
9
|
+
# the `language` matrix defined below to confirm you have the correct set of
|
10
|
+
# supported CodeQL languages.
|
11
|
+
#
|
12
|
+
name: "CodeQL"
|
13
|
+
|
14
|
+
on:
|
15
|
+
push:
|
16
|
+
branches: [ "master" ]
|
17
|
+
pull_request:
|
18
|
+
# The branches below must be a subset of the branches above
|
19
|
+
branches: [ "master" ]
|
20
|
+
schedule:
|
21
|
+
- cron: '34 3 * * 3'
|
22
|
+
|
23
|
+
jobs:
|
24
|
+
analyze:
|
25
|
+
name: Analyze
|
26
|
+
runs-on: ubuntu-latest
|
27
|
+
permissions:
|
28
|
+
actions: read
|
29
|
+
contents: read
|
30
|
+
security-events: write
|
31
|
+
|
32
|
+
strategy:
|
33
|
+
fail-fast: false
|
34
|
+
matrix:
|
35
|
+
language: [ 'ruby' ]
|
36
|
+
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
37
|
+
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
38
|
+
|
39
|
+
steps:
|
40
|
+
- name: Checkout repository
|
41
|
+
uses: actions/checkout@v3
|
42
|
+
|
43
|
+
# Initializes the CodeQL tools for scanning.
|
44
|
+
- name: Initialize CodeQL
|
45
|
+
uses: github/codeql-action/init@v2
|
46
|
+
with:
|
47
|
+
languages: ${{ matrix.language }}
|
48
|
+
# If you wish to specify custom queries, you can do so here or in a config file.
|
49
|
+
# By default, queries listed here will override any specified in a config file.
|
50
|
+
# Prefix the list here with "+" to use these queries and those in the config file.
|
51
|
+
|
52
|
+
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
53
|
+
# queries: security-extended,security-and-quality
|
54
|
+
|
55
|
+
|
56
|
+
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
57
|
+
# If this step fails, then you should remove it and run the build manually (see below)
|
58
|
+
- name: Autobuild
|
59
|
+
uses: github/codeql-action/autobuild@v2
|
60
|
+
|
61
|
+
# ℹ️ Command-line programs to run using the OS shell.
|
62
|
+
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
63
|
+
|
64
|
+
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
65
|
+
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
66
|
+
|
67
|
+
# - run: |
|
68
|
+
# echo "Run, Build Application using script"
|
69
|
+
# ./location_of_script_within_repo/buildscript.sh
|
70
|
+
|
71
|
+
- name: Perform CodeQL Analysis
|
72
|
+
uses: github/codeql-action/analyze@v2
|
73
|
+
with:
|
74
|
+
category: "/language:${{matrix.language}}"
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
2
|
+
# They are provided by a third-party and are governed by
|
3
|
+
# separate terms of service, privacy policy, and support
|
4
|
+
# documentation.
|
5
|
+
|
6
|
+
# GitHub recommends pinning actions to a commit SHA.
|
7
|
+
# To get a newer version, you will need to update the SHA.
|
8
|
+
# You can also reference a tag or branch, but the action may change without warning.
|
9
|
+
|
10
|
+
name: Ruby CI
|
11
|
+
|
12
|
+
on:
|
13
|
+
push:
|
14
|
+
branches: [ master ]
|
15
|
+
pull_request:
|
16
|
+
branches: [ master ]
|
17
|
+
|
18
|
+
jobs:
|
19
|
+
test:
|
20
|
+
runs-on: ${{ matrix.os }}-latest
|
21
|
+
strategy:
|
22
|
+
fail-fast: false
|
23
|
+
matrix:
|
24
|
+
os: [ubuntu, macos]
|
25
|
+
ruby: [3.0, 3.1, 3.2, head, debug, jruby, jruby-head, truffleruby, truffleruby-head]
|
26
|
+
continue-on-error: ${{ endsWith(matrix.ruby, 'head') || matrix.ruby == 'debug' }}
|
27
|
+
steps:
|
28
|
+
- uses: actions/checkout@v3
|
29
|
+
- uses: ruby/setup-ruby@v1
|
30
|
+
with:
|
31
|
+
ruby-version: ${{ matrix.ruby }}
|
32
|
+
- run: bundle install
|
33
|
+
- run: bundle exec rake
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
# FakeRedis
|
1
|
+
# FakeRedis
|
2
|
+

|
3
|
+
|
2
4
|
This a fake implementation of redis-rb for machines without Redis or test environments
|
3
5
|
|
4
6
|
|
@@ -100,5 +102,4 @@ Thanks to [all contributors](https://github.com/guilleiguaran/fakeredis/graphs/c
|
|
100
102
|
|
101
103
|
## Copyright
|
102
104
|
|
103
|
-
Copyright (c) 2011-
|
104
|
-
further details.
|
105
|
+
Copyright (c) 2011-2023 Guillermo Iguaran. See LICENSE for 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>, ["
|
22
|
-
s.add_development_dependency(%q<rspec>, ["~> 3
|
21
|
+
s.add_runtime_dependency(%q<redis>, ["~> 4.8"])
|
22
|
+
s.add_development_dependency(%q<rspec>, ["~> 3"])
|
23
23
|
end
|
@@ -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
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module FakeRedis
|
2
|
+
class GeoSet
|
3
|
+
class Point
|
4
|
+
BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz" # (geohash-specific) Base32 map
|
5
|
+
EARTH_RADIUS_IN_M = 6_378_100.0
|
6
|
+
|
7
|
+
attr_reader :lon, :lat, :name
|
8
|
+
|
9
|
+
def initialize(lon, lat, name)
|
10
|
+
@lon = Float(lon)
|
11
|
+
@lat = Float(lat)
|
12
|
+
@name = name
|
13
|
+
end
|
14
|
+
|
15
|
+
def geohash(precision = 10)
|
16
|
+
latlon = [@lat, @lon]
|
17
|
+
ranges = [[-90.0, 90.0], [-180.0, 180.0]]
|
18
|
+
coordinate = 1
|
19
|
+
|
20
|
+
(0...precision).map do
|
21
|
+
index = 0 # index into base32 map
|
22
|
+
|
23
|
+
5.times do |bit|
|
24
|
+
mid = (ranges[coordinate][0] + ranges[coordinate][1]) / 2
|
25
|
+
if latlon[coordinate] >= mid
|
26
|
+
index = index * 2 + 1
|
27
|
+
ranges[coordinate][0] = mid
|
28
|
+
else
|
29
|
+
index *= 2
|
30
|
+
ranges[coordinate][1] = mid
|
31
|
+
end
|
32
|
+
|
33
|
+
coordinate ^= 1
|
34
|
+
end
|
35
|
+
|
36
|
+
BASE32[index]
|
37
|
+
end.join
|
38
|
+
end
|
39
|
+
|
40
|
+
def distance_to(other)
|
41
|
+
lat1 = deg_to_rad(@lat)
|
42
|
+
lon1 = deg_to_rad(@lon)
|
43
|
+
lat2 = deg_to_rad(other.lat)
|
44
|
+
lon2 = deg_to_rad(other.lon)
|
45
|
+
haversine_distance(lat1, lon1, lat2, lon2)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def deg_to_rad(deg)
|
51
|
+
deg * Math::PI / 180.0
|
52
|
+
end
|
53
|
+
|
54
|
+
def haversine_distance(lat1, lon1, lat2, lon2)
|
55
|
+
h = Math.sin((lat2 - lat1) / 2) ** 2 + Math.cos(lat1) * Math.cos(lat2) *
|
56
|
+
Math.sin((lon2 - lon1) / 2) ** 2
|
57
|
+
|
58
|
+
2 * EARTH_RADIUS_IN_M * Math.asin(Math.sqrt(h))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize
|
63
|
+
@points = {}
|
64
|
+
end
|
65
|
+
|
66
|
+
def size
|
67
|
+
@points.size
|
68
|
+
end
|
69
|
+
|
70
|
+
def add(lon, lat, name)
|
71
|
+
@points[name] = Point.new(lon, lat, name)
|
72
|
+
end
|
73
|
+
|
74
|
+
def get(name)
|
75
|
+
@points[name]
|
76
|
+
end
|
77
|
+
|
78
|
+
def points_within_radius(center, radius)
|
79
|
+
@points.values.select do |point|
|
80
|
+
point.distance_to(center) <= radius
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/fakeredis/minitest.rb
CHANGED
@@ -6,7 +6,6 @@ module FakeRedis
|
|
6
6
|
return [] if type(key) == 'none'
|
7
7
|
|
8
8
|
unless %w(list set zset).include? type(key)
|
9
|
-
warn "Operation against a key holding the wrong kind of value: Expected list, set or zset at #{key}."
|
10
9
|
raise Redis::CommandError.new("WRONGTYPE Operation against a key holding the wrong kind of value")
|
11
10
|
end
|
12
11
|
|
data/lib/fakeredis/version.rb
CHANGED
data/lib/fakeredis/zset.rb
CHANGED
@@ -17,7 +17,7 @@ module FakeRedis
|
|
17
17
|
def select_by_score min, max
|
18
18
|
min = _floatify(min, true)
|
19
19
|
max = _floatify(max, false)
|
20
|
-
|
20
|
+
select {|_,v| v >= min && v <= max }
|
21
21
|
end
|
22
22
|
|
23
23
|
private
|
@@ -26,7 +26,7 @@ module FakeRedis
|
|
26
26
|
def _floatify(str, increment = true)
|
27
27
|
if (( inf = str.to_s.match(/^([+-])?inf/i) ))
|
28
28
|
(inf[1] == "-" ? -1.0 : 1.0) / 0.0
|
29
|
-
elsif (( number = str.to_s.match(/^\((
|
29
|
+
elsif (( number = str.to_s.match(/^\((-?\d+)/i) ))
|
30
30
|
number[1].to_i + (increment ? 1 : -1)
|
31
31
|
else
|
32
32
|
Float str.to_s
|
data/lib/fakeredis.rb
CHANGED
@@ -15,4 +15,20 @@ module FakeRedis
|
|
15
15
|
def self.disable
|
16
16
|
Redis::Connection.drivers.delete_if {|driver| Redis::Connection::Memory == driver }
|
17
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
|
18
34
|
end
|