redis 4.0.1 → 4.0.3

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +17 -29
  4. data/.travis/Gemfile +5 -0
  5. data/CHANGELOG.md +29 -0
  6. data/Gemfile +5 -0
  7. data/README.md +1 -1
  8. data/bin/build +71 -0
  9. data/lib/redis.rb +198 -12
  10. data/lib/redis/client.rb +26 -12
  11. data/lib/redis/cluster.rb +285 -0
  12. data/lib/redis/cluster/command.rb +81 -0
  13. data/lib/redis/cluster/command_loader.rb +32 -0
  14. data/lib/redis/cluster/key_slot_converter.rb +72 -0
  15. data/lib/redis/cluster/node.rb +104 -0
  16. data/lib/redis/cluster/node_key.rb +35 -0
  17. data/lib/redis/cluster/node_loader.rb +35 -0
  18. data/lib/redis/cluster/option.rb +76 -0
  19. data/lib/redis/cluster/slot.rb +69 -0
  20. data/lib/redis/cluster/slot_loader.rb +47 -0
  21. data/lib/redis/connection/ruby.rb +5 -2
  22. data/lib/redis/distributed.rb +10 -2
  23. data/lib/redis/errors.rb +46 -0
  24. data/lib/redis/pipeline.rb +9 -1
  25. data/lib/redis/version.rb +1 -1
  26. data/makefile +54 -22
  27. data/redis.gemspec +2 -1
  28. data/test/client_test.rb +17 -0
  29. data/test/cluster_abnormal_state_test.rb +38 -0
  30. data/test/cluster_blocking_commands_test.rb +15 -0
  31. data/test/cluster_client_internals_test.rb +77 -0
  32. data/test/cluster_client_key_hash_tags_test.rb +88 -0
  33. data/test/cluster_client_options_test.rb +147 -0
  34. data/test/cluster_client_pipelining_test.rb +59 -0
  35. data/test/cluster_client_replicas_test.rb +36 -0
  36. data/test/cluster_client_slots_test.rb +94 -0
  37. data/test/cluster_client_transactions_test.rb +71 -0
  38. data/test/cluster_commands_on_cluster_test.rb +165 -0
  39. data/test/cluster_commands_on_connection_test.rb +40 -0
  40. data/test/cluster_commands_on_geo_test.rb +74 -0
  41. data/test/cluster_commands_on_hashes_test.rb +11 -0
  42. data/test/cluster_commands_on_hyper_log_log_test.rb +17 -0
  43. data/test/cluster_commands_on_keys_test.rb +134 -0
  44. data/test/cluster_commands_on_lists_test.rb +15 -0
  45. data/test/cluster_commands_on_pub_sub_test.rb +101 -0
  46. data/test/cluster_commands_on_scripting_test.rb +56 -0
  47. data/test/cluster_commands_on_server_test.rb +221 -0
  48. data/test/cluster_commands_on_sets_test.rb +39 -0
  49. data/test/cluster_commands_on_sorted_sets_test.rb +35 -0
  50. data/test/cluster_commands_on_streams_test.rb +196 -0
  51. data/test/cluster_commands_on_strings_test.rb +15 -0
  52. data/test/cluster_commands_on_transactions_test.rb +41 -0
  53. data/test/cluster_commands_on_value_types_test.rb +14 -0
  54. data/test/commands_on_geo_test.rb +116 -0
  55. data/test/commands_on_hashes_test.rb +2 -14
  56. data/test/commands_on_hyper_log_log_test.rb +2 -14
  57. data/test/commands_on_lists_test.rb +2 -13
  58. data/test/commands_on_sets_test.rb +2 -70
  59. data/test/commands_on_sorted_sets_test.rb +2 -145
  60. data/test/commands_on_strings_test.rb +2 -94
  61. data/test/commands_on_value_types_test.rb +36 -0
  62. data/test/distributed_blocking_commands_test.rb +8 -0
  63. data/test/distributed_commands_on_hashes_test.rb +16 -3
  64. data/test/distributed_commands_on_hyper_log_log_test.rb +8 -13
  65. data/test/distributed_commands_on_lists_test.rb +4 -5
  66. data/test/distributed_commands_on_sets_test.rb +45 -46
  67. data/test/distributed_commands_on_sorted_sets_test.rb +51 -8
  68. data/test/distributed_commands_on_strings_test.rb +10 -0
  69. data/test/distributed_commands_on_value_types_test.rb +36 -0
  70. data/test/helper.rb +176 -32
  71. data/test/internals_test.rb +20 -1
  72. data/test/lint/blocking_commands.rb +40 -16
  73. data/test/lint/hashes.rb +41 -0
  74. data/test/lint/hyper_log_log.rb +15 -1
  75. data/test/lint/lists.rb +16 -0
  76. data/test/lint/sets.rb +142 -0
  77. data/test/lint/sorted_sets.rb +183 -2
  78. data/test/lint/strings.rb +102 -0
  79. data/test/pipelining_commands_test.rb +8 -0
  80. data/test/support/cluster/orchestrator.rb +199 -0
  81. data/test/support/redis_mock.rb +1 -1
  82. data/test/transactions_test.rb +10 -0
  83. metadata +81 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e0d0ba2328aca583c0f487729779c2b10b12c7b
