redis 3.3.3 → 5.0.5

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.
Files changed (136) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +280 -12
  3. data/README.md +141 -147
  4. data/lib/redis/client.rb +77 -539
  5. data/lib/redis/commands/bitmaps.rb +66 -0
  6. data/lib/redis/commands/cluster.rb +28 -0
  7. data/lib/redis/commands/connection.rb +53 -0
  8. data/lib/redis/commands/geo.rb +84 -0
  9. data/lib/redis/commands/hashes.rb +254 -0
  10. data/lib/redis/commands/hyper_log_log.rb +37 -0
  11. data/lib/redis/commands/keys.rb +437 -0
  12. data/lib/redis/commands/lists.rb +285 -0
  13. data/lib/redis/commands/pubsub.rb +54 -0
  14. data/lib/redis/commands/scripting.rb +114 -0
  15. data/lib/redis/commands/server.rb +188 -0
  16. data/lib/redis/commands/sets.rb +214 -0
  17. data/lib/redis/commands/sorted_sets.rb +818 -0
  18. data/lib/redis/commands/streams.rb +384 -0
  19. data/lib/redis/commands/strings.rb +314 -0
  20. data/lib/redis/commands/transactions.rb +115 -0
  21. data/lib/redis/commands.rb +235 -0
  22. data/lib/redis/distributed.rb +300 -108
  23. data/lib/redis/errors.rb +22 -1
  24. data/lib/redis/hash_ring.rb +36 -79
  25. data/lib/redis/pipeline.rb +69 -83
  26. data/lib/redis/subscribe.rb +26 -19
  27. data/lib/redis/version.rb +3 -1
  28. data/lib/redis.rb +113 -2685
  29. metadata +40 -218
  30. data/.gitignore +0 -16
  31. data/.travis/Gemfile +0 -11
  32. data/.travis.yml +0 -89
  33. data/.yardopts +0 -3
  34. data/Gemfile +0 -4
  35. data/Rakefile +0 -87
  36. data/benchmarking/logging.rb +0 -71
  37. data/benchmarking/pipeline.rb +0 -51
  38. data/benchmarking/speed.rb +0 -21
  39. data/benchmarking/suite.rb +0 -24
  40. data/benchmarking/worker.rb +0 -71
  41. data/examples/basic.rb +0 -15
  42. data/examples/consistency.rb +0 -114
  43. data/examples/dist_redis.rb +0 -43
  44. data/examples/incr-decr.rb +0 -17
  45. data/examples/list.rb +0 -26
  46. data/examples/pubsub.rb +0 -37
  47. data/examples/sentinel/sentinel.conf +0 -9
  48. data/examples/sentinel/start +0 -49
  49. data/examples/sentinel.rb +0 -41
  50. data/examples/sets.rb +0 -36
  51. data/examples/unicorn/config.ru +0 -3
  52. data/examples/unicorn/unicorn.rb +0 -20
  53. data/lib/redis/connection/command_helper.rb +0 -44
  54. data/lib/redis/connection/hiredis.rb +0 -66
  55. data/lib/redis/connection/registry.rb +0 -12
  56. data/lib/redis/connection/ruby.rb +0 -429
  57. data/lib/redis/connection/synchrony.rb +0 -133
  58. data/lib/redis/connection.rb +0 -9
  59. data/redis.gemspec +0 -44
  60. data/test/bitpos_test.rb +0 -69
  61. data/test/blocking_commands_test.rb +0 -42
  62. data/test/client_test.rb +0 -59
  63. data/test/command_map_test.rb +0 -30
  64. data/test/commands_on_hashes_test.rb +0 -21
  65. data/test/commands_on_hyper_log_log_test.rb +0 -21
  66. data/test/commands_on_lists_test.rb +0 -20
  67. data/test/commands_on_sets_test.rb +0 -77
  68. data/test/commands_on_sorted_sets_test.rb +0 -137
  69. data/test/commands_on_strings_test.rb +0 -101
  70. data/test/commands_on_value_types_test.rb +0 -133
  71. data/test/connection_handling_test.rb +0 -277
  72. data/test/db/.gitkeep +0 -0
  73. data/test/distributed_blocking_commands_test.rb +0 -46
  74. data/test/distributed_commands_on_hashes_test.rb +0 -10
  75. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  76. data/test/distributed_commands_on_lists_test.rb +0 -22
  77. data/test/distributed_commands_on_sets_test.rb +0 -83
  78. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  79. data/test/distributed_commands_on_strings_test.rb +0 -59
  80. data/test/distributed_commands_on_value_types_test.rb +0 -95
  81. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  82. data/test/distributed_connection_handling_test.rb +0 -23
  83. data/test/distributed_internals_test.rb +0 -79
  84. data/test/distributed_key_tags_test.rb +0 -52
  85. data/test/distributed_persistence_control_commands_test.rb +0 -26
  86. data/test/distributed_publish_subscribe_test.rb +0 -92
  87. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  88. data/test/distributed_scripting_test.rb +0 -102
  89. data/test/distributed_sorting_test.rb +0 -20
  90. data/test/distributed_test.rb +0 -58
  91. data/test/distributed_transactions_test.rb +0 -32
  92. data/test/encoding_test.rb +0 -18
  93. data/test/error_replies_test.rb +0 -59
  94. data/test/fork_safety_test.rb +0 -65
  95. data/test/helper.rb +0 -232
  96. data/test/helper_test.rb +0 -24
  97. data/test/internals_test.rb +0 -457
  98. data/test/lint/blocking_commands.rb +0 -150
  99. data/test/lint/hashes.rb +0 -162
  100. data/test/lint/hyper_log_log.rb +0 -60
  101. data/test/lint/lists.rb +0 -143
  102. data/test/lint/sets.rb +0 -140
  103. data/test/lint/sorted_sets.rb +0 -316
  104. data/test/lint/strings.rb +0 -260
  105. data/test/lint/value_types.rb +0 -122
  106. data/test/persistence_control_commands_test.rb +0 -26
  107. data/test/pipelining_commands_test.rb +0 -242
  108. data/test/publish_subscribe_test.rb +0 -282
  109. data/test/remote_server_control_commands_test.rb +0 -118
  110. data/test/scanning_test.rb +0 -413
  111. data/test/scripting_test.rb +0 -78
  112. data/test/sentinel_command_test.rb +0 -80
  113. data/test/sentinel_test.rb +0 -255
  114. data/test/sorting_test.rb +0 -59
  115. data/test/ssl_test.rb +0 -73
  116. data/test/support/connection/hiredis.rb +0 -1
  117. data/test/support/connection/ruby.rb +0 -1
  118. data/test/support/connection/synchrony.rb +0 -17
  119. data/test/support/redis_mock.rb +0 -130
  120. data/test/support/ssl/gen_certs.sh +0 -31
  121. data/test/support/ssl/trusted-ca.crt +0 -25
  122. data/test/support/ssl/trusted-ca.key +0 -27
  123. data/test/support/ssl/trusted-cert.crt +0 -81
  124. data/test/support/ssl/trusted-cert.key +0 -28
  125. data/test/support/ssl/untrusted-ca.crt +0 -26
  126. data/test/support/ssl/untrusted-ca.key +0 -27
  127. data/test/support/ssl/untrusted-cert.crt +0 -82
  128. data/test/support/ssl/untrusted-cert.key +0 -28
  129. data/test/support/wire/synchrony.rb +0 -24
  130. data/test/support/wire/thread.rb +0 -5
  131. data/test/synchrony_driver.rb +0 -88
  132. data/test/test.conf.erb +0 -9
  133. data/test/thread_safety_test.rb +0 -62
  134. data/test/transactions_test.rb +0 -264
  135. data/test/unknown_commands_test.rb +0 -14
  136. data/test/url_param_test.rb +0 -138
