uber_task 0.0.0 → 1.0.0.rc.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b12e30158c84ed0a66cb41d2b53a2c10e9d30fd6222589e9434e1dd45c7b2986
4
- data.tar.gz: 3cdafd13000af2d5870739ac73f9f487f99a1bebbcca014b033a8d7c99016172
3
+ metadata.gz: 33609c0486576e3d2c74c595fbb0701a1e594e43b3c58c832eb1a945ece13955
4
+ data.tar.gz: 66839b6376f6262bd24f7234e699bdd56b39e3251e9e543627deeb59b42e44a4
5
5
  SHA512:
6
- metadata.gz: 0d3a94725198a0dfddb55cf29af18880aed626a5b1729648116bb30e412d61d5a2ab136b52a99b7e19077ff7f9544451ef32c63a74c757d0e582f57f6b402951
7
- data.tar.gz: bbd84dd2dfaa6f2f3366f9cc73b8b03c10792dde17e201952a2004c502a4c1cf5b5f9ab9001f18401232b039080897d4444617a2e11ab3bd9cc938118b2b8710
6
+ metadata.gz: 5fc184374906f2e0e84358c902a6e50c993c3764c46bd65befe6aa74d152d62036263ceac8b4df4509c6a980ac890bda4923239c13ca0479c3497a6c023be77f
7
+ data.tar.gz: 5218173308542a26fd40b6747e29131d9fdc684d8dddb3924d8c19ff7386afdd6bb95dd28ee065c4e4afdfb9807d9c9b5bb19b530f9b80507630f04bfef3c781
data/README.md CHANGED
@@ -1,8 +1,14 @@
1
1
  # UberTask
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/uber_task`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Welcome to UberTask! A gem that will help you execute sequential tasks.
4
+ The gem provides the ability to retry a failed task
5
+ and reports the progress on the sequential tasks.
4
6
 
5
- TODO: Delete this and the text above, and describe your gem
7
+ ## Table of Contents
8
+
9
+ * [Installation](#installation)
10
+ * [Use case](#use-case)
11
+ * [Usage](#usage)
6
12
 
7
13
  ## Installation
8
14
 
@@ -19,25 +25,243 @@ And then execute:
19
25
  Or install it yourself as:
20
26
 
21
27
  $ gem install uber_task
22
-
23
28
  ## Usage
24
29
 
25
- TODO: Write usage instructions here
30
+ Imagine a Rails application similar to Circle CI, where we need to execute
31
+ steps in sequence like:
26
32
 
27
- ## Development
33
+ 1. Install the Ruby version as specified in the config file
34
+ 1. Install Node version as specified in the config file
35
+ 1. Checkout the code from GitHub
36
+ 1. Install bundler and gems
37
+ 1. Install node packages
38
+ 1. Create DB using `rails db:create`
39
+ 1. ...
40
+ 1. ...
28
41
 
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
42
+ Most of the steps are dependent on their previous step to execute successfully.
43
+ A few steps involve API calls to third-party services which can be a bottleneck
44
+ sometimes if their server is down or we have a bad connectivity issue.
30
45
 
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
46
+ ### run
32
47
 
33
- ## Contributing
48
+ `.run` method creates or adds the current task to the tasks list.
49
+ This needs to be called inside a function which usually is considered as a
50
+ parent function.
34
51
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/uber_task. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/uber_task/blob/master/CODE_OF_CONDUCT.md).
52
+ ```ruby
53
+ def create_build_and_test
54
+ UberTask.run(
55
+ 'Build and Test',
56
+ default_retry_count: 1,
57
+ default_retry_wait: 5.mins,
58
+ retry_count: 1
59
+ ) do
36
60
 