4
- data.tar.gz: 4a411cfa3512bafe7c2c12ac8a02c02bc87065aa
3
+ metadata.gz: 1ec178932a6874e8ac7a4f6cfa4390c8223367ec
4
+ data.tar.gz: 986e6c5d1729b9ec8f7d66900e5d9ad9437e29f9
5
5
  SHA512:
6
- metadata.gz: 3af7dc63dc7bdc243da306817583db9b6989c21a0714c6652b4a443bfd9c46b2afba0707b028d708a74148bbeedd09d4ec207a0459b3559cb19b95f2f030bb07
7
- data.tar.gz: 98d5aed456ebcd3ccdc50980879a3c14be4c8a1874c6febae95579d2fb3828bda2e82b05e62dc6a4966e178993d4a2a20dc3dedd9f8583cd731851eab2f8e164
6
+ metadata.gz: 14db8cb42f08014ebd942c39b5f119a68a8ec50fec046572bb3afc7708634589311b9bea0d4153bfd1d7cb1c46a94a00d77613872b2cb8c10c33f132755cd62d
7
+ data.tar.gz: efe0db683450fcb30b6d23b8c9448d6925b5c5d95b847f9563405a61cc7e667bbf8aea22642dcc9d8a1b716dc9c8cb55c562c86a08223e020991f69eff7df7f7
data/.gitignore CHANGED
@@ -5,6 +5,7 @@ Gemfile.lock
5
5
  /tmp/
6
6
  /.idea
7
7
  /.yardoc
