From 667c889fcf35b5e5a7aed1350592fe7991d630e0 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Fri, 13 Nov 2020 19:30:47 +0900 Subject: [PATCH 1/3] Add retryable - https://rubygems.org/gems/retryable - https://github.com/nfedyashev/retryable --- Gemfile | 4 +- Gemfile.lock | 20 ++++----- gems/retryable/3.0/_test/Steepfile | 9 ++++ gems/retryable/3.0/_test/test.rb | 26 +++++++++++ gems/retryable/3.0/configuration.rbs | 64 ++++++++++++++++++++++++++++ gems/retryable/3.0/retryable.rbs | 45 +++++++++++++++++++ gems/retryable/3.0/version.rbs | 16 +++++++ 7 files changed, 172 insertions(+), 12 deletions(-) create mode 100644 gems/retryable/3.0/_test/Steepfile create mode 100644 gems/retryable/3.0/_test/test.rb create mode 100644 gems/retryable/3.0/configuration.rbs create mode 100644 gems/retryable/3.0/retryable.rbs create mode 100644 gems/retryable/3.0/version.rbs diff --git a/Gemfile b/Gemfile index e3f206fd..5d9a18db 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", git: "https://github.com/ruby/rbs.git", branch: "master" +gem "steep", git: "https://github.com/soutaro/steep.git", branch: "master" gem "minitest" diff --git a/Gemfile.lock b/Gemfile.lock index 03c6690c..7a873ce5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,23 +1,23 @@ GIT remote: https://github.com/ruby/rbs.git - revision: 18635b273787ba0b8e0bd8f97ccb7a5de92b2c57 - branch: repo + revision: 219bd9cce48706894ed0de9e82a94af93ce28846 + branch: master specs: - rbs (0.13.1) + rbs (0.16.0) GIT remote: https://github.com/soutaro/steep.git - revision: 26bec8810e0784ddd8eb89649be84e0642924441 - branch: update-rbs + revision: 2928ed22d01ff89671d90876c4660499e5ed3f8c + branch: master specs: - steep (0.33.0) + steep (0.34.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) + rbs (~> 0.16.0) GEM remote: https://rubygems.org/ @@ -37,7 +37,7 @@ GEM i18n (1.8.5) concurrent-ruby (~> 1.0) language_server-protocol (3.15.0.1) - listen (3.2.1) + listen (3.3.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) minitest (5.14.2) @@ -49,9 +49,9 @@ GEM ffi (~> 1.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 diff --git a/gems/retryable/3.0/_test/Steepfile b/gems/retryable/3.0/_test/Steepfile new file mode 100644 index 00000000..e5fc6e2b --- /dev/null +++ b/gems/retryable/3.0/_test/Steepfile @@ -0,0 +1,9 @@ +target :test do + check "." + + repo_path "../../../" + library "retryable" + library "forwardable" + + typing_options :strict +end diff --git a/gems/retryable/3.0/_test/test.rb b/gems/retryable/3.0/_test/test.rb new file mode 100644 index 00000000..8a1ccc50 --- /dev/null +++ b/gems/retryable/3.0/_test/test.rb @@ -0,0 +1,26 @@ +require "retryable" + +Retryable.retryable do + # ... +end + +Retryable.configuration + +Retryable.configure do |config| + config.contexts = {} + config.ensure = ->(retries) { puts "retries: #{retries}" } + config.exception_cb = ->(exception) { puts exception.message } + config.log_method = ->(retries, exception) { puts "retries: #{retries}, exception=#{exception.message}" } + config.matching = /foo/ + config.matching = "bar" + config.matching = [/foo/, "bar"] + config.not = ArgumentError + config.not = [ArgumentError] + config.on = ArgumentError + config.on = [ArgumentError] + config.sleep = 1 + config.sleep = 0.5 + config.sleep = ->(retries) { retries ** 2 } + config.sleep_method = ->(n) { sleep(n.to_int + 1.5) } + config.tries = 3 +end diff --git a/gems/retryable/3.0/configuration.rbs b/gems/retryable/3.0/configuration.rbs new file mode 100644 index 00000000..cc1547d7 --- /dev/null +++ b/gems/retryable/3.0/configuration.rbs @@ -0,0 +1,64 @@ +module Retryable + # Used to set up and modify settings for the retryable. + class Configuration + type option_key = :contexts + | :ensure + | :exception_cb + | :log_method + | :matching + | :not + | :on + | :sleep + | :sleep_method + | :tries + + type options = { + contexts: Hash[Symbol, options], + ensure: ^(Integer) -> void, + exception_cb: ^(Exception) -> void, + log_method: ^(Integer, Exception) -> void, + matching: (String | Regexp) | Array[String | Regexp], + not: _Exception | Array[_Exception], + on: _Exception | Array[_Exception], + sleep: real | (^(real) -> void), + sleep_method: ^(real) -> void, + tries: Integer + } + + VALID_OPTION_KEYS: Array[option_key] + + attr_accessor contexts: Hash[Symbol, options] + attr_accessor ensure: ^(Integer) -> void + attr_accessor exception_cb: ^(Exception) -> void + attr_accessor log_method: ^(Integer, Exception) -> void + attr_accessor matching: (String | Regexp) | Array[String | Regexp] + attr_accessor not: _Exception | Array[_Exception] + attr_accessor on: _Exception | Array[_Exception] + attr_accessor sleep: real | (^(real) -> void) + attr_accessor sleep_method: ^(real) -> void + attr_accessor tries: Integer + + attr_accessor enabled: bool + + def initialize: () -> void + + def enable: () -> void + + alias enabled? enabled + + def disable: () -> void + + # Allows config options to be read like a hash + # + # @param [Symbol] option Key for a given attribute + def []: (option_key option) -> untyped + + # Returns a hash of all configurable options + def to_hash: () -> options + + # Returns a hash of all configurable options merged with +hash+ + # + # @param [Hash] hash A set of configuration options that will take precedence over the defaults + def merge: (options hash) -> options + end +end diff --git a/gems/retryable/3.0/retryable.rbs b/gems/retryable/3.0/retryable.rbs new file mode 100644 index 00000000..e18831e8 --- /dev/null +++ b/gems/retryable/3.0/retryable.rbs @@ -0,0 +1,45 @@ +# Runs a code block, and retries it when an exception occurs. It's great when working with flakey webservices (for example). +module Retryable + extend Forwardable + + # A Retryable configuration object. Must act like a hash and return sensible + # values for all Retryable configuration options. See Retryable::Configuration. + attr_writer configuration: Configuration + + # Call this method to modify defaults in your initializers. + # + # @example + # Retryable.configure do |config| + # config.contexts = {} + # config.ensure = proc {} + # config.exception_cb = proc {} + # config.log_method = proc {} + # config.matching = /.*/ + # config.not = [] + # config.on = StandardError + # config.sleep = 1 + # config.sleep_method = ->(seconds) { Kernel.sleep(seconds) } + # config.tries = 2 + # end + def self.configure: () { (Configuration) -> void } -> void + + # The configuration object. + # @see Retryable.configure + def self.configuration: () -> Configuration + + def self.enabled?: () -> bool + def self.enable: () -> void + def self.disable: () -> void + + def self.with_context: [X] (Symbol context_key, ?Configuration::options options) { () -> X } -> X + + alias self.retryable_with_context self.with_context + + def self.retryable: [X] (?Configuration::options options) { (Integer, Exception) -> X } -> X? + + private + + def self.check_for_invalid_options: (Configuration::options custom_options, Configuration::options default_options) -> void + + def self.matches?: (String message, Array[String | Regexp] candidates) -> bool +end diff --git a/gems/retryable/3.0/version.rbs b/gems/retryable/3.0/version.rbs new file mode 100644 index 00000000..fd9d3087 --- /dev/null +++ b/gems/retryable/3.0/version.rbs @@ -0,0 +1,16 @@ +module Retryable + # This module holds the Retryable version information. + module Version + def major: () -> Integer + + def minor: () -> Integer + + def patch: () -> Integer + + def to_h: () -> { major: Integer, minor: Integer, patch: Integer } + + def to_a: () -> Array[Integer] + + def to_s: () -> String + end +end From 2d4557723d51655f096a1bb658edf334fd704ff5 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 14 Nov 2020 22:16:14 +0900 Subject: [PATCH 2/3] Add test case --- gems/retryable/3.0/_test/test.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/gems/retryable/3.0/_test/test.rb b/gems/retryable/3.0/_test/test.rb index 8a1ccc50..b54c29f9 100644 --- a/gems/retryable/3.0/_test/test.rb +++ b/gems/retryable/3.0/_test/test.rb @@ -1,7 +1,11 @@ require "retryable" Retryable.retryable do - # ... + puts "foo" +end + +Retryable.retryable(tries: 3, on: ArgumentError) do + puts "foo" end Retryable.configuration From 0b1e92484aaf85ec54f4fb2b16fa76d7b8cf1b68 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Mon, 16 Nov 2020 02:31:57 +0900 Subject: [PATCH 3/3] Fix retryable RBS and test --- .gitmodules | 3 ++ gems/retryable/3.0/_src | 1 + gems/retryable/3.0/_test/Steepfile | 2 +- gems/retryable/3.0/_test/test.rb | 68 ++++++++++++++++++++++++---- gems/retryable/3.0/configuration.rbs | 22 ++++----- gems/retryable/3.0/retryable.rbs | 6 +-- 6 files changed, 79 insertions(+), 23 deletions(-) create mode 160000 gems/retryable/3.0/_src diff --git a/.gitmodules b/.gitmodules index f7a7f211..a44ca75b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "gems/redis/4.2/_src"] path = gems/redis/4.2/_src url = https://github.com/redis/redis-rb.git +[submodule "gems/retryable/3.0/_src"] + path = gems/retryable/3.0/_src + url = https://github.com/nfedyashev/retryable.git diff --git a/gems/retryable/3.0/_src b/gems/retryable/3.0/_src new file mode 160000 index 00000000..aa51cbd7 --- /dev/null +++ b/gems/retryable/3.0/_src @@ -0,0 +1 @@ +Subproject commit aa51cbd7ea570e223161571fe97eb8ca7ce8a535 diff --git a/gems/retryable/3.0/_test/Steepfile b/gems/retryable/3.0/_test/Steepfile index e5fc6e2b..edfcc500 100644 --- a/gems/retryable/3.0/_test/Steepfile +++ b/gems/retryable/3.0/_test/Steepfile @@ -2,7 +2,7 @@ target :test do check "." repo_path "../../../" - library "retryable" + library "retryable:3.0" library "forwardable" typing_options :strict diff --git a/gems/retryable/3.0/_test/test.rb b/gems/retryable/3.0/_test/test.rb index b54c29f9..36399a23 100644 --- a/gems/retryable/3.0/_test/test.rb +++ b/gems/retryable/3.0/_test/test.rb @@ -1,20 +1,61 @@ require "retryable" -Retryable.retryable do - puts "foo" +Retryable.retryable(tries: 0) + +str = Retryable.retryable do + "foo" end +str&.ascii_only? -Retryable.retryable(tries: 3, on: ArgumentError) do +Retryable.retryable do |retries, exception| + retries.bit_length + exception&.backtrace +end + +Retryable.retryable( + ensure: ->(retries) { retries.bit_length }, + exception_cb: ->(exception) { exception.backtrace }, + log_method: ->(retries, exception) { [retries.bit_length, exception.backtrace] }, + matching: [/foo/, "bar"], + not: [ArgumentError], + on: StandardError, + sleep: 10, + sleep_method: ->(n) { n ** 1 }, + tries: 3 +) do puts "foo" end -Retryable.configuration +Retryable.configuration.tap do |config| + config.contexts.keys + config.ensure.call(1) + config.exception_cb.call(RuntimeError.new) + config.log_method.call(1, RuntimeError.new) + config.matching + config.not + config.on + config.sleep + config.tries.integer? + config.enabled + config.enabled? + config.enable + config.disable + config[:tries] + config.to_hash + config.merge({ sleep: 1 }) +end + +Retryable.configuration = Retryable.configuration Retryable.configure do |config| - config.contexts = {} - config.ensure = ->(retries) { puts "retries: #{retries}" } - config.exception_cb = ->(exception) { puts exception.message } - config.log_method = ->(retries, exception) { puts "retries: #{retries}, exception=#{exception.message}" } + config.contexts[:foo] = { + on: [ArgumentError], + sleep: 10, + tries: 5 + } + config.ensure = ->(retries) { retries.bit_length } + config.exception_cb = ->(exception) { exception.backtrace } + config.log_method = ->(retries, exception) { [retries.bit_length, exception&.backtrace] } config.matching = /foo/ config.matching = "bar" config.matching = [/foo/, "bar"] @@ -28,3 +69,14 @@ config.sleep_method = ->(n) { sleep(n.to_int + 1.5) } config.tries = 3 end + +Retryable.with_context(:foo) +Retryable.with_context(:foo, { sleep: 10 }) +str = Retryable.with_context(:foo, { sleep: 10, tries: 3 }) do |retries, exception| + retries.bit_length + exception&.backtrace + "foo" +end +str&.ascii_only? + +Retryable.retryable_with_context(:faulty_service) diff --git a/gems/retryable/3.0/configuration.rbs b/gems/retryable/3.0/configuration.rbs index cc1547d7..8d2a87fa 100644 --- a/gems/retryable/3.0/configuration.rbs +++ b/gems/retryable/3.0/configuration.rbs @@ -13,16 +13,16 @@ module Retryable | :tries type options = { - contexts: Hash[Symbol, options], - ensure: ^(Integer) -> void, - exception_cb: ^(Exception) -> void, - log_method: ^(Integer, Exception) -> void, - matching: (String | Regexp) | Array[String | Regexp], - not: _Exception | Array[_Exception], - on: _Exception | Array[_Exception], - sleep: real | (^(real) -> void), - sleep_method: ^(real) -> void, - tries: Integer + contexts: Hash[Symbol, options] | nil, + ensure: (^(Integer) -> void) | nil, + exception_cb: (^(Exception) -> void) | nil, + log_method: (^(Integer, Exception?) -> void) | nil, + matching: (String | Regexp) | Array[String | Regexp] | nil, + not: _Exception | Array[_Exception] | nil, + on: _Exception | Array[_Exception] | nil, + sleep: real | (^(real) -> void) | nil, + sleep_method: (^(real) -> void) | nil, + tries: Integer | nil } VALID_OPTION_KEYS: Array[option_key] @@ -30,7 +30,7 @@ module Retryable attr_accessor contexts: Hash[Symbol, options] attr_accessor ensure: ^(Integer) -> void attr_accessor exception_cb: ^(Exception) -> void - attr_accessor log_method: ^(Integer, Exception) -> void + attr_accessor log_method: ^(Integer, Exception?) -> void attr_accessor matching: (String | Regexp) | Array[String | Regexp] attr_accessor not: _Exception | Array[_Exception] attr_accessor on: _Exception | Array[_Exception] diff --git a/gems/retryable/3.0/retryable.rbs b/gems/retryable/3.0/retryable.rbs index e18831e8..1b107098 100644 --- a/gems/retryable/3.0/retryable.rbs +++ b/gems/retryable/3.0/retryable.rbs @@ -4,7 +4,7 @@ module Retryable # A Retryable configuration object. Must act like a hash and return sensible # values for all Retryable configuration options. See Retryable::Configuration. - attr_writer configuration: Configuration + def self.configuration=: (Configuration) -> void # Call this method to modify defaults in your initializers. # @@ -31,11 +31,11 @@ module Retryable def self.enable: () -> void def self.disable: () -> void - def self.with_context: [X] (Symbol context_key, ?Configuration::options options) { () -> X } -> X + def self.with_context: [X] (Symbol context_key, ?Configuration::options options) ?{ (Integer, Exception?) -> X } -> X? alias self.retryable_with_context self.with_context - def self.retryable: [X] (?Configuration::options options) { (Integer, Exception) -> X } -> X? + def self.retryable: [X] (?Configuration::options options) ?{ (Integer, Exception?) -> X } -> X? private