redis-prescription 1.0.0 → 2.2.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 +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
|