diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..f7a7f211 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "gems/redis/4.2/_src"] + path = gems/redis/4.2/_src + url = https://github.com/redis/redis-rb.git diff --git a/Gemfile b/Gemfile index e3f206fd..73e2d805 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source "https://rubygems.org" -gem "rbs", git: "https://github.com/ruby/rbs.git", branch: "repo" -gem "steep", git: "https://github.com/soutaro/steep.git", branch: "update-rbs" +gem "rbs" +gem "steep" gem "minitest" diff --git a/Gemfile.lock b/Gemfile.lock index 03c6690c..e9cf7c3d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,24 +1,3 @@ -GIT - remote: https://github.com/ruby/rbs.git - revision: 18635b273787ba0b8e0bd8f97ccb7a5de92b2c57 - branch: repo - specs: - rbs (0.13.1) - -GIT - remote: https://github.com/soutaro/steep.git - revision: 26bec8810e0784ddd8eb89649be84e0642924441 - branch: update-rbs - specs: - steep (0.33.0) - activesupport (>= 5.1) - ast_utils (~> 0.3.0) - language_server-protocol (~> 3.15.0.1) - listen (~> 3.0) - parser (~> 2.7.0) - rainbow (>= 2.2.2, < 4.0) - rbs (~> 0.13.0) - GEM remote: https://rubygems.org/ specs: @@ -37,7 +16,7 @@ GEM i18n (1.8.5) concurrent-ruby (~> 1.0) language_server-protocol (3.15.0.1) - listen (3.2.1) + listen (3.3.1) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) minitest (5.14.2) @@ -47,19 +26,28 @@ GEM rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) + rbs (0.17.0) + steep (0.35.0) + activesupport (>= 5.1) + ast_utils (~> 0.3.0) + language_server-protocol (~> 3.15.0.1) + listen (~> 3.0) + parser (~> 2.7.0) + rainbow (>= 2.2.2, < 4.0) + rbs (~> 0.17.0) thor (1.0.1) thread_safe (0.3.6) - tzinfo (1.2.7) + tzinfo (1.2.8) thread_safe (~> 0.1) - zeitwerk (2.4.0) + zeitwerk (2.4.1) PLATFORMS ruby DEPENDENCIES minitest - rbs! - steep! + rbs + steep BUNDLED WITH 2.1.4 diff --git a/gems/redis/4.2/_scripts/test b/gems/redis/4.2/_scripts/test new file mode 100755 index 00000000..c7cb37b9 --- /dev/null +++ b/gems/redis/4.2/_scripts/test @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -v + +RBS_DIR=$(cd $(dirname $0)/..; pwd) +REPO_DIR=$(cd $(dirname $0)/../../..; pwd) +GEM=redis:4.2.0 + +bundle exec rbs --repo $REPO_DIR -r $GEM validate + +cd ${RBS_DIR}/_test +bundle exec steep check diff --git a/gems/redis/4.2/_src b/gems/redis/4.2/_src new file mode 160000 index 00000000..cf44d716 --- /dev/null +++ b/gems/redis/4.2/_src @@ -0,0 +1 @@ +Subproject commit cf44d716aa36f92556de7be1c2f4af4c6d750c92 diff --git a/gems/redis/4.2/_test/Steepfile b/gems/redis/4.2/_test/Steepfile new file mode 100644 index 00000000..9f944c26 --- /dev/null +++ b/gems/redis/4.2/_test/Steepfile @@ -0,0 +1,8 @@ +target :test do + check "." + + repo_path "../../../" + library "redis:4.2.0" + + typing_options :strict +end diff --git a/gems/redis/4.2/_test/alternate_drivers.rb b/gems/redis/4.2/_test/alternate_drivers.rb new file mode 100644 index 00000000..b51d5acf --- /dev/null +++ b/gems/redis/4.2/_test/alternate_drivers.rb @@ -0,0 +1,3 @@ +redis = Redis.new(:driver => :hiredis) + +redis = Redis.new(:driver => :synchrony) diff --git a/gems/redis/4.2/_test/cluster.rb b/gems/redis/4.2/_test/cluster.rb new file mode 100644 index 00000000..7b7937f8 --- /dev/null +++ b/gems/redis/4.2/_test/cluster.rb @@ -0,0 +1,10 @@ +# @type var nodes: Array[Redis::node] + +nodes = (7000..7005).map { |port| "redis://127.0.0.1:#{port}" } +redis = Redis.new(cluster: nodes) + +nodes = (7000..7005).map do |port| + # @type block: Redis::node + { host: '127.0.0.1', port: port } +end +redis = Redis.new(cluster: nodes) diff --git a/gems/redis/4.2/_test/error_handling.rb b/gems/redis/4.2/_test/error_handling.rb new file mode 100644 index 00000000..2f734fd6 --- /dev/null +++ b/gems/redis/4.2/_test/error_handling.rb @@ -0,0 +1,11 @@ +redis = Redis.new + +begin + redis.ping +rescue StandardError => e + e.inspect +# => # + + e.message +# => Timed out connecting to Redis on 10.0.1.1:6380 +end diff --git a/gems/redis/4.2/_test/expert-mode-options.rb b/gems/redis/4.2/_test/expert-mode-options.rb new file mode 100644 index 00000000..d64590f3 --- /dev/null +++ b/gems/redis/4.2/_test/expert-mode-options.rb @@ -0,0 +1 @@ +Redis.new(inherit_socket: true) diff --git a/gems/redis/4.2/_test/get_set.rb b/gems/redis/4.2/_test/get_set.rb new file mode 100644 index 00000000..90794844 --- /dev/null +++ b/gems/redis/4.2/_test/get_set.rb @@ -0,0 +1,7 @@ +redis = Redis.new + +redis.set("mykey", "hello world") +# => "OK" + +redis.get("mykey") +# => "hello world" diff --git a/gems/redis/4.2/_test/initialize.rb b/gems/redis/4.2/_test/initialize.rb new file mode 100644 index 00000000..83ee03ba --- /dev/null +++ b/gems/redis/4.2/_test/initialize.rb @@ -0,0 +1,9 @@ +redis = Redis.new + +redis = Redis.new(host: "10.0.1.1", port: 6380, db: 15) + +redis = Redis.new(url: "redis://:p4ssw0rd@10.0.1.1:6380/15") + +redis = Redis.new(path: "/tmp/redis.sock") + +redis = Redis.new(password: "mysecret") diff --git a/gems/redis/4.2/_test/mset_mget.rb b/gems/redis/4.2/_test/mset_mget.rb new file mode 100644 index 00000000..cbd75f85 --- /dev/null +++ b/gems/redis/4.2/_test/mset_mget.rb @@ -0,0 +1,7 @@ +redis = Redis.new(cluster: %w[redis://127.0.0.1:7000]) + +redis.mget('key1', 'key2') +#=> Redis::CommandError (CROSSSLOT Keys in request don't hash to the same slot) + +redis.mget('{key}1', '{key}2') +#=> [nil, nil] diff --git a/gems/redis/4.2/_test/multi.rb b/gems/redis/4.2/_test/multi.rb new file mode 100644 index 00000000..7cf1881a --- /dev/null +++ b/gems/redis/4.2/_test/multi.rb @@ -0,0 +1,7 @@ +redis = Redis.new + +redis.multi do + redis.set "foo", "bar" + redis.incr "baz" +end +# => ["OK", 1] diff --git a/gems/redis/4.2/_test/pipeline.rb b/gems/redis/4.2/_test/pipeline.rb new file mode 100644 index 00000000..5d0dae72 --- /dev/null +++ b/gems/redis/4.2/_test/pipeline.rb @@ -0,0 +1,16 @@ +redis = Redis.new + +# @type ivar @set: Redis::Future[String] +# @type ivar @incr: Redis::Future[Integer] + +redis.pipelined do |redis| + @set = redis.set "foo", "bar" + @incr = redis.incr "baz" +end +# => ["OK", 1] + +@set.value +# => "OK" + +@incr.value +# => 1 diff --git a/gems/redis/4.2/_test/reconnect.rb b/gems/redis/4.2/_test/reconnect.rb new file mode 100644 index 00000000..b4dbdabd --- /dev/null +++ b/gems/redis/4.2/_test/reconnect.rb @@ -0,0 +1,5 @@ +Redis.new( + :reconnect_attempts => 10, + :reconnect_delay => 1.5, + :reconnect_delay_max => 10.0, +) diff --git a/gems/redis/4.2/_test/sentinels.rb b/gems/redis/4.2/_test/sentinels.rb new file mode 100644 index 00000000..ba4ce993 --- /dev/null +++ b/gems/redis/4.2/_test/sentinels.rb @@ -0,0 +1,5 @@ +redis = Redis.new( + url: "redis://mymaster", + sentinels: [{ host: "127.0.0.1", port: 26380 }, { host: "127.0.0.1", port: 26381, password: "mysecret" }], + role: :master +) diff --git a/gems/redis/4.2/_test/ssl.rb b/gems/redis/4.2/_test/ssl.rb new file mode 100644 index 00000000..9333a30f --- /dev/null +++ b/gems/redis/4.2/_test/ssl.rb @@ -0,0 +1,21 @@ +# @type const OpenSSL: untyped +# @type const OpenSSL::X509: untyped +# @type const OpenSSL::X509::Certificate: untyped +# @type const OpenSSL::PKey::RSA: untyped +# @type const OpenSSL::PKey: untyped + +redis = Redis.new( + :url => "rediss://:p4ssw0rd@10.0.1.1:6381/15", + :ssl_params => { + :ca_file => "/path/to/ca.crt" + } +) + +redis = Redis.new( + :url => "rediss://:p4ssw0rd@10.0.1.1:6381/15", + :ssl_params => { + :ca_file => "/path/to/ca.crt", + :cert => OpenSSL::X509::Certificate.new(File.read("client.crt")), + :key => OpenSSL::PKey::RSA.new(File.read("client.key")) + } +) diff --git a/gems/redis/4.2/_test/timeout.rb b/gems/redis/4.2/_test/timeout.rb new file mode 100644 index 00000000..c5b108a8 --- /dev/null +++ b/gems/redis/4.2/_test/timeout.rb @@ -0,0 +1,7 @@ +Redis.new(:timeout => 1) + +Redis.new( + :connect_timeout => 0.2, + :read_timeout => 1.0, + :write_timeout => 0.5 +) diff --git a/gems/redis/4.2/future.rbs b/gems/redis/4.2/future.rbs new file mode 100644 index 00000000..c7884b45 --- /dev/null +++ b/gems/redis/4.2/future.rbs @@ -0,0 +1,5 @@ +class Redis + class Future[out T] + attr_reader value: T + end +end diff --git a/gems/redis/4.2/redis.rbs b/gems/redis/4.2/redis.rbs new file mode 100644 index 00000000..9a2921cc --- /dev/null +++ b/gems/redis/4.2/redis.rbs @@ -0,0 +1,201 @@ +class Redis + type time = Integer | Float + + type sentinel = { host: String, port: Integer?, password: String? } + + type node = String + | { host: String?, port: Integer?, db: Integer?, password: String?, path: String?, url: String? } + + # Create a new client instance + # + # @param [Hash] options + # @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection: + # `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket + # connection: `unix://[path to Redis socket]`. This overrides all other options. + # @option options [String] :host ("127.0.0.1") server hostname + # @option options [Integer] :port (6379) server port + # @option options [String] :path path to server socket (overrides host and port) + # @option options [Float] :timeout (5.0) timeout in seconds + # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds + # @option options [String] :password Password to authenticate against server + # @option options [Integer] :db (0) Database to select after initial connect + # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony` + # @option options [String] :id ID for the client connection, assigns name to current connection by sending + # `CLIENT SETNAME` + # @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated + # based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer + # @option options [Integer] :reconnect_attempts Number of attempts trying to connect + # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not + # @option options [Array] :sentinels List of sentinels to contact + # @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave` + # @option options [Array String, Integer}>] :cluster List of cluster nodes to contact + # @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not + # @option options [Class] :connector Class of custom connector + # + # @return [Redis] a new client instance + def initialize: (?url: String, + ?path: String, + ?host: String, + ?port: Integer, + ?db: Integer, + ?path: String, + ?sentinels: Array[sentinel], + ?role: :master | :slave, + ?cluster: Array[node], + ?repliace: boolish, + ?timeout: time, + ?connect_timeout: time, + ?read_timeout: time, + ?write_timeout: time, + ?reconnect_attempts: Integer, + ?reconnect_delay: Float, + ?reconnect_delay_max: Float, + ?password: String, + ?driver: Symbol, + ?inherit_socket: boolish, + ?ssl_params: Hash[Symbol, untyped]) -> void + + # Set the string value of a key. + # + # @param [String] key + # @param [String] value + # @param [Hash] options + # - `:ex => Integer`: Set the specified expire time, in seconds. + # - `:px => Integer`: Set the specified expire time, in milliseconds. + # - `:nx => true`: Only set the key if it does not already exist. + # - `:xx => true`: Only set the key if it already exist. + # - `:keepttl => true`: Retain the time to live associated with the key. + # @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true` + def set: (String key, top value, ?ex: Integer?, ?px: Integer?, ?nx: boolish, ?xx: boolish, ?keepttl: boolish) -> (String | bool) + + # Get the value of a key. + # + # @param [String] key + # @return [String] + def get: (String key) -> String? + + + # Set one or more values. + # + # @example + # redis.mset("key1", "v1", "key2", "v2") + # # => "OK" + # + # @param [Array] args array of keys and values + # @return [String] `"OK"` + # + # @see #mapped_mset + def mset: (*String args) -> String + + # Get the values of all the given keys. + # + # @example + # redis.mget("key1", "key2") + # # => ["v1", "v2"] + # + # @param [Array] keys + # @return [Array] an array of values for the specified keys + # + # @see #mapped_mget + def mget: (*String keys) -> Array[String?] + | [T] (*String keys) { (Array[String?]) -> T } -> T + + + # Decrement the integer value of a key by one. + # + # @example + # redis.decr("value") + # # => 4 + # + # @param [String] key + # @return [Integer] value after decrementing it + def decr: (String key) -> Integer + + # Decrement the integer value of a key by the given number. + # + # @example + # redis.decrby("value", 5) + # # => 0 + # + # @param [String] key + # @param [Integer] decrement + # @return [Integer] value after decrementing it + def decrby: (String key, Integer decrement) -> Integer + + # Increment the integer value of a key by one. + # + # @example + # redis.incr("value") + # # => 6 + # + # @param [String] key + # @return [Integer] value after incrementing it + def incr: (String key) -> Integer + + # Increment the integer value of a key by the given integer number. + # + # @example + # redis.incrby("value", 5) + # # => 10 + # + # @param [String] key + # @param [Integer] increment + # @return [Integer] value after incrementing it + def incrby: (String key, Integer increment) -> Integer + + interface _Pipelined + def get: (String key) -> Future[String?] + + def set: (String key, top value, ?ex: Integer?, ?px: Integer?, ?nx: boolish, ?xx: boolish, ?keepttl: boolish) -> Future[String] + + def decr: (String key) -> Future[Integer] + + def decrby: (String key, Integer decrement) -> Future[Integer] + + def incr: (String key) -> Future[Integer] + + def incrby: (String key, Integer increment) -> Future[Integer] + end + + def pipelined: () { (self & _Pipelined) -> void } -> Array[String | Integer] + + # Mark the start of a transaction block. + # + # Passing a block is optional. + # + # @example With a block + # redis.multi do |multi| + # multi.set("key", "value") + # multi.incr("counter") + # end # => ["OK", 6] + # + # @example Without a block + # redis.multi + # # => "OK" + # redis.set("key", "value") + # # => "QUEUED" + # redis.incr("counter") + # # => "QUEUED" + # redis.exec + # # => ["OK", 6] + # + # @yield [multi] the commands that are called inside this block are cached + # and written to the server upon returning from it + # @yieldparam [Redis] multi `self` + # + # @return [String, Array<...>] + # - when a block is not given, `OK` + # - when a block is given, an array with replies + # + # @see #watch + # @see #unwatch + def multi: () -> String + | () { (self) -> void } -> Array[String | Integer] + + # Ping the server. + # + # @param [optional, String] message + # @return [String] `PONG` + def ping: (?String? message) -> String + +end