37
- ## License
61
+ install_ruby
62
+ install_node
63
+ checkout_code_from_github
64
+ install_bundler_and_gems
65
+ install_node_packages
66
+ create_db
67
+ end
68
+ end
69
+ ```
70
+
71
+ `create_build_and_test` can be treated as a parent function that executes the pipeline steps.
72
+ On calling the `.run` method of UberTask inside this function, we add this function as the first task in our process.
73
+
74
+ #### parameters
75
+
76
+ 1. name -
77
+ Default value is nil. Set the value of this parameter which
78
+ signifies what the function does.
79
+
80
+ 1. default_retry_count:
81
+ Default value is nil. The count here signifies how many times the parent function
82
+ (in the above example `create_build_and_test` function) should be retried.
83
+ This is applicable only for the parent task.
84
+
85
+ 1. default_retry_wait:
86
+ Default value is nil. If the parent task fails and we want to retry it after a certain
87
+ timeout, we should set this value.
88
+
89
+ 1. retry_count:
90
+ Default value is 0. The count signifies how many times the subtasks should be retried.
91
+ If this is not set the `default_retry_count` will be assigned to `retry_count`.
92
+
93
+ 1. vital:
94
+ Default value is true. If set to true, the task is considered to be an important task
95
+ and an error will be raised if it fails. The task will be retried based on `retry_count`.
96
+ If the value is false, it won't be retried even if `retry_count` is greater than 0.
97
+
98
+ 1. block:
99
+ Pass a Ruby block that contains the code to be executed.
100
+
101
+ ### on_success
102
+
103
+ `#on_success` acts as an event handler similar to JavaScript handlers.
104
+
105
+ We need to call this inside the `UberTask#run` method. This event is triggered when the current task gets executed successfully.
106
+
107
+ ```ruby
108
+ def install_ruby
109
+ UberTask.run('Install Ruby', retry_count: 2) do
110
+ UberTask.on_success do
111
+ # code to send a Slack notification
112
+ send_ruby_installed_notification
113
+ end
114
+
115
+ process_ruby_version_and_install_ruby
116
+ end
117
+ end
118
+ ```
119
+
120
+ `install_ruby` is called inside the `create_build_and_test` function.
121
+ When we call `.run` again inside the subtask,
122
+ the function gets added to the top of the stack.
123
+ Our stack will look as below:
124
+
125
+ ```
126
+ install_ruby <- top
127
+ create_build_and_test
128
+ ```
129
+
130
+ When the `install_ruby` function is successful,
131
+ the code inside the `on_success` block gets executed.
132
+
133
+ #### parameters
134
+
135
+ 1. block:
136
+ Pass a Ruby block that contains the code to be executed.
137
+
138
+ ### on_report
139
+
140
+ This is similar to the `on_success` event.
141
+
142
+ We need to call this inside the `UberTask#run` method. This event is triggered when the current task reports something.
143
+
144
+ ```ruby
145
+ def install_ruby
146
+ UberTask.run('Install Ruby', retry_count: 2) do
147
+ UberTask.on_report do
148
+ puts 'This message appears when task reports something'
149
+ end
150
+
151
+ UberTask.report do
152
+ puts 'Starting ruby installation'
153
+ end
154
+
155
+ process_ruby_version_and_install_ruby
156
+
157
+ UberTask.report do
158
+ puts 'Finished ruby installation'
159
+ end
160
+ end
161
+ end
162
+ ```
163
+
164
+ #### parameters
165
+
166
+ 1. block:
167
+ Pass a Ruby block that contains the code to be executed.
168
+
169
+ ### on_skip
170
+
171
+ This is similar to the `on_success` event.
172
+
173
+ We need to call this inside the `UberTask#run` method. This event is triggered when the current task gets skipped.
174
+
175
+ ```ruby
176
+ def install_ruby
177
+ UberTask.run('Install Ruby', retry_count: 2) do
178
+ UberTask.on_skip do
179
+ puts 'Ruby installation was skipped'
180
+ end
181
+
182
+ if ruby_already_installed?
183
+ UberTask.skip('Ruby is already installed')
184
+ else
185
+ process_ruby_version_and_install_ruby
186
+ end
187
+ end
188
+ end
189
+ ```
190
+
191
+ #### parameters
192
+
193
+ 1. block:
194
+ Pass a Ruby block that contains the code to be executed.
195
+
196
+ ### on_subtask_error
197
+
198
+ This is similar to the `on_success` event.
38
199
 
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
200
+ We need to call this inside the `UberTask#run` method. This event is triggered when the subtask raises an error.
40
201
 
