take2 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 59388477e42afe74b6c90644d81827c912f32009
4
+ data.tar.gz: b90cad82f9cbede51323717710e8221858c75bd7
5
+ SHA512:
6
+ metadata.gz: e0fd246fd3e70ce50675a0c4356cc183e2af51dde8b70ec69f30c9e5124e18d70a83e2352cd3ebe03fe6e27df0876cb04c7737ab709ada1d9c1c07e3e5055bf3
7
+ data.tar.gz: 73016bec098e375b5d055999e8ca6d51972c11c7188671d554039676201f23e8ce0085a27c3d05191fa6aeb74d749f6332e29b9fe03a69cf0d9084e8822a1e25
data/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+ gemspec
3
+
4
+ gem "take2", :path => File.expand_path("..", __FILE__)
5
+
6
+ gem "rake"
7
+
8
+ group :test do
9
+ gem "rspec", "3.8.0"
10
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Restaurant Cheetah
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # take2
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ begin
2
+ require 'rspec/core/rake_task'
3
+ RSpec::Core::RakeTask.new(:spec)
4
+ rescue LoadError
5
+ end
6
+
7
+ task default: [:spec]
data/lib/take2.rb ADDED
@@ -0,0 +1,109 @@
1
+ require 'net/http'
2
+
3
+ require 'take2/version'
4
+ require 'take2/configuration'
5
+
6
+ module Take2
7
+
8
+ def self.included(base)
9
+ base.extend ClassMethods
10
+ base.send :set_defaults
11
+ base.send :include, InstanceMethods
12
+ end
13
+
14
+ class << self
15
+ attr_accessor :configuration
16
+ end
17
+
18
+ def self.configuration
19
+ @configuration ||= Configuration.new
20
+ end
21
+
22
+ def self.reset(options = {})
23
+ @configuration = Configuration.new(options)
24
+ end
25
+
26
+ def self.configure
27
+ yield(configuration) if block_given?
28
+ end
29
+
30
+ module InstanceMethods
31
+
32
+ def call_api_with_retry
33
+ config = self.class.retriable_configuration
34
+ tries ||= config[:retries]
35
+ begin
36
+ yield
37
+ rescue => e
38
+ if config[:retriable].map {|klass| e.class <= klass }.any?
39
+ unless tries.zero? || config[:retry_condition_proc]&.call(e)
40
+ config[:retry_proc]&.call(e, tries)
41
+ sleep(config[:time_to_sleep]) if config[:time_to_sleep]
42
+ tries -= 1
43
+ retry
44
+ end
45
+ end
46
+ log_error e
47
+ raise e
48
+ end
49
+ end
50
+
51
+ def log_error(error)
52
+ # Overrider this method in the includer
53
+ true
54
+ end
55
+
56
+ end
57
+
58
+ module ClassMethods
59
+
60
+ def number_of_retries(num)
61
+ self.retries = num
62
+ end
63
+
64
+ def retriable_errors(*errors)
65
+ self.retriable = errors
66
+ end
67
+
68
+ def retriable_condition(proc)
69
+ self.retry_condition_proc = proc
70
+ end
71
+
72
+ def on_retry(proc)
73
+ self.retry_proc = proc
74
+ end
75
+
76
+ def sleep_before_retry(seconds)
77
+ self.time_to_sleep = seconds
78
+ end
79
+
80
+ def retriable_configuration
81
+ Take2::Configuration::CONFIG_ATTRS.each_with_object({}) do |key, hash|
82
+ hash[key] = send(key)
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ attr_accessor(*Take2::Configuration::CONFIG_ATTRS)
89
+
90
+ def set_defaults
91
+ config = Take2.configuration.to_hash
92
+ Take2::Configuration::CONFIG_ATTRS.each do |attr|
93
+ instance_variable_set("@#{attr}", config[attr])
94
+ end
95
+ end
96
+
97
+ def response_status(response)
98
+ return response.status if response.respond_to? :status
99
+ response.status_code if response.respond_to? :status_code
100
+ end
101
+
102
+ def log_client_error(error, tries)
103
+ # Override this method in the includer
104
+ true
105
+ end
106
+
107
+ end
108
+
109
+ end
@@ -0,0 +1,39 @@
1
+ module Take2
2
+ class Configuration
3
+ CONFIG_ATTRS = [:retries, :retriable, :retry_proc, :retry_condition_proc, :time_to_sleep].freeze
4
+ attr_accessor(*CONFIG_ATTRS)
5
+
6
+ def initialize(options = {})
7
+ # Defaults
8
+ @retries = 3
9
+ @retriable = [
10
+ Net::HTTPServerError,
11
+ Net::HTTPServerException,
12
+ Net::HTTPRetriableError,
13
+ Errno::ECONNRESET,
14
+ IOError,
15
+ ].freeze
16
+ @retry_proc = proc {}
17
+ @retry_condition_proc = proc { false }
18
+ @time_to_sleep = 3
19
+ # Overwriting the defaults
20
+ options.each do |k, v|
21
+ raise ArgumentError, "#{k} is not a valid configuration" unless CONFIG_ATTRS.include?(k)
22
+ raise ArgumentError, "#{k} must be positive integer" unless v.is_a?(Integer) && v.positive?
23
+ raise ArgumentError, "#{k} must be positive number" unless (v.is_a?(Integer) || v.is_a?(Float)) && v.positive?
24
+ instance_variable_set(:"@#{k}", v)
25
+ end
26
+ end
27
+
28
+ def to_hash
29
+ CONFIG_ATTRS.each_with_object({}) do |key, hash|
30
+ hash[key] = public_send(key)
31
+ end
32
+ end
33
+
34
+ def [](value)
35
+ self.public_send(value)
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ module Take2
2
+ VERSION = "0.0.0"
3
+ end
@@ -0,0 +1,2 @@
1
+ require 'rspec'
2
+ require 'take2'
@@ -0,0 +1,176 @@
1
+ require 'spec_helper'
2
+ require 'take2'
3
+
4
+ RSpec.describe Take2 do
5
+
6
+ let!(:config) do
7
+ Take2.configure do |c|
8
+ c.retries = 1
9
+ c.retriable = [Net::HTTPServerError, Net::HTTPRetriableError].freeze
10
+ c.retry_condition_proc = proc {false}
11
+ c.retry_proc = proc {}
12
+ c.time_to_sleep = 0.5
13
+ end
14
+ end
15
+ let(:klass) { Class.new { include Take2 } }
16
+ let(:object) { klass.new }
17
+
18
+ describe 'default values' do
19
+
20
+ subject { klass.retriable_configuration }
21
+
22
+ it 'has a default value for :retries' do
23
+ expect(subject[:retries]).to eql described_class.configuration[:retries]
24
+ end
25
+
26
+ it 'has a default value for :retriable' do
27
+ expect(subject[:retriable]).to eql described_class.configuration[:retriable]
28
+ end
29
+
30
+ it 'has a default value for :retry_condition_proc' do
31
+ expect(subject[:retry_condition_proc].call).to eql described_class.configuration[:retry_condition_proc].call
32
+ end
33
+
34
+ it 'has a default value for :retry_proc' do
35
+ expect(subject[:retry_proc].call).to eql described_class.configuration[:retry_proc].call
36
+ end
37
+
38
+ it 'has a default value for :time_to_sleep' do
39
+ expect(subject[:time_to_sleep]).to eql described_class.configuration[:time_to_sleep]
40
+ end
41
+
42
+ end
43
+
44
+ describe 'included helpers' do
45
+
46
+ subject { klass.retriable_configuration }
47
+
48
+ describe '#number_of_retries' do
49
+
50
+ it 'sets the :retries attribute' do
51
+ klass.number_of_retries 1
52
+ expect(subject[:retries]).to eql 1
53
+ end
54
+
55
+ end
56
+
57
+ describe '#retriable_errors' do
58
+
59
+ it 'sets the :retriable_errors attribute' do
60
+ retriables = IOError, Faraday::ClientError
61
+ klass.retriable_errors retriables
62
+ expect(subject[:retriable]).to eql [retriables]
63
+ end
64
+
65
+ end
66
+
67
+ describe '#retriable_condition' do
68
+
69
+ it 'sets the :retriable_condition attribute' do
70
+ retriable_proc = proc { 'Ho-Ho-Ho' }
71
+ klass.retriable_condition retriable_proc
72
+ expect(subject[:retry_condition_proc].call).to eql retriable_proc.call
73
+ end
74
+
75
+ end
76
+
77
+ describe '#on_retry' do
78
+
79
+ it 'sets the :on_retry attribute' do
80
+ retry_proc = proc { |el| el }
81
+ klass.on_retry retry_proc
82
+ expect(subject[:retry_proc].call).to eql retry_proc.call
83
+ end
84
+
85
+ end
86
+
87
+ describe '#sleep_before_retry' do
88
+
89
+ it 'sets the :sleep_before_retry attribute' do
90
+ klass.sleep_before_retry 3.5
91
+ expect(subject[:time_to_sleep]).to eql 3.5
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
98
+ describe '.call_api_with_retry' do
99
+
100
+ def increment_retry_counter
101
+ @tries += 1
102
+ end
103
+
104
+ def wrath_the_gods_with(error)
105
+ increment_retry_counter
106
+ raise error
107
+ end
108
+
109
+ context 'when raised with non retriable error' do
110
+
111
+ let(:error) { StandardError.new 'Release the Kraken!!' }
112
+
113
+ before(:each) { @tries = 0 }
114
+
115
+ it 're raises the original error' do
116
+ expect do
117
+ object.call_api_with_retry { wrath_the_gods_with error }
118
+ end.to raise_error error.class
119
+ end
120
+
121
+ it 'is not retried' do
122
+ expect do
123
+ object.call_api_with_retry { wrath_the_gods_with error } rescue nil
124
+ end.to change{@tries}.from(0).to(1)
125
+ end
126
+
127
+ it 'logs the error' do
128
+ expect(object).to receive(:log_error).with(error)
129
+ object.call_api_with_retry { wrath_the_gods_with error } rescue nil
130
+ end
131
+
132
+ end
133
+
134
+ context 'when raised with retriable error' do
135
+
136
+ let(:retriable_error) { Net::HTTPRetriableError.new 'Release the Kraken...many times!!', nil }
137
+
138
+ before(:each) { @tries = 0 }
139
+
140
+ it 'retries correct number of times' do
141
+ expect do
142
+ object.call_api_with_retry { wrath_the_gods_with retriable_error } rescue nil
143
+ end.to change{@tries}.from(0).to(klass.retriable_configuration[:retries] + 1)
144
+ end
145
+
146
+ it 'calls the retry proc' do
147
+ expect(klass.retriable_configuration[:retry_proc]).to receive(:call).exactly(klass.retriable_configuration[:retries])
148
+ object.call_api_with_retry { wrath_the_gods_with retriable_error } rescue nil
149
+ end
150
+
151
+ it 'calls the retry_condition proc' do
152
+ expect(klass.retriable_configuration[:retry_condition_proc]).to receive(:call).exactly(klass.retriable_configuration[:retries])
153
+ object.call_api_with_retry { wrath_the_gods_with retriable_error } rescue nil
154
+ end
155
+
156
+ it 'sleeps the correct amount of time' do
157
+ allow_any_instance_of(Object).to receive(:sleep).with(klass.retriable_configuration[:time_to_sleep])
158
+ object.call_api_with_retry { wrath_the_gods_with retriable_error } rescue nil
159
+ end
160
+
161
+ it 'logs the error' do
162
+ expect(object).to receive(:log_error).with(retriable_error)
163
+ object.call_api_with_retry { wrath_the_gods_with retriable_error } rescue nil
164
+ end
165
+
166
+ it 're raises the original error' do
167
+ expect do
168
+ object.call_api_with_retry { wrath_the_gods_with retriable_error }
169
+ end.to raise_error retriable_error.class
170
+ end
171
+
172
+ end
173
+
174
+ end
175
+
176
+ end
data/take2.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ require File.expand_path("../lib/take2/version", __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "take2"
5
+ s.version = Take2::VERSION
6
+ s.platform = Gem::Platform::RUBY
7
+ s.licenses = ['MIT']
8
+ s.authors = ["Anton Magids"]
9
+ s.email = ["evnomadx@gmail.com"]
10
+ s.homepage = "https://github.com/restaurant-cheetah/take2"
11
+ s.summary = "Provides Take2 for your APIs calls"
12
+ s.description = "Easily define Take2 / retry behavior for API wrappers, service objects or a single method."
13
+ s.post_install_message = "Getting Take2 is dead easy!"
14
+
15
+ all_files = `git ls-files`.split("\n")
16
+ test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+
18
+ s.files = all_files - test_files
19
+ s.test_files = test_files
20
+ s.require_paths = ['lib']
21
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: take2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Anton Magids
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-09-12 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Easily define Take2 / retry behavior for API wrappers, service objects
14
+ or a single method.
15
+ email:
16
+ - evnomadx@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ".gitignore"
22
+ - Gemfile
23
+ - LICENSE
24
+ - README.md
25
+ - Rakefile
26
+ - lib/take2.rb
27
+ - lib/take2/configuration.rb
28
+ - lib/take2/version.rb
29
+ - spec/spec_helper.rb
30
+ - spec/take2_spec.rb
31
+ - take2.gemspec
32
+ homepage: https://github.com/restaurant-cheetah/take2
33
+ licenses:
34
+ - MIT
35
+ metadata: {}
36
+ post_install_message: Getting Take2 is dead easy!
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubyforge_project:
52
+ rubygems_version: 2.6.14
53
+ signing_key:
54
+ specification_version: 4
55
+ summary: Provides Take2 for your APIs calls
56
+ test_files:
57
+ - spec/spec_helper.rb
58
+ - spec/take2_spec.rb