fakeredis 0.7.0 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
![Build Status](https://github.com/guilleiguaran/fakeredis/actions/workflows/ruby.yml/badge.svg)
|
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
|