41
- ## Code of Conduct
202
+ ```ruby
203
+ def install_ruby_from_source
204
+ UberTask.run('Install Ruby from source', retry_count: 2) do
205
+ UberTask.on_subtask_error do |_task, event, err|
206
+ if network_error?(err)
207
+ puts 'Encountered network error, retrying...'
208
+ UberTask.retry(reason: err, wait: 3)
209
+ else
210
+ puts "Encountered unexpected error - #{err.message}"
211
+ event.handled
212
+ end
213
+ end
214
+
215
+ ### Subtask 1 -- network error can occur
216
+ UberTask.run('Download ruby archieve', retry_count: 3) do
217
+ download_ruby_archieve
218
+ end
219
+
220
+ ### Subtask 2 -- compilation can fail
221
+ UberTask.run('Compile ruby') do
222
+ compile_ruby
223
+ end
224
+ end
225
+ end
226
+ ```
227
+
228
+ #### parameters
229
+
230
+ 1. block:
231
+ Pass a Ruby block that contains the code to be executed.
232
+
233
+ ### on_retry
234
+
235
+ This is similar to the `on_success` event.
236
+
237
+ We need to call this inside the `UberTask#run` method. This event is triggered when the current task is retried.
238
+
239
+ ```ruby
240
+ def install_ruby
241
+ UberTask.run('Install Ruby', retry_count: 2) do
242
+ UberTask.on_retry do
243
+ puts 'Retrying to install ruby...'
244
+ end
245
+
246
+ result = process_ruby_version_and_install_ruby
247
+ UberTask.retry(reason: result.message) if result.error?
248
+ end
249
+ end
250
+ ```
251
+
252
+ #### parameters
253
+
254
+ 1. block:
255
+ Pass a Ruby block that contains the code to be executed.
256
+
257
+ ## Examples
258
+
259
+ You can find examples of gem usage at `examples/` folder:
260
+ ```
261
+ ruby examples/download_and_move_file.rb
262
+ ```
263
+
264
+ ## License
42
265
 
