redis 4.5.0 → 4.7.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.
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Pubsub
6
+ # Post a message to a channel.
7
+ def publish(channel, message)
8
+ send_command([:publish, channel, message])
9
+ end
10
+
11
+ def subscribed?
12
+ synchronize do |client|
13
+ client.is_a? SubscribedClient
14
+ end
15
+ end
16
+
17
+ # Listen for messages published to the given channels.
18
+ def subscribe(*channels, &block)
19
+ synchronize do |_client|
20
+ _subscription(:subscribe, 0, channels, block)
21
+ end
22
+ end
23
+
24
+ # Listen for messages published to the given channels. Throw a timeout error
25
+ # if there is no messages for a timeout period.
26
+ def subscribe_with_timeout(timeout, *channels, &block)
27
+ synchronize do |_client|
28
+ _subscription(:subscribe_with_timeout, timeout, channels, block)
29
+ end
30
+ end
31
+
32
+ # Stop listening for messages posted to the given channels.
33
+ def unsubscribe(*channels)
34
+ synchronize do |client|
35
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
36
+
37
+ client.unsubscribe(*channels)
38
+ end
39
+ end
40
+
41
+ # Listen for messages published to channels matching the given patterns.
42
+ def psubscribe(*channels, &block)
43
+ synchronize do |_client|
44
+ _subscription(:psubscribe, 0, channels, block)
45
+ end
46
+ end
47
+
48
+ # Listen for messages published to channels matching the given patterns.
49
+ # Throw a timeout error if there is no messages for a timeout period.
50
+ def psubscribe_with_timeout(timeout, *channels, &block)
51
+ synchronize do |_client|
52
+ _subscription(:psubscribe_with_timeout, timeout, channels, block)
53
+ end
54
+ end
55
+
56
+ # Stop listening for messages posted to channels matching the given patterns.
57
+ def punsubscribe(*channels)
58
+ synchronize do |client|
59
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
60
+
61
+ client.punsubscribe(*channels)
62
+ end
63
+ end
64
+
65
+ # Inspect the state of the Pub/Sub subsystem.
66
+ # Possible subcommands: channels, numsub, numpat.
67
+ def pubsub(subcommand, *args)
68
+ send_command([:pubsub, subcommand] + args)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Scripting
6
+ # Control remote script registry.
7
+ #
8
+ # @example Load a script
9
+ # sha = redis.script(:load, "return 1")
10
+ # # => <sha of this script>
11
+ # @example Check if a script exists
12
+ # redis.script(:exists, sha)
13
+ # # => true
14
+ # @example Check if multiple scripts exist
15
+ # redis.script(:exists, [sha, other_sha])
16
+ # # => [true, false]
17
+ # @example Flush the script registry
18
+ # redis.script(:flush)
19
+ # # => "OK"
20
+ # @example Kill a running script
21
+ # redis.script(:kill)
22
+ # # => "OK"
23
+ #
24
+ # @param [String] subcommand e.g. `exists`, `flush`, `load`, `kill`
25
+ # @param [Array<String>] args depends on subcommand
26
+ # @return [String, Boolean, Array<Boolean>, ...] depends on subcommand
27
+ #
28
+ # @see #eval
29
+ # @see #evalsha
30
+ def script(subcommand, *args)
31
+ subcommand = subcommand.to_s.downcase
32
+
33
+ if subcommand == "exists"
34
+ arg = args.first
35
+
36
+ send_command([:script, :exists, arg]) do |reply|
37
+ reply = reply.map { |r| Boolify.call(r) }
38
+
39
+ if arg.is_a?(Array)
40
+ reply
41
+ else
42
+ reply.first
43
+ end
44
+ end
45
+ else
46
+ send_command([:script, subcommand] + args)
47
+ end
48
+ end
49
+
50
+ # Evaluate Lua script.
51
+ #
52
+ # @example EVAL without KEYS nor ARGV
53
+ # redis.eval("return 1")
54
+ # # => 1
55
+ # @example EVAL with KEYS and ARGV as array arguments
56
+ # redis.eval("return { KEYS, ARGV }", ["k1", "k2"], ["a1", "a2"])
57
+ # # => [["k1", "k2"], ["a1", "a2"]]
58
+ # @example EVAL with KEYS and ARGV in a hash argument
59
+ # redis.eval("return { KEYS, ARGV }", :keys => ["k1", "k2"], :argv => ["a1", "a2"])
60
+ # # => [["k1", "k2"], ["a1", "a2"]]
61
+ #
62
+ # @param [Array<String>] keys optional array with keys to pass to the script
63
+ # @param [Array<String>] argv optional array with arguments to pass to the script
64
+ # @param [Hash] options
65
+ # - `:keys => Array<String>`: optional array with keys to pass to the script
66
+ # - `:argv => Array<String>`: optional array with arguments to pass to the script
67
+ # @return depends on the script
68
+ #
69
+ # @see #script
70
+ # @see #evalsha
71
+ def eval(*args)
72
+ _eval(:eval, args)
73
+ end
74
+
75
+ # Evaluate Lua script by its SHA.
76
+ #
77
+ # @example EVALSHA without KEYS nor ARGV
78
+ # redis.evalsha(sha)
79
+ # # => <depends on script>
80
+ # @example EVALSHA with KEYS and ARGV as array arguments
81
+ # redis.evalsha(sha, ["k1", "k2"], ["a1", "a2"])
82
+ # # => <depends on script>
83
+ # @example EVALSHA with KEYS and ARGV in a hash argument
84
+ # redis.evalsha(sha, :keys => ["k1", "k2"], :argv => ["a1", "a2"])
85
+ # # => <depends on script>
86
+ #
87
+ # @param [Array<String>] keys optional array with keys to pass to the script
88
+ # @param [Array<String>] argv optional array with arguments to pass to the script
89
+ # @param [Hash] options
90
+ # - `:keys => Array<String>`: optional array with keys to pass to the script
91
+ # - `:argv => Array<String>`: optional array with arguments to pass to the script
92
+ # @return depends on the script
93
+ #
94
+ # @see #script
95
+ # @see #eval
96
+ def evalsha(*args)
97
+ _eval(:evalsha, args)
98
+ end
99
+
100
+ private
101
+
102
+ def _eval(cmd, args)
103
+ script = args.shift
104
+ options = args.pop if args.last.is_a?(Hash)
105
+ options ||= {}
106
+
107
+ keys = args.shift || options[:keys] || []
108
+ argv = args.shift || options[:argv] || []
109
+
110
+ send_command([cmd, script, keys.length] + keys + argv)
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Server
6
+ # Asynchronously rewrite the append-only file.
7
+ #
8
+ # @return [String] `OK`
9
+ def bgrewriteaof
10
+ send_command([:bgrewriteaof])
11
+ end
12
+
13
+ # Asynchronously save the dataset to disk.
14
+ #
15
+ # @return [String] `OK`
16
+ def bgsave
17
+ send_command([:bgsave])
18
+ end
19
+
20
+ # Get or set server configuration parameters.
21
+ #
22
+ # @param [Symbol] action e.g. `:get`, `:set`, `:resetstat`
23
+ # @return [String, Hash] string reply, or hash when retrieving more than one
24
+ # property with `CONFIG GET`
25
+ def config(action, *args)
26
+ send_command([:config, action] + args) do |reply|
27
+ if reply.is_a?(Array) && action == :get
28
+ Hashify.call(reply)
29
+ else
30
+ reply
31
+ end
32
+ end
33
+ end
34
+
35
+ # Manage client connections.
36
+ #
37
+ # @param [String, Symbol] subcommand e.g. `kill`, `list`, `getname`, `setname`
38
+ # @return [String, Hash] depends on subcommand
39
+ def client(subcommand = nil, *args)
40
+ send_command([:client, subcommand] + args) do |reply|
41
+ if subcommand.to_s == "list"
42
+ reply.lines.map do |line|
43
+ entries = line.chomp.split(/[ =]/)
44
+ Hash[entries.each_slice(2).to_a]
45
+ end
46
+ else
47
+ reply
48
+ end
49
+ end
50
+ end
51
+
52
+ # Return the number of keys in the selected database.
53
+ #
54
+ # @return [Integer]
55
+ def dbsize
56
+ send_command([:dbsize])
57
+ end
58
+
59
+ # Remove all keys from all databases.
60
+ #
61
+ # @param [Hash] options
62
+ # - `:async => Boolean`: async flush (default: false)
63
+ # @return [String] `OK`
64
+ def flushall(options = nil)
65
+ if options && options[:async]
66
+ send_command(%i[flushall async])
67
+ else
68
+ send_command([:flushall])
69
+ end
70
+ end
71
+
72
+ # Remove all keys from the current database.
73
+ #
74
+ # @param [Hash] options
75
+ # - `:async => Boolean`: async flush (default: false)
76
+ # @return [String] `OK`
77
+ def flushdb(options = nil)
78
+ if options && options[:async]
79
+ send_command(%i[flushdb async])
80
+ else
81
+ send_command([:flushdb])
82
+ end
83
+ end
84
+
85
+ # Get information and statistics about the server.
86
+ #
87
+ # @param [String, Symbol] cmd e.g. "commandstats"
88
+ # @return [Hash<String, String>]
89
+ def info(cmd = nil)
90
+ send_command([:info, cmd].compact) do |reply|
91
+ if reply.is_a?(String)
92
+ reply = HashifyInfo.call(reply)
93
+
94
+ if cmd && cmd.to_s == "commandstats"
95
+ # Extract nested hashes for INFO COMMANDSTATS
96
+ reply = Hash[reply.map do |k, v|
97
+ v = v.split(",").map { |e| e.split("=") }
98
+ [k[/^cmdstat_(.*)$/, 1], Hash[v]]
99
+ end]
100
+ end
101
+ end
102
+
103
+ reply
104
+ end
105
+ end
106
+
107
+ # Get the UNIX time stamp of the last successful save to disk.
108
+ #
109
+ # @return [Integer]
110
+ def lastsave
111
+ send_command([:lastsave])
112
+ end
113
+
114
+ # Listen for all requests received by the server in real time.
115
+ #
116
+ # There is no way to interrupt this command.
117
+ #
118
+ # @yield a block to be called for every line of output
119
+ # @yieldparam [String] line timestamp and command that was executed
120
+ def monitor(&block)
121
+ synchronize do |client|
122
+ client.call_loop([:monitor], &block)
123
+ end
124
+ end
125
+
126
+ # Synchronously save the dataset to disk.
127
+ #
128
+ # @return [String]
129
+ def save
130
+ send_command([:save])
131
+ end
132
+
133
+ # Synchronously save the dataset to disk and then shut down the server.
134
+ def shutdown
135
+ synchronize do |client|
136
+ client.with_reconnect(false) do
137
+ begin
138
+ client.call([:shutdown])
139
+ rescue ConnectionError
140
+ # This means Redis has probably exited.
141
+ nil
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ # Make the server a slave of another instance, or promote it as master.
148
+ def slaveof(host, port)
149
+ send_command([:slaveof, host, port])
150
+ end
151
+
152
+ # Interact with the slowlog (get, len, reset)
153
+ #
154
+ # @param [String] subcommand e.g. `get`, `len`, `reset`
155
+ # @param [Integer] length maximum number of entries to return
156
+ # @return [Array<String>, Integer, String] depends on subcommand
157
+ def slowlog(subcommand, length = nil)
158
+ synchronize do |client|
159
+ args = [:slowlog, subcommand]
160
+ args << length if length
161
+ client.call args
162
+ end
163
+ end
164
+
165
+ # Internal command used for replication.
166
+ def sync
167
+ send_command([:sync])
168
+ end
169
+
170
+ # Return the server time.
171
+ #
172
+ # @example
173
+ # r.time # => [ 1333093196, 606806 ]
174
+ #
175
+ # @return [Array<Integer>] tuple of seconds since UNIX epoch and
176
+ # microseconds in the current second
177
+ def time
178
+ send_command([:time]) do |reply|
179
+ reply&.map(&:to_i)
180
+ end
181
+ end
182
+
183
+ def debug(*args)
184
+ send_command([:debug] + args)
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,207 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Sets
6
+ # Get the number of members in a set.
7
+ #
8
+ # @param [String] key
9
+ # @return [Integer]
10
+ def scard(key)
11
+ send_command([:scard, key])
12
+ end
13
+
14
+ # Add one or more members to a set.
15
+ #
16
+ # @param [String] key
17
+ # @param [String, Array<String>] member one member, or array of members
18
+ # @return [Boolean, Integer] `Boolean` when a single member is specified,
19
+ # holding whether or not adding the member succeeded, or `Integer` when an
20
+ # array of members is specified, holding the number of members that were
21
+ # successfully added
22
+ def sadd(key, member)
23
+ send_command([:sadd, key, member]) do |reply|
24
+ if member.is_a? Array
25
+ # Variadic: return integer
26
+ reply
27
+ else
28
+ # Single argument: return boolean
29
+ Boolify.call(reply)
30
+ end
31
+ end
32
+ end
33
+
34
+ # Remove one or more members from a set.
35
+ #
36
+ # @param [String] key
37
+ # @param [String, Array<String>] member one member, or array of members
38
+ # @return [Boolean, Integer] `Boolean` when a single member is specified,
39
+ # holding whether or not removing the member succeeded, or `Integer` when an
40
+ # array of members is specified, holding the number of members that were
41
+ # successfully removed
42
+ def srem(key, member)
43
+ send_command([:srem, key, member]) do |reply|
44
+ if member.is_a? Array
45
+ # Variadic: return integer
46
+ reply
47
+ else
48
+ # Single argument: return boolean
49
+ Boolify.call(reply)
50
+ end
51
+ end
52
+ end
53
+
54
+ # Remove and return one or more random member from a set.
55
+ #
56
+ # @param [String] key
57
+ # @return [String]
58
+ # @param [Integer] count
59
+ def spop(key, count = nil)
60
+ if count.nil?
61
+ send_command([:spop, key])
62
+ else
63
+ send_command([:spop, key, count])
64
+ end
65
+ end
66
+
67
+ # Get one or more random members from a set.
68
+ #
69
+ # @param [String] key
70
+ # @param [Integer] count
71
+ # @return [String]
72
+ def srandmember(key, count = nil)
73
+ if count.nil?
74
+ send_command([:srandmember, key])
75
+ else
76
+ send_command([:srandmember, key, count])
77
+ end
78
+ end
79
+
80
+ # Move a member from one set to another.
81
+ #
82
+ # @param [String] source source key
83
+ # @param [String] destination destination key
84
+ # @param [String] member member to move from `source` to `destination`
85
+ # @return [Boolean]
86
+ def smove(source, destination, member)
87
+ send_command([:smove, source, destination, member], &Boolify)
88
+ end
89
+
90
+ # Determine if a given value is a member of a set.
91
+ #
92
+ # @param [String] key
93
+ # @param [String] member
94
+ # @return [Boolean]
95
+ def sismember(key, member)
96
+ send_command([:sismember, key, member], &Boolify)
97
+ end
98
+
99
+ # Determine if multiple values are members of a set.
100
+ #
101
+ # @param [String] key
102
+ # @param [String, Array<String>] members
103
+ # @return [Array<Boolean>]
104
+ def smismember(key, *members)
105
+ send_command([:smismember, key, *members]) do |reply|
106
+ reply.map(&Boolify)
107
+ end
108
+ end
109
+
110
+ # Get all the members in a set.
111
+ #
112
+ # @param [String] key
113
+ # @return [Array<String>]
114
+ def smembers(key)
115
+ send_command([:smembers, key])
116
+ end
117
+
118
+ # Subtract multiple sets.
119
+ #
120
+ # @param [String, Array<String>] keys keys pointing to sets to subtract
121
+ # @return [Array<String>] members in the difference
122
+ def sdiff(*keys)
123
+ send_command([:sdiff, *keys])
124
+ end
125
+
126
+ # Subtract multiple sets and store the resulting set in a key.
127
+ #
128
+ # @param [String] destination destination key
129
+ # @param [String, Array<String>] keys keys pointing to sets to subtract
130
+ # @return [Integer] number of elements in the resulting set
131
+ def sdiffstore(destination, *keys)
132
+ send_command([:sdiffstore, destination, *keys])
133
+ end
134
+
135
+ # Intersect multiple sets.
136
+ #
137
+ # @param [String, Array<String>] keys keys pointing to sets to intersect
138
+ # @return [Array<String>] members in the intersection
139
+ def sinter(*keys)
140
+ send_command([:sinter, *keys])
141
+ end
142
+
143
+ # Intersect multiple sets and store the resulting set in a key.
144
+ #
145
+ # @param [String] destination destination key
146
+ # @param [String, Array<String>] keys keys pointing to sets to intersect
147
+ # @return [Integer] number of elements in the resulting set
148
+ def sinterstore(destination, *keys)
149
+ send_command([:sinterstore, destination, *keys])
150
+ end
151
+
152
+ # Add multiple sets.
153
+ #
154
+ # @param [String, Array<String>] keys keys pointing to sets to unify
155
+ # @return [Array<String>] members in the union
156
+ def sunion(*keys)
157
+ send_command([:sunion, *keys])
158
+ end
159
+
160
+ # Add multiple sets and store the resulting set in a key.
161
+ #
162
+ # @param [String] destination destination key
163
+ # @param [String, Array<String>] keys keys pointing to sets to unify
164
+ # @return [Integer] number of elements in the resulting set
165
+ def sunionstore(destination, *keys)
166
+ send_command([:sunionstore, destination, *keys])
167
+ end
168
+
169
+ # Scan a set
170
+ #
171
+ # @example Retrieve the first batch of keys in a set
172
+ # redis.sscan("set", 0)
173
+ #
174
+ # @param [String, Integer] cursor the cursor of the iteration
175
+ # @param [Hash] options
176
+ # - `:match => String`: only return keys matching the pattern
177
+ # - `:count => Integer`: return count keys at most per iteration
178
+ #
179
+ # @return [String, Array<String>] the next cursor and all found members
180
+ def sscan(key, cursor, **options)
181
+ _scan(:sscan, cursor, [key], **options)
182
+ end
183
+
184
+ # Scan a set
185
+ #
186
+ # @example Retrieve all of the keys in a set
187
+ # redis.sscan_each("set").to_a
188
+ # # => ["key1", "key2", "key3"]
189
+ #
190
+ # @param [Hash] options
191
+ # - `:match => String`: only return keys matching the pattern
192
+ # - `:count => Integer`: return count keys at most per iteration
193
+ #
194
+ # @return [Enumerator] an enumerator for all keys in the set
195
+ def sscan_each(key, **options, &block)
196
+ return to_enum(:sscan_each, key, **options) unless block_given?
197
+
198
+ cursor = 0
199
+ loop do
200
+ cursor, keys = sscan(key, cursor, **options)
201
+ keys.each(&block)
202
+ break if cursor == "0"
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end