8
+ /.bundle
8
9
  /coverage/*
9
10
  /doc/
10
11
  /examples/sentinel/sentinel.conf
@@ -14,3 +15,5 @@ Gemfile.lock
14
15
  /redis/*
15
16
  /test/db
16
17
  /test/test.conf
18
+ appendonly.aof
19
+ temp-rewriteaof-*.aof
@@ -1,13 +1,19 @@
1
1
  language: ruby
2
+ cache:
3
+ directories:
4
+ - tmp/cache
5
+ before_install:
6
+ - gem update --system 2.6.14
7
+ - gem --version
2
8
 
3
- script: make test
9
+ script: make
4
10
 
5
11
  rvm:
6
12
  - 2.2.2
7
13
  - 2.3.3
8
14
  - 2.4.1
9
- - jruby-9
10
- - rbx-3
15
+ - 2.5.0
16
+ - jruby-9.1.17.0
11
17
 
12
18
  gemfile: ".travis/Gemfile"
13
19
 
@@ -19,7 +25,7 @@ before_script:
19
25
  env:
20
26
  global:
21
27
  - VERBOSE=true
22
- - TIMEOUT=1
28
+ - TIMEOUT=9
23
29
  matrix:
24
30
  - DRIVER=ruby REDIS_BRANCH=3.0
25
31
  - DRIVER=ruby REDIS_BRANCH=3.2
@@ -27,7 +33,7 @@ env:
27
33
  - DRIVER=hiredis REDIS_BRANCH=3.2
28
34
  - DRIVER=synchrony REDIS_BRANCH=3.0
29
35
  - DRIVER=synchrony REDIS_BRANCH=3.2
30
- - DRIVER=ruby REDIS_BRANCH=unstable
36
+ - DRIVER=ruby REDIS_BRANCH=4.0
31
37
 
32
38
  branches:
33
39
  only:
@@ -38,34 +44,16 @@ branches:
38
44
  matrix:
39
45
  exclude:
40
46
  # hiredis
41
- - rvm: jruby-9
42
- gemfile: .travis/Gemfile
43
- env: DRIVER=hiredis REDIS_BRANCH=3.0
44
- - rvm: jruby-9
45
- gemfile: .travis/Gemfile
46
- env: DRIVER=hiredis REDIS_BRANCH=3.2
47
- - rvm: jruby-9
48
- gemfile: .travis/Gemfile
49
- env: DRIVER=hiredis REDIS_BRANCH=3.0
50
- - rvm: jruby-9
51
- gemfile: .travis/Gemfile
52
- env: DRIVER=hiredis REDIS_BRANCH=3.2
47
+ - rvm: jruby-9.1.17.0
48
+ env: DRIVER=hiredis REDIS_BRANCH=3.0
49
+ - rvm: jruby-9.1.17.0
50
+ env: DRIVER=hiredis REDIS_BRANCH=3.2
53
51
 
54
52
  # synchrony
55
- - rvm: jruby-9
56
- gemfile: .travis/Gemfile
53
+ - rvm: jruby-9.1.17.0
57
54
  env: DRIVER=synchrony REDIS_BRANCH=3.0
58
- - rvm: jruby-9
59
- gemfile: .travis/Gemfile
55
+ - rvm: jruby-9.1.17.0
60
56
  env: DRIVER=synchrony REDIS_BRANCH=3.2
61
- - rvm: jruby-9
62
- gemfile: .travis/Gemfile
63
- env: DRIVER=synchrony REDIS_BRANCH=3.0
64
- - rvm: jruby-9
65
- gemfile: .travis/Gemfile
66
- env: DRIVER=synchrony REDIS_BRANCH=3.2
67
- allow_failures:
68
- - rvm: rbx-3
69
57
 
70
58
  notifications:
71
59
  irc:
@@ -2,6 +2,11 @@ source "https://rubygems.org"
2
2
 
3
3
  gemspec :path => "../"
4
4
 
5
+ # Using jruby-openssl 0.10.0, we get NPEs in jruby tests: https://github.com/redis/redis-rb/issues/756
6
+ platform :jruby do
7
+ gem 'jruby-openssl', '<0.10.0'
8
+ end
9
+
5
10
  case ENV["DRIVER"]
6
11
  when "hiredis"
7
12
  gem "hiredis"
@@ -1,3 +1,23 @@
1
+ # 4.0.3
2
+
3
+ * Fix raising command error for first command in pipeline. See #788.
4
+ * Fix the gemspec to stop exposing a `build` executable. See #785.
5
+ * Add `:reconnect_delay` and `:reconnect_delay_max` options. See #778.
6
+
7
+ # 4.0.2
8
+
9
+ * Added `Redis#unlink`. See #766.
10
+
11
+ * `Redis.new` now accept a custom connector via `:connector`. See #591.
12
+
13
+ * `Redis#multi` no longer perform empty transactions. See #747.
14
+
15
+ * `Redis#hdel` now accepts hash keys as multiple arguments like `#del`. See #755.
16
+
17
+ * Allow to skip SSL verification. See #745.
18
+
19
+ * Add Geo commands: `geoadd`, `geohash`, `georadius`, `georadiusbymember`, `geopos`, `geodist`. See #730.
20
+
1
21
  # 4.0.1
2
22
 
3
23
  * `Redis::Distributed` now supports `mget` and `mapped_mget`. See #687.
@@ -21,6 +41,15 @@
21
41
 
22
42
  * Dropped official support for Ruby < 2.2.2.
23
43
 
44
+ # 3.3.5
45
+
46
+ * Fixed Ruby 1.8 compatibility after backporting `Redis#connection`. See #719.
47
+
48
+ # 3.3.4 (yanked)
49
+
50
+ * `Redis#connection` returns a hash with connection information.
51
+ You shouldn't need to call `Redis#_client`, ever.
52
+
24
53
  # 3.3.3
25
54
 
26
55
  * Improved timeout handling after dropping Timeout module.
data/Gemfile CHANGED
@@ -1,3 +1,8 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
+
5
+ # Using jruby-openssl 0.10.0, we get NPEs in jruby tests: https://github.com/redis/redis-rb/issues/756
6
+ platform :jruby do
7
+ gem 'jruby-openssl', '<0.10.0'
8
+ end
data/README.md CHANGED
@@ -176,7 +176,7 @@ it can't connect to the server a `Redis::CannotConnectError` error will be raise
176
176
  ```ruby
177
177
  begin
178
178
  redis.ping
179
- rescue Exception => e
179
+ rescue StandardError => e
180
180
  e.inspect
181
181
  # => #<Redis::CannotConnectError: Timed out connecting to Redis on 10.0.1.1:6380>
182
182
 
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ TARBALL = ARGV[0]
4
+
5
+ require 'digest/sha1'
6
+ require 'fileutils'
7
+
8
+ class Builder
9
+ def initialize(redis_branch, tmp_dir)
10
+ @redis_branch = redis_branch
11
+ @tmp_dir = tmp_dir
12
+ @build_dir = File.join(@tmp_dir, "cache", "redis-#{redis_branch}")
13
+ end
14
+
15
+ def run
16
+ download_tarball
17
+ if old_checkum != checksum
18
+ build
19
+ update_checksum
20
+ end
21
+ 0
22
+ end
23
+
24
+ def download_tarball
25
+ command!('wget', tarball_url, '-O', tarball_path)
26
+ end
27
+
28
+ def tarball_path
29
+ File.join(@tmp_dir, "redis-#{@redis_branch}.tar.gz")
30
+ end
31
+
32
+ def tarball_url
33
+ "https://github.com/antirez/redis/archive/#{@redis_branch}.tar.gz"
34
+ end
35
+
36
+ def build
37
+ FileUtils.rm_rf(@build_dir)
38
+ FileUtils.mkdir_p(@build_dir)
39
+ command!('tar', 'xf', tarball_path, '-C', File.expand_path('../', @build_dir))
40
+ Dir.chdir(@build_dir) do
41
+ command!('make')
42
+ end
43
+ end
44
+
45
+ def update_checksum
46
+ File.write(checksum_path, checksum)
47
+ end
48
+
49
+ def old_checkum
50
+ File.read(checksum_path)
51
+ rescue Errno::ENOENT
52
+ nil
53
+ end
54
+
55
+ def checksum_path
56
+ File.join(@build_dir, 'build.checksum')
57
+ end
58
+
59
+ def checksum
60
+ @checksum ||= Digest::SHA1.file(tarball_path).hexdigest
61
+ end
62
+
63
+ def command!(*args)
64
+ puts "$ #{args.join(' ')}"
65
+ unless system(*args)
66
+ raise "Command failed with status #{$?.exitstatus}"
67
+ end
68
+ end
69
+ end
70
+
71
+ exit Builder.new(ARGV[0], ARGV[1]).run
@@ -31,11 +31,16 @@ class Redis
31
31
  # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
32
32
  # @option options [Array] :sentinels List of sentinels to contact
33
33
  # @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
34
+ # @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
35
+ # @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
36
+ # @option options [Class] :connector Class of custom connector
34
37
  #
35
38
  # @return [Redis] a new client instance
36
39
  def initialize(options = {})
37
40
  @options = options.dup
38
- @original_client = @client = Client.new(options)
41
+ @cluster_mode = options.key?(:cluster)
42
+ client = @cluster_mode ? Cluster : Client
43
+ @original_client = @client = client.new(options)
39
44
  @queue = Hash.new { |h, k| h[k] = [] }
40
45
 
41
46
  super() # Monitor#initialize
@@ -273,9 +278,7 @@ class Redis
273
278
  synchronize do |client|
274
279
  client.call([:info, cmd].compact) do |reply|
275
280
  if reply.kind_of?(String)
276
- reply = Hash[reply.split("\r\n").map do |line|
277
- line.split(":", 2) unless line =~ /^(#|$)/
278
- end.compact]
281
+ reply = HashifyInfo.call(reply)
279
282
 
280
283
  if cmd && cmd.to_s == "commandstats"
281
284
  # Extract nested hashes for INFO COMMANDSTATS
@@ -524,6 +527,16 @@ class Redis
524
527
  end
525
528
  end
526
529
 
530
+ # Unlink one or more keys.
531
+ #
532
+ # @param [String, Array<String>] keys
533
+ # @return [Fixnum] number of keys that were unlinked
534
+ def unlink(*keys)
535
+ synchronize do |client|
536
+ client.call([:unlink] + keys)
537
+ end
538
+ end
539
+
527
540
  # Determine if a key exists.
528
541
  #
529
542
  # @param [String] key
@@ -2102,9 +2115,9 @@ class Redis
2102
2115
  # @param [String] key
2103
2116
  # @param [String, Array<String>] field
2104
2117
  # @return [Fixnum] the number of fields that were removed from the hash
2105
- def hdel(key, field)
2118
+ def hdel(key, *fields)
2106
2119
  synchronize do |client|
2107
- client.call([:hdel, key, field])
2120
+ client.call([:hdel, key, *fields])
2108
2121
  end
2109
2122
  end
2110
2123
 
@@ -2710,6 +2723,86 @@ class Redis
2710
2723
  end
2711
2724
  end
2712
2725
 
2726
+ # Adds the specified geospatial items (latitude, longitude, name) to the specified key
2727
+ #
2728
+ # @param [String] key
2729
+ # @param [Array] member arguemnts for member or members: longitude, latitude, name
2730
+ # @return [Intger] number of elements added to the sorted set
2731
+ def geoadd(key, *member)
2732
+ synchronize do |client|
2733
+ client.call([:geoadd, key, member])
2734
+ end
2735
+ end
2736
+
2737
+ # Returns geohash string representing position for specified members of the specified key.
2738
+ #
2739
+ # @param [String] key
2740
+ # @param [String, Array<String>] member one member or array of members
2741
+ # @return [Array<String, nil>] returns array containg geohash string if member is present, nil otherwise
2742
+ def geohash(key, member)
2743
+ synchronize do |client|
2744
+ client.call([:geohash, key, member])
2745
+ end
2746
+ end
2747
+
2748
+
2749
+ # Query a sorted set representing a geospatial index to fetch members matching a
2750
+ # given maximum distance from a point
2751
+ #
2752
+ # @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
2753
+ # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest to the nearest relative to the center
2754
+ # @param [Integer] count limit the results to the first N matching items
2755
+ # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
2756
+ # @return [Array<String>] may be changed with `options`
2757
+
2758
+ def georadius(*args, **geoptions)
2759
+ geoarguments = _geoarguments(*args, **geoptions)
2760
+
2761
+ synchronize do |client|
2762
+ client.call([:georadius, *geoarguments])
2763
+ end
2764
+ end
2765
+
2766
+ # Query a sorted set representing a geospatial index to fetch members matching a
2767
+ # given maximum distance from an already existing member
2768
+ #
2769
+ # @param [Array] args key, member, radius, unit(m|km|ft|mi)
2770
+ # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest to the nearest relative to the center
2771
+ # @param [Integer] count limit the results to the first N matching items
2772
+ # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
2773
+ # @return [Array<String>] may be changed with `options`
2774
+
2775
+ def georadiusbymember(*args, **geoptions)
2776
+ geoarguments = _geoarguments(*args, **geoptions)
2777
+
2778
+ synchronize do |client|
2779
+ client.call([:georadiusbymember, *geoarguments])
2780
+ end
2781
+ end
2782
+
2783
+ # Returns longitude and latitude of members of a geospatial index
2784
+ #
2785
+ # @param [String] key
2786
+ # @param [String, Array<String>] member one member or array of members
2787
+ # @return [Array<Array<String>, nil>] returns array of elements, where each element is either array of longitude and latitude or nil
2788
+ def geopos(key, member)
2789
+ synchronize do |client|
2790
+ client.call([:geopos, key, member])
2791
+ end
2792
+ end
2793
+
2794
+ # Returns the distance between two members of a geospatial index
2795
+ #
2796
+ # @param [String ]key
2797
+ # @param [Array<String>] members
2798
+ # @param ['m', 'km', 'mi', 'ft'] unit
2799
+ # @return [String, nil] returns distance in spefied unit if both members present, nil otherwise.
2800
+ def geodist(key, member1, member2, unit = 'm')
2801
+ synchronize do |client|
2802
+ client.call([:geodist, key, member1, member2, unit])
2803
+ end
2804
+ end
2805
+
2713
2806
  # Interact with the sentinel command (masters, master, slaves, failover)
2714
2807
  #
2715
2808
  # @param [String] subcommand e.g. `masters`, `master`, `slaves`
@@ -2737,6 +2830,41 @@ class Redis
2737
2830
  end
2738
2831
  end
2739
2832
 
2833
+ # Sends `CLUSTER *` command to random node and returns its reply.
2834
+ #
2835
+ # @see https://redis.io/commands#cluster Reference of cluster command
2836
+ #
2837
+ # @param subcommand [String, Symbol] the subcommand of cluster command
2838
+ # e.g. `:slots`, `:nodes`, `:slaves`, `:info`
2839
+ #
2840
+ # @return [Object] depends on the subcommand
2841
+ def cluster(subcommand, *args)
2842
+ subcommand = subcommand.to_s.downcase
2843
+ block = case subcommand
2844
+ when 'slots' then HashifyClusterSlots
2845
+ when 'nodes' then HashifyClusterNodes
2846
+ when 'slaves' then HashifyClusterSlaves
2847
+ when 'info' then HashifyInfo
2848
+ else Noop
2849
+ end
2850
+
2851
+ # @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
2852
+ block = Noop unless @cluster_mode
2853
+
2854
+ synchronize do |client|
2855
+ client.call([:cluster, subcommand] + args, &block)
2856
+ end
2857
+ end
2858
+
2859
+ # Sends `ASKING` command to random node and returns its reply.
2860
+ #
2861
+ # @see https://redis.io/topics/cluster-spec#ask-redirection ASK redirection
2862
+ #
2863
+ # @return [String] `'OK'`
2864
+ def asking
2865
+ synchronize { |client| client.call(%i[asking]) }
2866
+ end
2867
+
2740
2868
  def id
2741
2869
  @original_client.id
2742
2870
  end
@@ -2750,6 +2878,8 @@ class Redis
2750
2878
  end
2751
2879
 
2752
2880
  def connection
2881
+ return @original_client.connection_info if @cluster_mode
2882
+
2753
2883
  {
2754
2884
  host: @original_client.host,
2755
2885
  port: @original_client.port,
@@ -2805,14 +2935,70 @@ private
2805
2935
  }
2806
2936
 
2807
2937
  FloatifyPairs =
2808
- lambda { |array|
2809
- if array
2810
- array.each_slice(2).map do |member, score|
2811
- [member, Floatify.call(score)]
2812
- end
2938
+ lambda { |result|
2939
+ result.each_slice(2).map do |member, score|
2940
+ [member, Floatify.call(score)]
2941
+ end
2942
+ }
2943
+
2944
+ HashifyInfo =
2945
+ lambda { |reply|
2946
+ Hash[reply.split("\r\n").map do |line|
2947
+ line.split(':', 2) unless line =~ /^(#|$)/
2948
+ end.compact]
2949
+ }
2950
+
2951
+ HashifyClusterNodeInfo =
2952
+ lambda { |str|
2953
+ arr = str.split(' ')
2954
+ {
2955
+ 'node_id' => arr[0],
2956
+ 'ip_port' => arr[1],
2957
+ 'flags' => arr[2].split(','),
2958
+ 'master_node_id' => arr[3],
2959
+ 'ping_sent' => arr[4],
2960
+ 'pong_recv' => arr[5],
2961
+ 'config_epoch' => arr[6],
2962
+ 'link_state' => arr[7],
2963
+ 'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
2964
+ }
2965
+ }
2966
+
2967
+ HashifyClusterSlots =
2968
+ lambda { |reply|
2969
+ reply.map do |arr|
2970
+ first_slot, last_slot = arr[0..1]
2971
+ master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
2972
+ replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
2973
+ {
2974
+ 'start_slot' => first_slot,
2975
+ 'end_slot' => last_slot,
2976
+ 'master' => master,
2977
+ 'replicas' => replicas
2978
+ }
2813
2979
  end
2814
2980
  }
2815
2981
 
2982
+ HashifyClusterNodes =
2983
+ lambda { |reply|
2984
+ reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
2985
+ }
2986
+
2987
+ HashifyClusterSlaves =
2988
+ lambda { |reply|
2989
+ reply.map { |str| HashifyClusterNodeInfo.call(str) }
2990
+ }
2991
+
2992
+ Noop = ->(reply) { reply }
2993
+
2994
+ def _geoarguments(*args, options: nil, sort: nil, count: nil)
2995
+ args.push sort if sort
2996
+ args.push 'count', count if count
2997
+ args.push options if options
2998
+
2999
+ args.uniq
3000
+ end
3001
+
2816
3002
  def _subscription(method, timeout, channels, block)
2817
3003
  return @client.call([method] + channels) if subscribed?
2818
3004
 
@@ -2827,11 +3013,11 @@ private
2827
3013
  @client = original
2828
3014
  end
2829
3015
  end
2830
-
2831
3016
  end
2832
3017
 
2833
3018
  require_relative "redis/version"
2834
3019
  require_relative "redis/connection"
2835
3020
  require_relative "redis/client"
3021
+ require_relative "redis/cluster"
2836
3022
  require_relative "redis/pipeline"
2837
3023
  require_relative "redis/subscribe"