43
- Everyone interacting in the UberTask project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/uber_task/blob/master/CODE_OF_CONDUCT.md).
266
+ The gem is available as open source under the terms of the
267
+ [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UberTask
4
+ class Error < RuntimeError; end
5
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UberTask
4
+ class Event
5
+ attr_reader :name
6
+
7
+ def initialize(name)
8
+ @name = name
9
+ end
10
+
11
+ def handled
12
+ raise EventHandled
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UberTask
4
+ # We are inheriting from Exception to make sure it won't be caught by accident
5
+ # which would affect the flow the tasks.
6
+ class EventHandled < Exception; end # rubocop:disable Lint/InheritException
7
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UberTask
4
+ module Internal
5
+ module Path
6
+ def self.shorten(path)
7
+ if defined?(Rails)
8
+ rails_root = Rails.root.to_s
9
+ if rails_root && path.start_with?(rails_root)
10
+ path = path.delete_prefix(rails_root)
11
+ return "[PROJECT]#{path}"
12
+ end
13
+ end
14
+ path.gsub(%r{^.+?/(ruby)?gems}, '[GEM]')
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UberTask
4
+ module Internal
5
+ def self.trace
6
+ return unless Thread.current[:__uber_task_trace__]
7
+
8
+ UberTask.logger.debug yield
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UberTask
4
+ class Location
5
+ attr_accessor :path,
6
+ :line
7
+
8
+ def initialize(path:, line:)
9
+ @path = Internal::Path.shorten(path)
10
+ @line = line
11
+ end
12
+
13
+ def to_s
14
+ "#{path}:#{line}"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UberTask
4
+ class RetryInfo
5
+ attr_accessor :report,
6
+ :retries_remaining,
7
+ :wait
8
+
9
+ attr_reader :default_retries,
10
+ :default_retry_wait,
11
+ :retries
12
+
13
+ def initialize(
14
+ default_retries: nil,
15
+ default_retry_wait: nil,
16
+ retries: nil
17
+ )
18
+ @default_retries = default_retries || 0
19
+ @default_retry_wait = default_retry_wait || 0
20
+ @report = true
21
+ @retries = retries || 0
22
+ @retries_remaining = retries || 0
23
+ @wait = 0
24
+ end
25
+
26
+ def message(wait: nil)
27
+ if wait.nil?
28
+ wait = @wait
29
+ end
30
+
31
+ if wait < 1
32
+ wait = default_retry_wait || 0
33
+ end
34
+
35
+ if wait.positive?
36
+ <<~MSG.strip
37
+ Retrying in #{wait} seconds... (#{retries_remaining} retries remaining)
38
+ MSG
39
+ else
40
+ <<~MSG.strip
41
+ Retrying... (#{retries_remaining} retries remaining)
42
+ MSG
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UberTask
4
+ # We are inheriting from Exception to make sure it won't be caught by accident
5
+ # which would affect the flow the tasks.
6
+ class RetryTask < Exception # rubocop:disable Lint/InheritException
7
+ attr_accessor :reason,
8
+ :wait
9
+
10
+ def initialize(reason: nil, wait: 0)
11
+ validate_wait_value!(wait)
12
+
13
+ super('Requested to retry the task.')
14
+ @reason = reason
15
+ @wait = wait
16
+ end
17
+
18
+ private
19
+
20
+ def validate_wait_value!(value)
21
+ raise ArgumentError, '`wait` is not numberic' unless value.is_a?(Numeric)
22
+ raise ArgumentError, '`wait` cannot be negative' if value.negative?
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UberTask
4
+ # We are inheriting from Exception to make sure it won't be caught by accident
5
+ # which would affect the flow the tasks.
6
+ class SkipTask < Exception # rubocop:disable Lint/InheritException
7
+ attr_accessor :reason
8
+
9
+ def initialize(reason: nil)
10
+ super('Requested to skip the task.')
11
+ @reason = reason
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'colorize'
4
+
5
+ module UberTask
6
+ class TaskContext
7
+ attr_reader :body,
8
+ :handlers,
9
+ :location,
10
+ :method,
11
+ :name,
12
+ :parent,
13
+ :retry_info,
14
+ :scope,
15
+ :subtasks,
16
+ :vital
17
+
18
+ def initialize(
19
+ name:,
20
+ retry_info:,
21
+ vital: true,
22
+ &block
23
+ )
24
+ @body = block
25
+ @handlers = {
26
+ retry: nil,
27
+ report: nil,
28
+ skip: nil,
29
+ subtask_error: nil,
30
+ success: nil,
31
+ }
32
+ @location = Location.new(
33
+ path: block.source_location[0],
34
+ line: block.source_location[1],
35
+ )
36
+ @method = @body.binding.eval('__method__')
37
+ @name = name
38
+ @parent = UberTask.current
39
+ @parent&.add_subtask(self)
40
+ @retry_info = retry_info
41
+ @scope = @body.binding.eval('self')
42
+ @vital = vital
43
+ end
44
+
45
+ def add_subtask(context)
46
+ @subtasks ||= []
47
+ @subtasks.push(context)
48
+ end
49
+
50
+ def execute
51
+ enter_task
52
+ result = @scope.instance_exec(self) do |context|
53
+ context.body.call(context)
54
+ end
55
+ execute_handler(:success)
56
+ result
57
+ rescue RetryTask => err
58
+ if retry?(err)
59
+ exit_task
60
+ retry
61
+ end
62
+ rescue SkipTask => err
63
+ execute_handler(:skip, err.reason)
64
+ # rubocop:disable Lint/RescueException
65
+ rescue Exception => err
66
+ parent_asked_to_retry = false
67
+
68
+ begin
69
+ # We will allow ancestors to check errors from this task because in some
70
+ # situations, like, for example, a connection issue, a parent task may
71
+ # order the child to retry without having to put the error check on each
72
+ # child task.
73
+ @parent&.execute_handler_chain(self, :subtask_error, err)
74
+ rescue RetryTask => err
75
+ if !retry?(err)
76
+ return
77
+ end
78
+ parent_asked_to_retry = true
79
+ rescue SkipTask => err
80
+ execute_handler(:skip, err.reason)
81
+ return
82
+ end
83
+
84
+ if parent_asked_to_retry
85
+ exit_task
86
+ retry
87
+ end
88
+
89
+ # None of the parents asked to retry or skip the task.
90
+ raise err
91
+ # rubocop:enable Lint/RescueException
92
+ ensure
93
+ exit_task
94
+ end
95
+
96
+ def enter_task
97
+ thread = Thread.current
98
+ thread[:__uber_task_stack__] ||= []
99
+ thread[:__uber_task_stack__].push(self)
100
+ Internal.trace { enter_message }
101
+ end
102
+
103
+ def exit_task
104
+ thread = Thread.current
105
+ thread[:__uber_task_stack__].pop
106
+ Internal.trace { exit_message }
107
+ end
108
+
109
+ def execute_handler(key, *args)
110
+ @scope.instance_exec(self) do |context|
111
+ context.handlers[key]&.call(*args)
112
+ end
113
+ end
114
+
115
+ def execute_handler_chain(task, key, *args)
116
+ begin
117
+ @scope.instance_exec(self) do |context|
118
+ event = Event.new(key.to_s)
119
+ context.handlers[key]&.call(task, event, *args)
120
+ end
121
+ rescue EventHandled
122
+ return
123
+ end
124
+
125
+ @parent&.execute_handler_chain(task, key, *args)
126
+ end
127
+
128
+ def full_location
129
+ "#{@location.path}:" + @location.line.to_s.red
130
+ end
131
+
132
+ def full_name
133
+ result = @scope.to_s.magenta + ".#{@method}".cyan
134
+ result += " #{@name.to_s.white}" unless @name.nil?
135
+ result
136
+ end
137
+
138
+ def level
139
+ @parent.nil? ? 0 : @parent.level + 1
140
+ end
141
+
142
+ def report(level = ::Logger::INFO)
143
+ message = yield
144
+ execute_handler_chain(self, :report, message, level)
145
+ end
146
+
147
+ private
148
+
149
+ def enter_message
150
+ enter_tag = '[Enter]'.yellow
151
+ trace = "#{uber_task_tag} #{enter_tag} #{full_name} #{full_location}"
152
+ indent = ' ' * level
153
+ indent + trace
154
+ end
155
+
156
+ def exit_message
157
+ exit_tag = '[Enter]'.red
158
+ trace = "#{uber_task_tag} #{exit_tag} #{full_name} #{full_location}"
159
+ indent = ' ' * level
160
+ indent + trace
161
+ end
162
+
163
+ def uber_task_tag
164
+ '[UberTask]'.green
165
+ end
166
+
167
+ def retry?(err)
168
+ if @retry_info.retries_remaining.positive?
169
+ @retry_info.retries_remaining -= 1
170
+
171
+ if @retry_info.report
172
+ report { @retry_info.message(wait: err.wait) }
173
+ end
174
+
175
+ # The RetryTask exception specified the amount of time to wait.
176
+ if err.wait.positive?
177
+ sleep(err.wait)
178
+ # The task itself has the amount of time to wait configured.
179
+ elsif @retry_info.wait.positive?
180
+ sleep(@retry_info.wait)
181
+ # The task inherited the amount of time to wait from the parent task.
182
+ elsif @retry_info.default_retry_wait.positive?
183
+ sleep(@retry_info.default_retry_wait)
184
+ end
185
+
186
+ execute_handler(:retry, err.reason)
187
+
188
+ Internal.trace { exit_message }
189
+ Internal.trace { enter_message }
190
+
191
+ return true
192
+ end
193
+
194
+ raise err.reason if @vital
195
+
196
+ # We cannot retry anymore and it is not a vital task.
197
+ execute_handler(:skip, err.reason)
198
+ false
199
+ end
200
+ end
201
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UberTask
4
- VERSION = "0.0.0"
4
+ VERSION = '1.0.0.rc.0'
5
5
  end
data/lib/uber_task.rb CHANGED
@@ -1,8 +1,132 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "uber_task/version"
3
+ require 'logger'
4
+ require 'uber_task/internal'
5
+ require 'uber_task/internal/path'
6
+ require 'uber_task/error'
7
+ require 'uber_task/event'
8
+ require 'uber_task/event_handled'
9
+ require 'uber_task/location'
10
+ require 'uber_task/retry_info'
11
+ require 'uber_task/retry_task'
12
+ require 'uber_task/skip_task'
13
+ require 'uber_task/task_context'
4
14
 
5
15
  module UberTask
6
- class Error < StandardError; end
7
- # Your code goes here...
16
+ class << self
17
+ attr_writer :logger
18
+
19
+ def logger
20
+ @logger ||=
21
+ if defined?(Rails.logger)
22
+ Rails.logger
23
+ else
24
+ Logger.new($stdout, progname: name)
25
+ end
26
+ end
27
+ end
28
+
29
+ def self.current
30
+ Thread.current[:__uber_task_stack__] ||= []
31
+ Thread.current[:__uber_task_stack__].last
32
+ end
33
+
34
+ def self.disable_tracing
35
+ Thread.current[:__uber_task_trace__] = false
36
+ end
37
+
38
+ def self.enable_tracing
39
+ Thread.current[:__uber_task_trace__] = true
40
+ end
41
+
42
+ def self.on_report(&block)
43
+ UberTask.current.handlers[:report] = block
44
+ end
45
+
46
+ def self.on_retry(
47
+ report: true,
48
+ wait: 0,
49
+ &block
50
+ )
51
+ context = UberTask.current
52
+ context.handlers[:retry] = block
53
+ context.retry_info.report = report
54
+ context.retry_info.wait = wait
55
+ end
56
+
57
+ def self.on_skip(&block)
58
+ UberTask.current.handlers[:skip] = block
59
+ end
60
+
61
+ def self.on_subtask_error(&block)
62
+ UberTask.current.handlers[:subtask_error] = block
63
+ end
64
+
65
+ def self.on_success(&block)
66
+ UberTask.current.handlers[:success] = block
67
+ end
68
+
69
+ def self.report(
70
+ level = Logger::INFO,
71
+ &block
72
+ )
73
+ UberTask.current.report(level, &block)
74
+ end
75
+
76
+ def self.retry(reason: nil, wait: 0)
77
+ if block_given?
78
+ reason ||= yield
79
+ end
80
+ raise RetryTask.new(
81
+ reason: reason,
82
+ wait: wait,
83
+ )
84
+ end
85
+
86
+ def self.run(
87
+ name = nil,
88
+ default_retry_count: nil,
89
+ default_retry_wait: nil,
90
+ retry_count: 0,
91
+ vital: true,
92
+ &block
93
+ )
94
+ parent = UberTask.current
95
+
96
+ if parent
97
+ if default_retry_count.nil?
98
+ default_retry_count = parent.retry_info.default_retries
99
+ end
100
+
101
+ if default_retry_wait.nil?
102
+ default_retry_wait = parent.retry_info.default_retry_wait
103
+ end
104
+
105
+ if retry_count < 1
106
+ retry_count = default_retry_count || 0
107
+ end
108
+ end
109
+
110
+ retry_info = RetryInfo.new(
111
+ default_retries: default_retry_count,
112
+ default_retry_wait: default_retry_wait,
113
+ retries: retry_count,
114
+ )
115
+
116
+ context = TaskContext.new(
117
+ name: name,
118
+ retry_info: retry_info,
119
+ vital: vital,
120
+ &block
121
+ )
122
+
123
+ context.execute
124
+ end
125
+
126
+ def self.skip(reason = nil)
127
+ if block_given?
128
+ reason ||= yield
129
+ end
130
+ raise SkipTask.new(reason: reason)
131
+ end
8
132
  end
metadata CHANGED
@@ -1,33 +1,52 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uber_task
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 1.0.0.rc.0
5
5
  platform: ruby
6
6
  authors:
7
+ - Alexandre Borela
7
8
  - Justin Gordon
8
9
  autorequire:
9
- bindir: exe
10
+ bindir: bin
10
11
  cert_chain: []
11
- date: 2022-06-17 00:00:00.000000000 Z
12
- dependencies: []
13
- description: Longer description or delete this line.
12
+ date: 2026-01-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: colorize
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 1.1.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 1.1.0
28
+ description: Run tasks in a structured way with support for retry and progress report.
14
29
  email:
30
+ - alexandre@shakacode.com
15
31
  - justin@shakacode.com
16
32
  executables: []
17
33
  extensions: []
18
34
  extra_rdoc_files: []
19
35
  files:
20
- - ".rspec"
21
- - ".rubocop.yml"
22
- - CHANGELOG.md
23
- - CODE_OF_CONDUCT.md
24
- - Gemfile
25
- - LICENSE.txt
36
+ - LICENSE
26
37
  - README.md
27
- - Rakefile
28
38
  - lib/uber_task.rb
39
+ - lib/uber_task/error.rb
40
+ - lib/uber_task/event.rb
41
+ - lib/uber_task/event_handled.rb
42
+ - lib/uber_task/internal.rb
43
+ - lib/uber_task/internal/path.rb
44
+ - lib/uber_task/location.rb
45
+ - lib/uber_task/retry_info.rb
46
+ - lib/uber_task/retry_task.rb
47
+ - lib/uber_task/skip_task.rb
48
+ - lib/uber_task/task_context.rb
29
49
  - lib/uber_task/version.rb
30
- - sig/uber_task.rbs
31
50
  homepage: https://github.com/shakacode/uber_task
32
51
  licenses:
33
52
  - MIT
@@ -35,6 +54,7 @@ metadata:
35
54
  homepage_uri: https://github.com/shakacode/uber_task
36
55
  source_code_uri: https://github.com/shakacode/uber_task
37
56
  changelog_uri: https://github.com/shakacode/uber_task/blob/main/CHANGELOG.md
57
+ rubygems_mfa_required: 'true'
38
58
  post_install_message:
39
59
  rdoc_options: []
40
60
  require_paths:
@@ -43,15 +63,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
43
63
  requirements:
44
64
  - - ">="
45
65
  - !ruby/object:Gem::Version
46
- version: 2.6.0
66
+ version: 2.7.5
47
67
  required_rubygems_version: !ruby/object:Gem::Requirement
48
68
  requirements:
49
69
  - - ">="
50
70
  - !ruby/object:Gem::Version
51
71
  version: '0'
52
72
  requirements: []
53
- rubygems_version: 3.2.32
73
+ rubygems_version: 3.5.11
54
74
  signing_key:
55
75
  specification_version: 4
56
- summary: Running tasks
76
+ summary: Sequential task management
57
77
  test_files: []
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
data/.rubocop.yml DELETED
@@ -1,13 +0,0 @@
1
- AllCops:
2
- TargetRubyVersion: 2.6
3
-
4
- Style/StringLiterals:
5
- Enabled: true
6
- EnforcedStyle: double_quotes
7
-
8
- Style/StringLiteralsInInterpolation:
9
- Enabled: true
10
- EnforcedStyle: double_quotes
11
-
12
- Layout/LineLength:
13
- Max: 120
data/CHANGELOG.md DELETED
@@ -1,5 +0,0 @@
1
- ## [Unreleased]
2
-
3
- ## [0.1.0] - 2022-06-16
4
-
5
- - Initial release
data/CODE_OF_CONDUCT.md DELETED
@@ -1,84 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
-
7
- We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
-
9
- ## Our Standards
10
-
11
- Examples of behavior that contributes to a positive environment for our community include:
12
-
13
- * Demonstrating empathy and kindness toward other people
14
- * Being respectful of differing opinions, viewpoints, and experiences
15
- * Giving and gracefully accepting constructive feedback
16
- * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
- * Focusing on what is best not just for us as individuals, but for the overall community
18
-
19
- Examples of unacceptable behavior include:
20
-
21
- * The use of sexualized language or imagery, and sexual attention or
22
- advances of any kind
23
- * Trolling, insulting or derogatory comments, and personal or political attacks
24
- * Public or private harassment
25
- * Publishing others' private information, such as a physical or email
26
- address, without their explicit permission
27
- * Other conduct which could reasonably be considered inappropriate in a
28
- professional setting
29
-
30
- ## Enforcement Responsibilities
31
-
32
- Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
33
-
34
- Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
35
-
36
- ## Scope
37
-
38
- This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
39
-
40
- ## Enforcement
41
-
42
- Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at justin@shakacode.com. All complaints will be reviewed and investigated promptly and fairly.
43
-
44
- All community leaders are obligated to respect the privacy and security of the reporter of any incident.
45
-
46
- ## Enforcement Guidelines
47
-
48
- Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
49
-
50
- ### 1. Correction
51
-
52
- **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
53
-
54
- **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
55
-
56
- ### 2. Warning
57
-
58
- **Community Impact**: A violation through a single incident or series of actions.
59
-
60
- **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
61
-
62
- ### 3. Temporary Ban
63
-
64
- **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
65
-
66
- **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
67
-
68
- ### 4. Permanent Ban
69
-
70
- **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
71
-
72
- **Consequence**: A permanent ban from any sort of public interaction within the community.
73
-
74
- ## Attribution
75
-
76
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
77
- available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
78
-
79
- Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
80
-
81
- [homepage]: https://www.contributor-covenant.org
82
-
83
- For answers to common questions about this code of conduct, see the FAQ at
84
- https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
data/Gemfile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- # Specify your gem's dependencies in uber_task.gemspec
6
- gemspec
7
-
8
- gem "rake", "~> 13.0"
9
-
10
- gem "rspec", "~> 3.0"
11
-
12
- gem "rubocop", "~> 1.21"
data/Rakefile DELETED
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rspec/core/rake_task"
5
-
6
- RSpec::Core::RakeTask.new(:spec)
7
-
8
- require "rubocop/rake_task"
9
-
10
- RuboCop::RakeTask.new
11
-
12
- task default: %i[spec rubocop]
data/sig/uber_task.rbs DELETED
@@ -1,4 +0,0 @@
1
- module UberTask
2
- VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
- end
File without changes