redis-prescription 1.0.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +2 -1
- data/{README.md → README.adoc} +44 -42
- data/lib/redis-prescription.rb +3 -0
- data/lib/redis_prescription/adapters/redis.rb +39 -0
- data/lib/redis_prescription/adapters/redis_client.rb +42 -0
- data/lib/redis_prescription/adapters/redis_namespace.rb +39 -0
- data/lib/redis_prescription/adapters.rb +20 -0
- data/lib/redis_prescription/errors.rb +91 -0
- data/lib/redis_prescription/version.rb +6 -0
- data/lib/redis_prescription.rb +61 -0
- metadata +28 -41
- data/.gitignore +0 -10
- data/.rspec +0 -1
- data/.rubocop.yml +0 -40
- data/.travis.yml +0 -28
- data/.yardopts +0 -2
- data/Gemfile +0 -31
- data/Guardfile +0 -21
- data/Rakefile +0 -15
- data/lib/redis/prescription/version.rb +0 -8
- data/lib/redis/prescription.rb +0 -90
- data/redis-prescription.gemspec +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c503c680688ac288e5d3b9e59314c20055c10239b31733de6855f6a78ed90d3e
|
4
|
+
data.tar.gz: f0a2373d51deb922ac02bb8283e05cb583dc3578f95ea8f4923bba2d1a3058e1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 516e413b8da8d7dace049f9169c307d3a87b1ee52ecdc759368f3012d2d51e6b44732d38a2980635bc5acb1015436960c48fe17239edd734101feda83d18c503
|
7
|
+
data.tar.gz: 2c07386fbc7fb53a0a8093df08a35ed6d366a37084df8823cc49685cf95034e5599f7aeacb2fd502ebef6d4f0654f244988e86bd7bd26bfd30c3f0f7741d079b
|
data/LICENSE.txt
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c)
|
3
|
+
Copyright (c) 2020-2023 Alexey Zapparov
|
4
|
+
Copyright (c) 2018 SensorTower Inc.
|
4
5
|
|
5
6
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
7
|
of this software and associated documentation files (the "Software"), to deal
|
data/{README.md → README.adoc}
RENAMED
@@ -1,48 +1,56 @@
|
|
1
|
-
|
1
|
+
= Redis::Prescription
|
2
2
|
|
3
3
|
Redis LUA stored procedure runner. Preloads (and reloads when needed, e.g. when
|
4
4
|
scripts were flushed away) script and then runs it with `EVALSHA`.
|
5
5
|
|
6
6
|
|
7
|
-
|
7
|
+
== Installation
|
8
8
|
|
9
9
|
Add this line to your application's Gemfile:
|
10
10
|
|
11
|
-
|
12
|
-
gem "redis-prescription"
|
13
|
-
```
|
14
|
-
|
15
|
-
And then execute:
|
16
|
-
|
17
|
-
$ bundle
|
11
|
+
$ bundle add redis-prescription
|
18
12
|
|
19
13
|
Or install it yourself as:
|
20
14
|
|
21
15
|
$ gem install redis-prescription
|
22
16
|
|
23
17
|
|
24
|
-
|
18
|
+
== Usage
|
25
19
|
|
26
|
-
|
27
|
-
|
20
|
+
[source,ruby]
|
21
|
+
----
|
22
|
+
script = RedisPrescription.new <<~LUA
|
28
23
|
return tonumber(redis.call('GET', KEYS[1]) or 42)
|
29
24
|
LUA
|
30
25
|
|
31
|
-
|
26
|
+
redis = Redis.new
|
27
|
+
script.call(redis, keys: [:xxx]) # => 42
|
32
28
|
|
33
|
-
|
34
|
-
script.
|
35
|
-
|
29
|
+
redis.set(:xxx, 123)
|
30
|
+
script.call(redis, keys: [:xxx]) # => 123
|
31
|
+
----
|
36
32
|
|
37
33
|
|
38
|
-
|
34
|
+
== Supported Ruby Versions
|
39
35
|
|
40
|
-
This library aims to support and is
|
41
|
-
versions:
|
36
|
+
This library aims to support and is tested against:
|
42
37
|
|
43
|
-
*
|
44
|
-
|
45
|
-
|
38
|
+
* https://www.ruby-lang.org[Ruby]
|
39
|
+
** MRI 3.0.x
|
40
|
+
** MRI 3.1.x
|
41
|
+
* https://redis.io[Redis Server]
|
42
|
+
** 6.2.x
|
43
|
+
** 7.0.x
|
44
|
+
* https://github.com/redis/redis-rb[redis-rb]
|
45
|
+
** 4.7.x
|
46
|
+
** 4.8.x
|
47
|
+
** 5.0.x
|
48
|
+
* https://github.com/resque/redis-namespace[redis-namespace]
|
49
|
+
** 1.10.x
|
50
|
+
* https://github.com/redis-rb/redis-client[redis-client]
|
51
|
+
** 0.12.x
|
52
|
+
** 0.13.x
|
53
|
+
** 0.14.x
|
46
54
|
|
47
55
|
If something doesn't work on one of these versions, it's a bug.
|
48
56
|
|
@@ -57,33 +65,27 @@ patches in a timely fashion. If critical issues for a particular implementation
|
|
57
65
|
exist at the time of a major release, support for that Ruby version may be
|
58
66
|
dropped.
|
59
67
|
|
68
|
+
The same applies to *Redis Server*, *redis-rb*, *redis-namespace*,
|
69
|
+
and *redis-client* support.
|
70
|
+
|
71
|
+
|
72
|
+
== Similar Projects
|
73
|
+
|
74
|
+
* https://github.com/Shopify/wolverine
|
60
75
|
|
61
|
-
## Development
|
62
76
|
|
63
|
-
|
64
|
-
Then, run `bundle exec rake spec` to run the tests with ruby-rb client.
|
77
|
+
== Development
|
65
78
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
push git commits and tags, and push the `.gem` file to [rubygems.org][2].
|
79
|
+
scripts/update-gemfiles
|
80
|
+
scripts/run-rspec
|
81
|
+
bundle exec rubocop
|
70
82
|
|
71
83
|
|
72
|
-
|
84
|
+
== Contributing
|
73
85
|
|
74
|
-
* Fork
|
86
|
+
* Fork redis-prescription
|
75
87
|
* Make your changes
|
76
88
|
* Ensure all tests pass (`bundle exec rake`)
|
77
|
-
* Send a
|
89
|
+
* Send a merge request
|
78
90
|
* If we like them we'll merge them
|
79
91
|
* If we've accepted a patch, feel free to ask for commit access!
|
80
|
-
|
81
|
-
|
82
|
-
## Copyright
|
83
|
-
|
84
|
-
Copyright (c) 2018 SensorTower Inc.
|
85
|
-
See LICENSE.md for further details.
|
86
|
-
|
87
|
-
|
88
|
-
[1]: http://travis-ci.org/sensortower/redis-prescription
|
89
|
-
[2]: https://rubygems.org
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../errors"
|
4
|
+
|
5
|
+
class RedisPrescription
|
6
|
+
module Adapters
|
7
|
+
# redis-rb adapter
|
8
|
+
class Redis
|
9
|
+
def self.adapts?(redis)
|
10
|
+
defined?(::Redis) && redis.is_a?(::Redis)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(redis)
|
14
|
+
@redis = redis
|
15
|
+
end
|
16
|
+
|
17
|
+
def adapter_name
|
18
|
+
"redis"
|
19
|
+
end
|
20
|
+
|
21
|
+
def eval(script, keys, argv)
|
22
|
+
@redis.eval(script, keys, argv)
|
23
|
+
rescue ::Redis::CommandError => e
|
24
|
+
raise CommandError, e.message
|
25
|
+
end
|
26
|
+
|
27
|
+
def evalsha(digest, keys, argv)
|
28
|
+
@redis.evalsha(digest, keys, argv)
|
29
|
+
rescue ::Redis::CommandError => e
|
30
|
+
raise CommandError, e.message
|
31
|
+
end
|
32
|
+
|
33
|
+
def purge!
|
34
|
+
@redis.script("flush")
|
35
|
+
@redis.flushdb
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../errors"
|
4
|
+
|
5
|
+
class RedisPrescription
|
6
|
+
module Adapters
|
7
|
+
# redis-client adapter
|
8
|
+
class RedisClient
|
9
|
+
def self.adapts?(redis)
|
10
|
+
return true if defined?(::RedisClient) && redis.is_a?(::RedisClient)
|
11
|
+
return true if defined?(::RedisClient::Decorator::Client) && redis.is_a?(::RedisClient::Decorator::Client)
|
12
|
+
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(redis)
|
17
|
+
@redis = redis
|
18
|
+
end
|
19
|
+
|
20
|
+
def adapter_name
|
21
|
+
"redis-client"
|
22
|
+
end
|
23
|
+
|
24
|
+
def eval(script, keys, argv)
|
25
|
+
@redis.call("EVAL", script, keys.size, *keys, *argv)
|
26
|
+
rescue ::RedisClient::CommandError => e
|
27
|
+
raise CommandError, e.message
|
28
|
+
end
|
29
|
+
|
30
|
+
def evalsha(digest, keys, argv)
|
31
|
+
@redis.call("EVALSHA", digest, keys.size, *keys, *argv)
|
32
|
+
rescue ::RedisClient::CommandError => e
|
33
|
+
raise CommandError, e.message
|
34
|
+
end
|
35
|
+
|
36
|
+
def purge!
|
37
|
+
@redis.call("SCRIPT", "FLUSH")
|
38
|
+
@redis.call("FLUSHDB")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../errors"
|
4
|
+
|
5
|
+
class RedisPrescription
|
6
|
+
module Adapters
|
7
|
+
# redis-namespace adapter
|
8
|
+
class RedisNamespace
|
9
|
+
def self.adapts?(redis)
|
10
|
+
defined?(::Redis::Namespace) && redis.is_a?(::Redis::Namespace)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(redis)
|
14
|
+
@redis = redis
|
15
|
+
end
|
16
|
+
|
17
|
+
def adapter_name
|
18
|
+
"redis-namespace"
|
19
|
+
end
|
20
|
+
|
21
|
+
def eval(script, keys, argv)
|
22
|
+
@redis.eval(script, keys, argv)
|
23
|
+
rescue ::Redis::CommandError => e
|
24
|
+
raise CommandError, e.message
|
25
|
+
end
|
26
|
+
|
27
|
+
def evalsha(digest, keys, argv)
|
28
|
+
@redis.evalsha(digest, keys, argv)
|
29
|
+
rescue ::Redis::CommandError => e
|
30
|
+
raise CommandError, e.message
|
31
|
+
end
|
32
|
+
|
33
|
+
def purge!
|
34
|
+
@redis.redis.script("flush")
|
35
|
+
@redis.redis.flushdb
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./adapters/redis"
|
4
|
+
require_relative "./adapters/redis_client"
|
5
|
+
require_relative "./adapters/redis_namespace"
|
6
|
+
|
7
|
+
class RedisPrescription
|
8
|
+
# @api internal
|
9
|
+
module Adapters
|
10
|
+
class << self
|
11
|
+
def [](redis)
|
12
|
+
return Adapters::Redis.new(redis) if Adapters::Redis.adapts?(redis)
|
13
|
+
return Adapters::RedisClient.new(redis) if Adapters::RedisClient.adapts?(redis)
|
14
|
+
return Adapters::RedisNamespace.new(redis) if Adapters::RedisNamespace.adapts?(redis)
|
15
|
+
|
16
|
+
raise TypeError, "Unsupported redis client: #{redis.class}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class RedisPrescription
|
4
|
+
# Top-level error class for all RedisPrescription errors
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
# Redis command error wrapper, `#cause` will be `Redis::CommandError` or
|
8
|
+
# `RedisClient::CommandError`.
|
9
|
+
class CommandError < Error; end
|
10
|
+
|
11
|
+
# Lua script eval/evalsha failure
|
12
|
+
class ScriptError < Error
|
13
|
+
# rubocop:disable Layout/LineLength
|
14
|
+
LUA_ERROR_MESSAGE = %r{
|
15
|
+
# * Error compiling script:
|
16
|
+
# ** Redis 6.0, 6.2, 7.0
|
17
|
+
# *** ERR Error compiling script (new function): user_script:7: unexpected symbol near '!'
|
18
|
+
# * Error running script:
|
19
|
+
# ** Redis 6.0, 6.2
|
20
|
+
# *** ERR Error running script (call to f_64203334c42d5690c2d008a78aa7789f5b83e5bb): @user_script:4: user_script:4: attempt to perform arithmetic on a string value
|
21
|
+
# ** Redis 7.0
|
22
|
+
# *** ERR user_script:4: attempt to perform arithmetic on a string value script: 64203334c42d5690c2d008a78aa7789f5b83e5bb, on @user_script:4.
|
23
|
+
\A
|
24
|
+
ERR\s
|
25
|
+
(?:
|
26
|
+
Error\scompiling\sscript\s\([^)]+\):\s # Redis 6.0, 6.2, 7.0
|
27
|
+
.+:(?<loc>\d+):\s # Redis 6.0, 6.2, 7.0
|
28
|
+
(?<message>.+) # Redis 6.0, 6.2, 7.0
|
29
|
+
|
|
30
|
+
(?:Error\srunning\sscript\s\([^)]+\):\s@\S+\s)? # Redis 6.0, 6.2
|
31
|
+
.+:(?<loc>\d+):\s # Redis 6.0, 6.2, 7.0
|
32
|
+
(?<message>.+?) # Redis 6.0, 6.2, 7.0
|
33
|
+
(?::\s\h+,\son\s@[^:]+:\d+\.)? # Redis 7.0
|
34
|
+
)
|
35
|
+
\z
|
36
|
+
}x
|
37
|
+
private_constant :LUA_ERROR_MESSAGE
|
38
|
+
# rubocop:enable Layout/LineLength
|
39
|
+
|
40
|
+
# Lua script source
|
41
|
+
#
|
42
|
+
# @return [String]
|
43
|
+
attr_reader :source
|
44
|
+
|
45
|
+
# Line of code where error was encountered
|
46
|
+
#
|
47
|
+
# @return [Integer?]
|
48
|
+
attr_reader :loc
|
49
|
+
|
50
|
+
# @param message [String]
|
51
|
+
# @param source [#to_s]
|
52
|
+
def initialize(message, source)
|
53
|
+
@source = -source.to_s
|
54
|
+
|
55
|
+
if (parsed = LUA_ERROR_MESSAGE.match(message))
|
56
|
+
@loc = parsed[:loc].to_i
|
57
|
+
message = [parsed[:message], excerpt(@source, @loc)].compact.join("\n\n")
|
58
|
+
end
|
59
|
+
|
60
|
+
super(message)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def excerpt(source, loc)
|
66
|
+
lines = excerpt_lines(source, loc)
|
67
|
+
gutter = lines.map(&:first).max.to_s.length
|
68
|
+
|
69
|
+
lines.map! do |(pos, line)|
|
70
|
+
format(pos == loc ? "\t%#{gutter}d > %s" : "\t%#{gutter}d | %s", pos, line).rstrip
|
71
|
+
end
|
72
|
+
|
73
|
+
lines.join("\n")
|
74
|
+
rescue => e
|
75
|
+
warn "Failed extracting source excerpt: #{e.message}"
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def excerpt_lines(source, loc)
|
80
|
+
lines = source.lines
|
81
|
+
pos = loc - 1 # reported line of code is 1-offset
|
82
|
+
min = pos - 2 # 2 lines of head context
|
83
|
+
max = pos + 2 # 2 lines of tail context
|
84
|
+
|
85
|
+
min = 0 if min.negative?
|
86
|
+
max = lines.size - 1 if lines.size <= max
|
87
|
+
|
88
|
+
(min..max).map { |i| [i.succ, lines[i]] }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "./redis_prescription/adapters"
|
4
|
+
require_relative "./redis_prescription/errors"
|
5
|
+
require_relative "./redis_prescription/version"
|
6
|
+
|
7
|
+
# Lua script executor for redis.
|
8
|
+
#
|
9
|
+
# Instead of executing script with `EVAL` everytime - loads script once
|
10
|
+
# and then runs it with `EVALSHA`.
|
11
|
+
#
|
12
|
+
# @example Usage
|
13
|
+
#
|
14
|
+
# redis = Redis.new
|
15
|
+
# script = RedisPrescription.new("return ARGV[1] + ARGV[2]")
|
16
|
+
# script.call(redis, argv: [2, 2]) # => 4
|
17
|
+
class RedisPrescription
|
18
|
+
# Redis error fired when script ID is unkown.
|
19
|
+
NOSCRIPT = "NOSCRIPT"
|
20
|
+
private_constant :NOSCRIPT
|
21
|
+
|
22
|
+
EMPTY_LIST = [].freeze
|
23
|
+
private_constant :EMPTY_LIST
|
24
|
+
|
25
|
+
# Lua script source.
|
26
|
+
# @return [String]
|
27
|
+
attr_reader :source
|
28
|
+
|
29
|
+
# Lua script SHA1 digest.
|
30
|
+
# @return [String]
|
31
|
+
attr_reader :digest
|
32
|
+
|
33
|
+
# @param source [#to_s] Lua script
|
34
|
+
def initialize(source)
|
35
|
+
@source = -source.to_s
|
36
|
+
@digest = Digest::SHA1.hexdigest(@source).freeze
|
37
|
+
end
|
38
|
+
|
39
|
+
# Executes script and return result of execution.
|
40
|
+
# @param redis [Redis, RedisClient]
|
41
|
+
# @param keys [Array] keys to pass to the script
|
42
|
+
# @param argv [Array] arguments to pass to the script
|
43
|
+
# @raise [TypeError] if given redis client is not supported
|
44
|
+
# @raise [ScriptError] if script execution failed
|
45
|
+
# @return depends on the script
|
46
|
+
def call(redis, keys: EMPTY_LIST, argv: EMPTY_LIST)
|
47
|
+
evalsha_with_fallback(Adapters[redis], keys, argv)
|
48
|
+
rescue CommandError => e
|
49
|
+
raise ScriptError.new(e.message, @source)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def evalsha_with_fallback(redis, keys, argv)
|
55
|
+
redis.evalsha(@digest, keys, argv)
|
56
|
+
rescue CommandError => e
|
57
|
+
raise unless e.message.include?(NOSCRIPT)
|
58
|
+
|
59
|
+
redis.eval(@source, keys, argv)
|
60
|
+
end
|
61
|
+
end
|
metadata
CHANGED
@@ -1,56 +1,44 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-prescription
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexey Zapparov
|
8
|
-
autorequire:
|
9
|
-
bindir:
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: bundler
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '1.16'
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '1.16'
|
11
|
+
date: 2023-04-05 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
27
13
|
description: |
|
28
|
-
Preloads (and reloads when needed, e.g. when scripts
|
29
|
-
|
14
|
+
Preloads (and reloads when needed, e.g. when scripts were flushed away)
|
15
|
+
script and then runs it with `EVALSHA`.
|
30
16
|
email:
|
31
|
-
-
|
17
|
+
- alexey@zapparov.com
|
32
18
|
executables: []
|
33
19
|
extensions: []
|
34
20
|
extra_rdoc_files: []
|
35
21
|
files:
|
36
|
-
- ".gitignore"
|
37
|
-
- ".rspec"
|
38
|
-
- ".rubocop.yml"
|
39
|
-
- ".travis.yml"
|
40
|
-
- ".yardopts"
|
41
|
-
- Gemfile
|
42
|
-
- Guardfile
|
43
22
|
- LICENSE.txt
|
44
|
-
- README.
|
45
|
-
-
|
46
|
-
- lib/
|
47
|
-
- lib/
|
48
|
-
- redis
|
49
|
-
|
23
|
+
- README.adoc
|
24
|
+
- lib/redis-prescription.rb
|
25
|
+
- lib/redis_prescription.rb
|
26
|
+
- lib/redis_prescription/adapters.rb
|
27
|
+
- lib/redis_prescription/adapters/redis.rb
|
28
|
+
- lib/redis_prescription/adapters/redis_client.rb
|
29
|
+
- lib/redis_prescription/adapters/redis_namespace.rb
|
30
|
+
- lib/redis_prescription/errors.rb
|
31
|
+
- lib/redis_prescription/version.rb
|
32
|
+
homepage: https://gitlab.com/ixti/redis-prescription
|
50
33
|
licenses:
|
51
34
|
- MIT
|
52
|
-
metadata:
|
53
|
-
|
35
|
+
metadata:
|
36
|
+
homepage_uri: https://gitlab.com/ixti/redis-prescription
|
37
|
+
source_code_uri: https://gitlab.com/ixti/redis-prescription
|
38
|
+
bug_tracker_uri: https://gitlab.com/ixti/redis-prescription/issues
|
39
|
+
changelog_uri: https://gitlab.com/ixti/redis-prescription/blob/v2.2.0/CHANGES.md
|
40
|
+
rubygems_mfa_required: 'true'
|
41
|
+
post_install_message:
|
54
42
|
rdoc_options: []
|
55
43
|
require_paths:
|
56
44
|
- lib
|
@@ -58,16 +46,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
46
|
requirements:
|
59
47
|
- - ">="
|
60
48
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
49
|
+
version: '3.0'
|
62
50
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
51
|
requirements:
|
64
52
|
- - ">="
|
65
53
|
- !ruby/object:Gem::Version
|
66
54
|
version: '0'
|
67
55
|
requirements: []
|
68
|
-
|
69
|
-
|
70
|
-
signing_key:
|
56
|
+
rubygems_version: 3.3.26
|
57
|
+
signing_key:
|
71
58
|
specification_version: 4
|
72
59
|
summary: Redis LUA stored procedure runner.
|
73
60
|
test_files: []
|
data/.gitignore
DELETED
data/.rspec
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
--require spec_helper
|
data/.rubocop.yml
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
AllCops:
|
2
|
-
TargetRubyVersion: 2.3
|
3
|
-
DisplayCopNames: true
|
4
|
-
|
5
|
-
|
6
|
-
## Layout ######################################################################
|
7
|
-
|
8
|
-
Layout/DotPosition:
|
9
|
-
EnforcedStyle: trailing
|
10
|
-
|
11
|
-
Layout/IndentArray:
|
12
|
-
EnforcedStyle: consistent
|
13
|
-
|
14
|
-
|
15
|
-
## Metrics #####################################################################
|
16
|
-
|
17
|
-
Metrics/BlockLength:
|
18
|
-
Exclude:
|
19
|
-
- "spec/**/*"
|
20
|
-
|
21
|
-
|
22
|
-
## Style #######################################################################
|
23
|
-
|
24
|
-
Style/HashSyntax:
|
25
|
-
EnforcedStyle: hash_rockets
|
26
|
-
|
27
|
-
Style/RegexpLiteral:
|
28
|
-
EnforcedStyle: percent_r
|
29
|
-
|
30
|
-
Style/RescueStandardError:
|
31
|
-
EnforcedStyle: implicit
|
32
|
-
|
33
|
-
Style/SafeNavigation:
|
34
|
-
Enabled: false
|
35
|
-
|
36
|
-
Style/StringLiterals:
|
37
|
-
EnforcedStyle: double_quotes
|
38
|
-
|
39
|
-
Style/YodaCondition:
|
40
|
-
Enabled: false
|
data/.travis.yml
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
sudo: false
|
3
|
-
|
4
|
-
services:
|
5
|
-
- redis-server
|
6
|
-
|
7
|
-
cache: bundler
|
8
|
-
|
9
|
-
rvm:
|
10
|
-
- 2.3
|
11
|
-
- 2.4
|
12
|
-
- 2.5
|
13
|
-
|
14
|
-
matrix:
|
15
|
-
fast_finish: true
|
16
|
-
include:
|
17
|
-
- rvm: 2.4
|
18
|
-
env: TEST_SUITE="rubocop"
|
19
|
-
|
20
|
-
before_install:
|
21
|
-
- gem update --system
|
22
|
-
- gem --version
|
23
|
-
- gem install bundler --no-rdoc --no-ri
|
24
|
-
- bundle --version
|
25
|
-
|
26
|
-
install: bundle install --without development doc
|
27
|
-
|
28
|
-
script: bundle exec rake $TEST_SUITE
|
data/.yardopts
DELETED
data/Gemfile
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
source "https://rubygems.org"
|
4
|
-
ruby RUBY_VERSION
|
5
|
-
|
6
|
-
gem "rake"
|
7
|
-
gem "rspec"
|
8
|
-
gem "rubocop", "~> 0.52.0", :require => false
|
9
|
-
|
10
|
-
gem "redis"
|
11
|
-
gem "redis-namespace"
|
12
|
-
|
13
|
-
group :development do
|
14
|
-
gem "guard", :require => false
|
15
|
-
gem "guard-rspec", :require => false
|
16
|
-
gem "guard-rubocop", :require => false
|
17
|
-
gem "pry", :require => false
|
18
|
-
end
|
19
|
-
|
20
|
-
group :test do
|
21
|
-
gem "codecov", :require => false
|
22
|
-
gem "simplecov", :require => false
|
23
|
-
end
|
24
|
-
|
25
|
-
group :doc do
|
26
|
-
gem "redcarpet"
|
27
|
-
gem "yard"
|
28
|
-
end
|
29
|
-
|
30
|
-
# Specify your gem's dependencies in redis-prescription.gemspec
|
31
|
-
gemspec
|
data/Guardfile
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
guard :rspec, :cmd => "bundle exec rspec" do
|
4
|
-
require "guard/rspec/dsl"
|
5
|
-
dsl = Guard::RSpec::Dsl.new(self)
|
6
|
-
|
7
|
-
# RSpec files
|
8
|
-
rspec = dsl.rspec
|
9
|
-
watch(rspec.spec_helper) { rspec.spec_dir }
|
10
|
-
watch(rspec.spec_support) { rspec.spec_dir }
|
11
|
-
watch(rspec.spec_files)
|
12
|
-
|
13
|
-
# Ruby files
|
14
|
-
ruby = dsl.ruby
|
15
|
-
dsl.watch_spec_files_for(ruby.lib_files)
|
16
|
-
end
|
17
|
-
|
18
|
-
guard :rubocop do
|
19
|
-
watch(%r{.+\.rb$})
|
20
|
-
watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
|
21
|
-
end
|
data/Rakefile
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "bundler/gem_tasks"
|
4
|
-
|
5
|
-
require "rspec/core/rake_task"
|
6
|
-
RSpec::Core::RakeTask.new
|
7
|
-
|
8
|
-
require "rubocop/rake_task"
|
9
|
-
RuboCop::RakeTask.new
|
10
|
-
|
11
|
-
if ENV["CI"]
|
12
|
-
task :default => :spec
|
13
|
-
else
|
14
|
-
task :default => %i[rubocop spec]
|
15
|
-
end
|
data/lib/redis/prescription.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "prescription/version"
|
4
|
-
|
5
|
-
# @see https://github.com/redis/redis-rb
|
6
|
-
class Redis
|
7
|
-
# Lua script executor for redis.
|
8
|
-
#
|
9
|
-
# Instead of executing script with `EVAL` everytime - loads script once
|
10
|
-
# and then runs it with `EVALSHA`.
|
11
|
-
#
|
12
|
-
# @example Usage
|
13
|
-
#
|
14
|
-
# script = Redis::Prescription.new("return ARGV[1] + ARGV[2]")
|
15
|
-
# script.eval(Redis.current, :argv => [2, 2]) # => 2
|
16
|
-
class Prescription
|
17
|
-
# Script load command.
|
18
|
-
LOAD = "load"
|
19
|
-
private_constant :LOAD
|
20
|
-
|
21
|
-
# Redis error fired when script ID is unkown.
|
22
|
-
NOSCRIPT = "NOSCRIPT"
|
23
|
-
private_constant :NOSCRIPT
|
24
|
-
|
25
|
-
# LUA script source.
|
26
|
-
# @return [String]
|
27
|
-
attr_reader :source
|
28
|
-
|
29
|
-
# LUA script SHA1 digest.
|
30
|
-
# @return [String]
|
31
|
-
attr_reader :digest
|
32
|
-
|
33
|
-
# @param source [#to_s] Lua script.
|
34
|
-
def initialize(source)
|
35
|
-
@source = source.to_s.strip.freeze
|
36
|
-
@digest = Digest::SHA1.hexdigest(@source).freeze
|
37
|
-
end
|
38
|
-
|
39
|
-
# Loads script to redis.
|
40
|
-
# @param redis (see #namespaceless)
|
41
|
-
# @return [void]
|
42
|
-
def bootstrap!(redis)
|
43
|
-
digest = namespaceless(redis).script(LOAD, @source)
|
44
|
-
return if @digest == digest
|
45
|
-
|
46
|
-
# XXX: this may happen **ONLY** if script digesting will be
|
47
|
-
# changed in redis, which is not likely gonna happen.
|
48
|
-
warn "[#{self.class}] Unexpected digest: " \
|
49
|
-
"#{digest.inspect} (expected: #{@digest.inspect})"
|
50
|
-
|
51
|
-
@digest = digest.freeze
|
52
|
-
end
|
53
|
-
|
54
|
-
# Executes script and returns result of execution.
|
55
|
-
# @param redis (see #namespaceless)
|
56
|
-
# @param keys [Array] keys to pass to the script
|
57
|
-
# @param argv [Array] arguments to pass to the script
|
58
|
-
# @return depends on the script
|
59
|
-
def eval(redis, keys: [], argv: [])
|
60
|
-
redis.evalsha(@digest, keys, argv)
|
61
|
-
rescue => e
|
62
|
-
raise unless e.message.include? NOSCRIPT
|
63
|
-
|
64
|
-
bootstrap!(redis)
|
65
|
-
redis.evalsha(@digest, keys, argv)
|
66
|
-
end
|
67
|
-
|
68
|
-
# Reads given file and returns new {Prescription} with its contents.
|
69
|
-
# @param file [String]
|
70
|
-
# @return [Prescription]
|
71
|
-
def self.read(file)
|
72
|
-
new File.read file
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
# Yields real namespace-less redis client.
|
78
|
-
# @param redis [Redis, Redis::Namespace]
|
79
|
-
# @return [Redis]
|
80
|
-
def namespaceless(redis)
|
81
|
-
if redis.is_a?(Redis)
|
82
|
-
redis
|
83
|
-
elsif defined?(Redis::Namespace) && redis.is_a?(Redis::Namespace)
|
84
|
-
redis.redis
|
85
|
-
else
|
86
|
-
raise TypeError, "Unsupported redis client type: #{redis.class}"
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
data/redis-prescription.gemspec
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
lib = File.expand_path("../lib", __FILE__)
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
|
6
|
-
require "redis/prescription/version"
|
7
|
-
|
8
|
-
Gem::Specification.new do |spec|
|
9
|
-
spec.name = "redis-prescription"
|
10
|
-
spec.version = Redis::Prescription::VERSION
|
11
|
-
spec.authors = ["Alexey Zapparov"]
|
12
|
-
spec.email = ["ixti@member.fsf.org"]
|
13
|
-
|
14
|
-
spec.summary = "Redis LUA stored procedure runner."
|
15
|
-
spec.description = <<~DESCRIPTION
|
16
|
-
Preloads (and reloads when needed, e.g. when scripts
|
17
|
-
were flushed away) script and then runs it with `EVALSHA`.
|
18
|
-
DESCRIPTION
|
19
|
-
|
20
|
-
spec.homepage = "https://github.com/ixti/redis-prescription"
|
21
|
-
spec.license = "MIT"
|
22
|
-
|
23
|
-
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
24
|
-
f.match(%r{^(test|spec|features)/})
|
25
|
-
end
|
26
|
-
|
27
|
-
spec.require_paths = ["lib"]
|
28
|
-
|
29
|
-
spec.add_development_dependency "bundler", "~> 1.16"
|
30
|
-
end
|