@@ -31,12 +31,16 @@ def self.current=(redis)
3131  # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not 
3232  # @option options [Array] :sentinels List of sentinels to contact 
3333  # @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 
3436  # @option options [Class] :connector Class of custom connector 
3537  # 
3638  # @return [Redis] a new client instance 
3739  def  initialize ( options  =  { } ) 
3840    @options  =  options . dup 
39-     @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 ) 
4044    @queue  =  Hash . new  {  |h ,  k | h [ k ]  =  [ ]  } 
4145
4246    super ( )  # Monitor#initialize 
@@ -274,9 +278,7 @@ def info(cmd = nil)
274278    synchronize  do  |client |
275279      client . call ( [ :info ,  cmd ] . compact )  do  |reply |
276280        if  reply . kind_of? ( String ) 
277-           reply  =  Hash [ reply . split ( "\r \n " ) . map  do  |line |
278-             line . split ( ":" ,  2 )  unless  line  =~ /^(#|$)/ 
279-           end . compact ] 
281+           reply  =  HashifyInfo . call ( reply ) 
280282
281283          if  cmd  && cmd . to_s  == "commandstats" 
282284            # Extract nested hashes for INFO COMMANDSTATS 
@@ -2818,6 +2820,41 @@ def sentinel(subcommand, *args)
28182820    end 
28192821  end 
28202822
2823+   # Sends `CLUSTER *` command to random node and returns its reply. 
2824+   # 
2825+   # @see https://redis.io/commands#cluster Reference of cluster command 
2826+   # 
2827+   # @param subcommand [String, Symbol] the subcommand of cluster command 
2828+   #   e.g. `:slots`, `:nodes`, `:slaves`, `:info` 
2829+   # 
2830+   # @return [Object] depends on the subcommand 
2831+   def  cluster ( subcommand ,  *args ) 
2832+     subcommand  =  subcommand . to_s . downcase 
2833+     block  =  case  subcommand 
2834+             when  'slots'   then  HashifyClusterSlots 
2835+             when  'nodes'   then  HashifyClusterNodes 
2836+             when  'slaves'  then  HashifyClusterSlaves 
2837+             when  'info'    then  HashifyInfo 
2838+             else  Noop 
2839+             end 
2840+ 
2841+     # @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected 
2842+     block  =  Noop  unless  @cluster_mode 
2843+ 
2844+     synchronize  do  |client |
2845+       client . call ( [ :cluster ,  subcommand ]  + args ,  &block ) 
2846+     end 
2847+   end 
2848+ 
2849+   # Sends `ASKING` command to random node and returns its reply. 
2850+   # 
2851+   # @see https://redis.io/topics/cluster-spec#ask-redirection ASK redirection 
2852+   # 
2853+   # @return [String] `'OK'` 
2854+   def  asking 
2855+     synchronize  {  |client | client . call ( %i[ asking ] )  } 
2856+   end 
2857+ 
28212858  def  id 
28222859    @original_client . id 
28232860  end 
@@ -2831,6 +2868,8 @@ def dup
28312868  end 
28322869
28332870  def  connection 
2871+     return  @original_client . connection_info  if  @cluster_mode 
2872+ 
28342873    { 
28352874      host :     @original_client . host , 
28362875      port :     @original_client . port , 
@@ -2896,6 +2935,56 @@ def method_missing(command, *args)
28962935      end 
28972936    } 
28982937
2938+   HashifyInfo  = 
2939+     lambda  {  |reply |
2940+       Hash [ reply . split ( "\r \n " ) . map  do  |line |
2941+         line . split ( ':' ,  2 )  unless  line  =~ /^(#|$)/ 
2942+       end . compact ] 
2943+     } 
2944+ 
2945+   HashifyClusterNodeInfo  = 
2946+     lambda  {  |str |
2947+       arr  =  str . split ( ' ' ) 
2948+       { 
2949+         'node_id'         =>  arr [ 0 ] , 
2950+         'ip_port'         =>  arr [ 1 ] , 
2951+         'flags'           =>  arr [ 2 ] . split ( ',' ) , 
2952+         'master_node_id'  =>  arr [ 3 ] , 
2953+         'ping_sent'       =>  arr [ 4 ] , 
2954+         'pong_recv'       =>  arr [ 5 ] , 
2955+         'config_epoch'    =>  arr [ 6 ] , 
2956+         'link_state'      =>  arr [ 7 ] , 
2957+         'slots'           =>  arr [ 8 ] . nil?  ? nil  : Range . new ( *arr [ 8 ] . split ( '-' ) ) 
2958+       } 
2959+     } 
2960+ 
2961+   HashifyClusterSlots  = 
2962+     lambda  {  |reply |
2963+       reply . map  do  |arr |
2964+         first_slot ,  last_slot  =  arr [ 0 ..1 ] 
2965+         master  =  {  'ip'  =>  arr [ 2 ] [ 0 ] ,  'port'  =>  arr [ 2 ] [ 1 ] ,  'node_id'  =>  arr [ 2 ] [ 2 ]  } 
2966+         replicas  =  arr [ 3 ..-1 ] . map  {  |r | {  'ip'  =>  r [ 0 ] ,  'port'  =>  r [ 1 ] ,  'node_id'  =>  r [ 2 ]  }  } 
2967+         { 
2968+           'start_slot'  =>  first_slot , 
2969+           'end_slot'    =>  last_slot , 
2970+           'master'      =>  master , 
2971+           'replicas'    =>  replicas 
2972+         } 
2973+       end 
2974+     } 
2975+ 
2976+   HashifyClusterNodes  = 
2977+     lambda  {  |reply |
2978+       reply . split ( /[\r \n ]+/ ) . map  {  |str | HashifyClusterNodeInfo . call ( str )  } 
2979+     } 
2980+ 
2981+   HashifyClusterSlaves  = 
2982+     lambda  {  |reply |
2983+       reply . map  {  |str | HashifyClusterNodeInfo . call ( str )  } 
2984+     } 
2985+ 
2986+   Noop  =  -> ( reply )  {  reply  } 
2987+ 
28992988  def  _geoarguments ( *args ,  options : nil ,  sort : nil ,  count : nil ) 
29002989    args . push  sort  if  sort 
29012990    args . push  'count' ,  count  if  count 
@@ -2918,11 +3007,11 @@ def _subscription(method, timeout, channels, block)
29183007      @client  =  original 
29193008    end 
29203009  end 
2921- 
29223010end 
29233011
29243012require_relative  "redis/version" 
29253013require_relative  "redis/connection" 
29263014require_relative  "redis/client" 
3015+ require_relative  "redis/cluster" 
29273016require_relative  "redis/pipeline" 
29283017require_relative  "redis/subscribe" 
0 commit comments