@@ -0,0 +1,285 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Commands
5
+ module Lists
6
+ # Get the length of a list.
7
+ #
8
+ # @param [String] key
9
+ # @return [Integer]
10
+ def llen(key)
11
+ send_command([:llen, key])
12
+ end
13
+
14
+ # Remove the first/last element in a list, append/prepend it to another list and return it.
15
+ #
16
+ # @param [String] source source key
17
+ # @param [String] destination destination key
18
+ # @param [String, Symbol] where_source from where to remove the element from the source list
19
+ # e.g. 'LEFT' - from head, 'RIGHT' - from tail
20
+ # @param [String, Symbol] where_destination where to push the element to the source list
21
+ # e.g. 'LEFT' - to head, 'RIGHT' - to tail
22
+ #
23
+ # @return [nil, String] the element, or nil when the source key does not exist
24
+ #
25
+ # @note This command comes in place of the now deprecated RPOPLPUSH.
26
+ # Doing LMOVE RIGHT LEFT is equivalent.
27
+ def lmove(source, destination, where_source, where_destination)
28
+ where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
29
+
30
+ send_command([:lmove, source, destination, where_source, where_destination])
31
+ end
32
+
33
+ # Remove the first/last element in a list and append/prepend it
34
+ # to another list and return it, or block until one is available.
35
+ #
36
+ # @example With timeout
37
+ # element = redis.blmove("foo", "bar", "LEFT", "RIGHT", timeout: 5)
38
+ # # => nil on timeout
39
+ # # => "element" on success
40
+ # @example Without timeout
41
+ # element = redis.blmove("foo", "bar", "LEFT", "RIGHT")
42
+ # # => "element"
43
+ #
44
+ # @param [String] source source key
45
+ # @param [String] destination destination key
46
+ # @param [String, Symbol] where_source from where to remove the element from the source list
47
+ # e.g. 'LEFT' - from head, 'RIGHT' - from tail
48
+ # @param [String, Symbol] where_destination where to push the element to the source list
49
+ # e.g. 'LEFT' - to head, 'RIGHT' - to tail
50
+ # @param [Hash] options
51
+ # - `:timeout => [Float, Integer]`: timeout in seconds, defaults to no timeout
52
+ #
53
+ # @return [nil, String] the element, or nil when the source key does not exist or the timeout expired
54
+ #
55
+ def blmove(source, destination, where_source, where_destination, timeout: 0)
56
+ where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
57
+
58
+ command = [:blmove, source, destination, where_source, where_destination, timeout]
59
+ send_blocking_command(command, timeout)
60
+ end
61
+
62
+ # Prepend one or more values to a list, creating the list if it doesn't exist
63
+ #
64
+ # @param [String] key
65
+ # @param [String, Array<String>] value string value, or array of string values to push
66
+ # @return [Integer] the length of the list after the push operation
67
+ def lpush(key, value)
68
+ send_command([:lpush, key, value])
69
+ end
70
+
71
+ # Prepend a value to a list, only if the list exists.
72
+ #
73
+ # @param [String] key
74
+ # @param [String] value
75
+ # @return [Integer] the length of the list after the push operation
76
+ def lpushx(key, value)
77
+ send_command([:lpushx, key, value])
78
+ end
79
+
80
+ # Append one or more values to a list, creating the list if it doesn't exist
81
+ #
82
+ # @param [String] key
83
+ # @param [String, Array<String>] value string value, or array of string values to push
84
+ # @return [Integer] the length of the list after the push operation
85
+ def rpush(key, value)
86
+ send_command([:rpush, key, value])
87
+ end
88
+
89
+ # Append a value to a list, only if the list exists.
90
+ #
91
+ # @param [String] key
92
+ # @param [String] value
93
+ # @return [Integer] the length of the list after the push operation
94
+ def rpushx(key, value)
95
+ send_command([:rpushx, key, value])
96
+ end
97
+
98
+ # Remove and get the first elements in a list.
99
+ #
100
+ # @param [String] key
101
+ # @param [Integer] count number of elements to remove
102
+ # @return [nil, String, Array<String>] the values of the first elements
103
+ def lpop(key, count = nil)
104
+ command = [:lpop, key]
105
+ command << Integer(count) if count
106
+ send_command(command)
107
+ end
108
+
109
+ # Remove and get the last elements in a list.
110
+ #
111
+ # @param [String] key
112
+ # @param [Integer] count number of elements to remove
113
+ # @return [nil, String, Array<String>] the values of the last elements
114
+ def rpop(key, count = nil)
115
+ command = [:rpop, key]
116
+ command << Integer(count) if count
117
+ send_command(command)
118
+ end
119
+
120
+ # Remove the last element in a list, append it to another list and return it.
121
+ #
122
+ # @param [String] source source key
123
+ # @param [String] destination destination key
124
+ # @return [nil, String] the element, or nil when the source key does not exist
125
+ def rpoplpush(source, destination)
126
+ send_command([:rpoplpush, source, destination])
127
+ end
128
+
129
+ # Remove and get the first element in a list, or block until one is available.
130
+ #
131
+ # @example With timeout
132
+ # list, element = redis.blpop("list", :timeout => 5)
133
+ # # => nil on timeout
134
+ # # => ["list", "element"] on success
135
+ # @example Without timeout
136
+ # list, element = redis.blpop("list")
137
+ # # => ["list", "element"]
138
+ # @example Blocking pop on multiple lists
139
+ # list, element = redis.blpop(["list", "another_list"])
140
+ # # => ["list", "element"]
141
+ #
142
+ # @param [String, Array<String>] keys one or more keys to perform the
143
+ # blocking pop on
144
+ # @param [Hash] options
145
+ # - `:timeout => [Float, Integer]`: timeout in seconds, defaults to no timeout
146
+ #
147
+ # @return [nil, [String, String]]
148
+ # - `nil` when the operation timed out
149
+ # - tuple of the list that was popped from and element was popped otherwise
150
+ def blpop(*args)
151
+ _bpop(:blpop, args)
152
+ end
153
+
154
+ # Remove and get the last element in a list, or block until one is available.
155
+ #
156
+ # @param [String, Array<String>] keys one or more keys to perform the
157
+ # blocking pop on
158
+ # @param [Hash] options
159
+ # - `:timeout => [Float, Integer]`: timeout in seconds, defaults to no timeout
160
+ #
161
+ # @return [nil, [String, String]]
162
+ # - `nil` when the operation timed out
163
+ # - tuple of the list that was popped from and element was popped otherwise
164
+ #
165
+ # @see #blpop
166
+ def brpop(*args)
167
+ _bpop(:brpop, args)
168
+ end
169
+
170
+ # Pop a value from a list, push it to another list and return it; or block
171
+ # until one is available.
172
+ #
173
+ # @param [String] source source key
174
+ # @param [String] destination destination key
175
+ # @param [Hash] options
176
+ # - `:timeout => [Float, Integer]`: timeout in seconds, defaults to no timeout
177
+ #
178
+ # @return [nil, String]
179
+ # - `nil` when the operation timed out
180
+ # - the element was popped and pushed otherwise
181
+ def brpoplpush(source, destination, timeout: 0)
182
+ command = [:brpoplpush, source, destination, timeout]
183
+ send_blocking_command(command, timeout)
184
+ end
185
+
186
+ # Get an element from a list by its index.
187
+ #
188
+ # @param [String] key
189
+ # @param [Integer] index
190
+ # @return [String]
191
+ def lindex(key, index)
192
+ send_command([:lindex, key, Integer(index)])
193
+ end
194
+
195
+ # Insert an element before or after another element in a list.
196
+ #
197
+ # @param [String] key
198
+ # @param [String, Symbol] where `BEFORE` or `AFTER`
199
+ # @param [String] pivot reference element
200
+ # @param [String] value
201
+ # @return [Integer] length of the list after the insert operation, or `-1`
202
+ # when the element `pivot` was not found
203
+ def linsert(key, where, pivot, value)
204
+ send_command([:linsert, key, where, pivot, value])
205
+ end
206
+
207
+ # Get a range of elements from a list.
208
+ #
209
+ # @param [String] key
210
+ # @param [Integer] start start index
211
+ # @param [Integer] stop stop index
212
+ # @return [Array<String>]
213
+ def lrange(key, start, stop)
214
+ send_command([:lrange, key, Integer(start), Integer(stop)])
215
+ end
216
+
217
+ # Remove elements from a list.
218
+ #
219
+ # @param [String] key
220
+ # @param [Integer] count number of elements to remove. Use a positive
221
+ # value to remove the first `count` occurrences of `value`. A negative
222
+ # value to remove the last `count` occurrences of `value`. Or zero, to
223
+ # remove all occurrences of `value` from the list.
224
+ # @param [String] value
225
+ # @return [Integer] the number of removed elements
226
+ def lrem(key, count, value)
227
+ send_command([:lrem, key, Integer(count), value])
228
+ end
229
+
230
+ # Set the value of an element in a list by its index.
231
+ #
232
+ # @param [String] key
233
+ # @param [Integer] index
234
+ # @param [String] value
235
+ # @return [String] `OK`
236
+ def lset(key, index, value)
237
+ send_command([:lset, key, Integer(index), value])
238
+ end
239
+
240
+ # Trim a list to the specified range.
241
+ #
242
+ # @param [String] key
243
+ # @param [Integer] start start index
244
+ # @param [Integer] stop stop index
245
+ # @return [String] `OK`
246
+ def ltrim(key, start, stop)
247
+ send_command([:ltrim, key, Integer(start), Integer(stop)])
248
+ end
249
+
250
+ private
251
+
252
+ def _bpop(cmd, args, &blk)
253
+ timeout = if args.last.is_a?(Hash)
254
+ options = args.pop
255
+ options[:timeout]
256
+ end
257
+
258
+ timeout ||= 0
259
+ unless timeout.is_a?(Integer) || timeout.is_a?(Float)
260
+ raise ArgumentError, "timeout must be an Integer or Float, got: #{timeout.class}"
261
+ end
262
+
263
+ args.flatten!(1)
264
+ command = [cmd].concat(args)
265
+ command << timeout
266
+ send_blocking_command(command, timeout, &blk)
267
+ end
268
+
269
+ def _normalize_move_wheres(where_source, where_destination)
270
+ where_source = where_source.to_s.upcase
271
+ where_destination = where_destination.to_s.upcase
272
+
273
+ if where_source != "LEFT" && where_source != "RIGHT"
274
+ raise ArgumentError, "where_source must be 'LEFT' or 'RIGHT'"
275
+ end
276
+
277
+ if where_destination != "LEFT" && where_destination != "RIGHT"
278
+ raise ArgumentError, "where_destination must be 'LEFT' or 'RIGHT'"
279
+ end
280
+
281
+ [where_source, where_destination]
282
+ end
283
+ end
284
+ end
285
+ end
@@ -0,0 +1,54 @@
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
+ !@subscription_client.nil?
13
+ end
14
+
15
+ # Listen for messages published to the given channels.
16
+ def subscribe(*channels, &block)
17
+ _subscription(:subscribe, 0, channels, block)
18
+ end
19
+
20
+ # Listen for messages published to the given channels. Throw a timeout error
21
+ # if there is no messages for a timeout period.
22
+ def subscribe_with_timeout(timeout, *channels, &block)
23
+ _subscription(:subscribe_with_timeout, timeout, channels, block)
24
+ end
25
+
26
+ # Stop listening for messages posted to the given channels.
27
+ def unsubscribe(*channels)
28
+ _subscription(:unsubscribe, 0, channels, nil)
29
+ end
30
+
31
+ # Listen for messages published to channels matching the given patterns.
32
+ def psubscribe(*channels, &block)
33
+ _subscription(:psubscribe, 0, channels, block)
34
+ end
35
+
36
+ # Listen for messages published to channels matching the given patterns.
37
+ # Throw a timeout error if there is no messages for a timeout period.
38
+ def psubscribe_with_timeout(timeout, *channels, &block)
39
+ _subscription(:psubscribe_with_timeout, timeout, channels, block)
40
+ end
41
+
42
+ # Stop listening for messages posted to channels matching the given patterns.
43
+ def punsubscribe(*channels)
44
+ _subscription(:punsubscribe, 0, channels, nil)
45
+ end
46
+
47
+ # Inspect the state of the Pub/Sub subsystem.
48
+ # Possible subcommands: channels, numsub, numpat.
49
+ def pubsub(subcommand, *args)
50
+ send_command([:pubsub, subcommand] + args)
51
+ end
52
+ end
53
+ end
54
+ 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, *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
121
+ synchronize do |client|
122
+ client = client.pubsub
123
+ client.call_v([:monitor])
124
+ loop do
125
+ yield client.next_event
126
+ end
127
+ end
128
+ end
129
+
130
+ # Synchronously save the dataset to disk.
131
+ #
132
+ # @return [String]
133
+ def save
134
+ send_command([:save])
135
+ end
136
+
137
+ # Synchronously save the dataset to disk and then shut down the server.
138
+ def shutdown
139
+ synchronize do |client|
140
+ client.disable_reconnection do
141
+ client.call_v([:shutdown])
142
+ rescue ConnectionError
143
+ # This means Redis has probably exited.
144
+ nil
145
+ end
146
+ end
147
+ end
148
+
149
+ # Make the server a slave of another instance, or promote it as master.
150
+ def slaveof(host, port)
151
+ send_command([:slaveof, host, port])
152
+ end
153
+
154
+ # Interact with the slowlog (get, len, reset)
155
+ #
156
+ # @param [String] subcommand e.g. `get`, `len`, `reset`
157
+ # @param [Integer] length maximum number of entries to return
158
+ # @return [Array<String>, Integer, String] depends on subcommand
159
+ def slowlog(subcommand, length = nil)
160
+ args = [:slowlog, subcommand]
161
+ args << Integer(length) if length
162
+ send_command(args)
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