level_up 0.1.0 → 0.2.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.
- data/CHANGELOG.txt +7 -0
- data/README.md +29 -29
- data/app/controllers/level_up/jobs_controller.rb +15 -16
- data/app/helpers/level_up/home_helper.rb +5 -1
- data/app/models/level_up/job.rb +65 -55
- data/app/models/level_up/{state → task}/cancel.rb +1 -1
- data/app/models/level_up/{state → task}/end.rb +1 -1
- data/app/models/level_up/task/start.rb +8 -0
- data/app/models/level_up/{state.rb → task.rb} +13 -13
- data/app/views/level_up/home/index.html.erb +4 -4
- data/app/views/level_up/jobs/edit.html.erb +4 -4
- data/app/views/level_up/jobs/index.html.erb +7 -7
- data/app/views/level_up/jobs/show.html.erb +6 -6
- data/db/migrate/20130212111454_create_level_up_jobs.rb +4 -4
- data/lib/level_up/configuration.rb +14 -2
- data/lib/level_up/version.rb +1 -1
- data/test/dummy/app/models/account_banish.rb +18 -0
- data/test/dummy/app/models/account_downgrade.rb +13 -0
- data/test/dummy/app/{jobs → models}/account_upgrade.rb +3 -3
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/{20130212113009_create_delayed_jobs.rb → 20130404201224_create_delayed_jobs.rb} +0 -0
- data/test/dummy/db/migrate/{20130215111404_create_level_up_jobs.level_up.rb → 20130404201329_create_level_up_jobs.level_up.rb} +4 -4
- data/test/dummy/db/schema.rb +9 -9
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +4020 -0
- data/test/dummy/log/test.log +21327 -0
- data/test/dummy/tmp/cache/assets/C2C/CE0/sprockets%2F33f519d653c9b4886c417440a0993824 +0 -0
- data/test/dummy/tmp/cache/assets/C6A/F00/sprockets%2F177385361781d5fe78d217f1f48b8566 +0 -0
- data/test/dummy/tmp/cache/assets/C6C/ED0/sprockets%2F320c25374f5ea136824c04c256e1605b +0 -0
- data/test/dummy/tmp/cache/assets/C87/110/sprockets%2Fc4428c04b8830f451f7636c2811f975d +0 -0
- data/test/dummy/tmp/cache/assets/CB9/E40/sprockets%2F43e6286ecc7751fc65d32b72c6158940 +0 -0
- data/test/dummy/tmp/cache/assets/CCE/7E0/sprockets%2F7d295633b50bfa8b04345d5e600b343a +0 -0
- data/test/dummy/tmp/cache/assets/CD5/450/sprockets%2F2ac0e7e07211a99107e0ba7e2680c885 +0 -0
- data/test/dummy/tmp/cache/assets/CE4/220/sprockets%2Ff1a78dd6750c553265fd3a242d76c277 +0 -0
- data/test/dummy/tmp/cache/assets/CF7/BA0/sprockets%2F8f9487ea6943415e46a5d895e0920ebe +0 -0
- data/test/dummy/tmp/cache/assets/D0E/2B0/sprockets%2F3e386ff8d231b92766061c4fad4ad504 +0 -0
- data/test/dummy/tmp/cache/assets/D19/660/sprockets%2F018d64588c824dd3e7e6c8d6d0d6510e +0 -0
- data/test/dummy/tmp/cache/assets/D25/E10/sprockets%2F989ec024c5db6ae875689b410f8e6d53 +0 -0
- data/test/dummy/tmp/cache/assets/D33/200/sprockets%2Fc291105cf21ca8e744c65f3d4f446aa1 +0 -0
- data/test/dummy/tmp/cache/assets/D34/610/sprockets%2Fcc081ecb5b32894151f16a144383befe +0 -0
- data/test/dummy/tmp/cache/assets/D4D/110/sprockets%2Fc16dfb491d9b98f482b4f0059dd355b8 +0 -0
- data/test/dummy/tmp/cache/assets/D4F/890/sprockets%2Fe3f96e9aa9b032c43788676c5ce066db +0 -0
- data/test/dummy/tmp/cache/assets/D53/370/sprockets%2F1cf0ca91840a1483edcac4d0c403b149 +0 -0
- data/test/dummy/tmp/cache/assets/D69/B90/sprockets%2Fdb0ee3e51128419c85616ceaca66ab58 +0 -0
- data/test/dummy/tmp/cache/assets/D75/B20/sprockets%2Fb659146edbe91be3b1b9d64cb9370c76 +0 -0
- data/test/dummy/tmp/cache/assets/D79/430/sprockets%2Fc030f9d26fab468c49dc494195d77eab +0 -0
- data/test/dummy/tmp/cache/assets/D9D/3C0/sprockets%2F3019d99c7bae1e55eb7c2bf4688b1b1a +0 -0
- data/test/dummy/tmp/cache/assets/DAA/1A0/sprockets%2Fba643993838fce8ddc1d42e6ce1fc346 +0 -0
- data/test/dummy/tmp/cache/assets/DD4/DA0/sprockets%2F17fadf3354a429c53acfe9fca6268d5d +0 -0
- data/test/dummy/tmp/cache/assets/DF8/880/sprockets%2F255d8bde2bf6681724e4ddedcc3cbc02 +0 -0
- data/test/dummy/tmp/job_1.svg +78 -0
- data/test/dummy/tmp/job_2.svg +78 -0
- data/test/dummy/tmp/job_3.svg +78 -0
- data/test/unit/level_up/job_test.rb +124 -52
- metadata +84 -19
- data/app/models/level_up/state/start.rb +0 -9
- data/app/views/level_up/home/workflow.html.erb +0 -5
- data/test/dummy/app/jobs/account_banish.rb +0 -18
- data/test/dummy/app/jobs/account_downgrade.rb +0 -13
- data/test/dummy/app/jobs/mailing_list_subscription.rb +0 -15
data/CHANGELOG.txt
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
== 0.2.0 Hungry Beast
|
|
2
|
+
* Configurable size of saved backtrace when rescuing from an error
|
|
3
|
+
* Renamed 'states' into 'tasks'
|
|
4
|
+
* Renamed 'move_to', 'retry_in' and 'manual_task' methods into 'move_to!', 'retry_in!' and 'manual_task'
|
|
5
|
+
* New 'class_name' option to allow overriding a task class when declaring it
|
|
6
|
+
* Bug fixes and improvements
|
|
7
|
+
|
|
1
8
|
== 0.1.0 Apprentice Warrior
|
|
2
9
|
* First release
|
data/README.md
CHANGED
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
## What ?
|
|
6
6
|
|
|
7
7
|
If you are building a web app, chances are good you have some jobs to design and execute in order to provide services to your customers.
|
|
8
|
-
Generally, that means handling
|
|
9
|
-
LevelUp lets you build all of these and compose them to create a runnable job. Concretely, you will define a
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
Generally, that means handling computer-based or manual tasks, calls to external services, failures, timers and retries.
|
|
9
|
+
LevelUp lets you build all of these and compose them to create a runnable job. Concretely, you will define a task graph, where each task contains
|
|
10
|
+
its own business logic implemented in ruby. Jobs can be performed synchronously in the current thread or asynchronously by background workers.
|
|
11
|
+
Three methods are available in each state to control the job flow: move_to!(task_name), retry_in!(delay), manual_task!(task_description).
|
|
12
12
|
|
|
13
13
|
## Why use LevelUp ?
|
|
14
14
|
|
|
15
|
-
Designing your jobs graphically with
|
|
15
|
+
Designing your jobs graphically with tasks and transitions can be more easier than directly writing code, especially for non-technical people.
|
|
16
16
|
Graphs can be drawn, printed, shared and analysed making it easier to let everyone know what the system is doing at specific points in time.
|
|
17
|
-
From a developers point of view, it’s clearer to separate the different parts of a job into isolated states. Class
|
|
18
|
-
in multiple jobs to avoid code duplication. For example, you can use the Template design pattern to implement the generic part of a
|
|
17
|
+
From a developers point of view, it’s clearer to separate the different parts of a job into isolated states. Class-based tasks are reusable
|
|
18
|
+
in multiple jobs to avoid code duplication. For example, you can use the Template design pattern to implement the generic part of a task
|
|
19
19
|
in a parent class and implement specialized parts in children classes.
|
|
20
20
|
|
|
21
21
|
## Requirements
|
|
@@ -71,13 +71,13 @@ config.level_up.http_password = "your-password"
|
|
|
71
71
|
## Writing Job Models
|
|
72
72
|
|
|
73
73
|
```ruby
|
|
74
|
-
# app/
|
|
74
|
+
# app/models/hard_job.rb
|
|
75
75
|
class HardJob < LevelUp::Job
|
|
76
|
-
#
|
|
76
|
+
# tasks and transitions
|
|
77
77
|
job do
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
task :start, transitions: :first_task
|
|
79
|
+
task :first_task, transitions: :second_task
|
|
80
|
+
task :second_task, transitions: :end
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def first_task
|
|
@@ -90,14 +90,14 @@ class HardJob < LevelUp::Job
|
|
|
90
90
|
end
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
###
|
|
93
|
+
### Task classes
|
|
94
94
|
|
|
95
|
-
You can also define
|
|
95
|
+
You can also define task logic in a class instead of a method.
|
|
96
96
|
|
|
97
97
|
```ruby
|
|
98
|
-
app/
|
|
98
|
+
app/models/hard_job/first_task.rb
|
|
99
99
|
module HardJob
|
|
100
|
-
class FirstTask < LevelUp::
|
|
100
|
+
class FirstTask < LevelUp::Task
|
|
101
101
|
def run
|
|
102
102
|
# logic goes here
|
|
103
103
|
end
|
|
@@ -106,9 +106,9 @@ end
|
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
```ruby
|
|
109
|
-
app/
|
|
109
|
+
app/models/hard_job/second_task.rb
|
|
110
110
|
module HardJob
|
|
111
|
-
class SecondTask < LevelUp::
|
|
111
|
+
class SecondTask < LevelUp::Task
|
|
112
112
|
def run
|
|
113
113
|
# logic goes here
|
|
114
114
|
end
|
|
@@ -118,35 +118,35 @@ end
|
|
|
118
118
|
|
|
119
119
|
### Flow control
|
|
120
120
|
|
|
121
|
-
Inside a
|
|
121
|
+
Inside a task, you can call 3 methods to control the job flow:
|
|
122
122
|
|
|
123
|
-
#### move_to(
|
|
123
|
+
#### move_to!(task_name)
|
|
124
124
|
|
|
125
|
-
Leave the current
|
|
125
|
+
Leave the current task and run the specified one.
|
|
126
126
|
```ruby
|
|
127
127
|
# example:
|
|
128
|
-
move_to :second_task
|
|
128
|
+
move_to! :second_task
|
|
129
129
|
```
|
|
130
130
|
|
|
131
|
-
#### retry_in(delay)
|
|
131
|
+
#### retry_in!(delay)
|
|
132
132
|
|
|
133
|
-
Stop the execution and
|
|
133
|
+
Stop the execution and enqueue a new delayed_job to re-run the current task after the specified delay in seconds.
|
|
134
134
|
|
|
135
135
|
```ruby
|
|
136
136
|
# example:
|
|
137
|
-
retry_in 1.hour
|
|
137
|
+
retry_in! 1.hour
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
-
####
|
|
141
|
-
Stop the execution and set the
|
|
140
|
+
#### manual_task!(description)
|
|
141
|
+
Stop the execution and set the manual_task and manual_task_description attributes to notify that some work need to be done manually.
|
|
142
142
|
|
|
143
143
|
```ruby
|
|
144
144
|
# example:
|
|
145
|
-
|
|
145
|
+
manual_task! "check payment information"
|
|
146
146
|
```
|
|
147
147
|
|
|
148
148
|
You can also raise a StandardError (or a subclass) to stop the execution and set the error attribute.
|
|
149
|
-
The time, the
|
|
149
|
+
The time, the task and the error backtrace will be saved.
|
|
150
150
|
|
|
151
151
|
## Running Jobs
|
|
152
152
|
In your code:
|
|
@@ -68,10 +68,10 @@ module LevelUp
|
|
|
68
68
|
|
|
69
69
|
def move
|
|
70
70
|
job = Job.find params[:id]
|
|
71
|
-
if job.boot_async!(params[:
|
|
72
|
-
redirect_to job_path(
|
|
71
|
+
if job.boot_async!(params[:task])
|
|
72
|
+
redirect_to job_path(job), notice: "Moved to #{params[:task]}!"
|
|
73
73
|
else
|
|
74
|
-
flash[:error] = "Error while moving to #{params[:
|
|
74
|
+
flash[:error] = "Error while moving to #{params[:task]}"
|
|
75
75
|
redirect_to job_path(job)
|
|
76
76
|
end
|
|
77
77
|
end
|
|
@@ -91,22 +91,21 @@ module LevelUp
|
|
|
91
91
|
g.edge[:color] = '#000000'
|
|
92
92
|
g.edge[:arrowhead] = 'open'
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
job.
|
|
96
|
-
|
|
97
|
-
if
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
states[state][:color] = '#600615'
|
|
94
|
+
tasks = {}
|
|
95
|
+
job.tasks.each do |task|
|
|
96
|
+
tasks[task] = g.add_nodes(task.to_s.humanize.downcase)
|
|
97
|
+
if task == :start
|
|
98
|
+
tasks[task][:fillcolor] = '#5db1a4'
|
|
99
|
+
tasks[task][:color] = '#048282'
|
|
100
|
+
elsif task == :end
|
|
101
|
+
tasks[task][:fillcolor] = '#b40d28'
|
|
102
|
+
tasks[task][:color] = '#600615'
|
|
104
103
|
end
|
|
105
104
|
end
|
|
106
105
|
|
|
107
|
-
job.
|
|
108
|
-
job.transitions(
|
|
109
|
-
g.add_edges(
|
|
106
|
+
job.tasks.each do |task|
|
|
107
|
+
job.transitions(task).each do |transition|
|
|
108
|
+
g.add_edges(tasks[task], tasks[transition])
|
|
110
109
|
end
|
|
111
110
|
end
|
|
112
111
|
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
module LevelUp
|
|
2
2
|
module HomeHelper
|
|
3
3
|
def job_entry(job)
|
|
4
|
-
|
|
4
|
+
if job
|
|
5
|
+
link_to "#{job.key} @ #{I18n.l(job.created_at, format: :long)}", job_path(job)
|
|
6
|
+
else
|
|
7
|
+
'-'
|
|
8
|
+
end
|
|
5
9
|
end
|
|
6
10
|
end
|
|
7
11
|
end
|
data/app/models/level_up/job.rb
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
module LevelUp
|
|
2
|
-
class
|
|
2
|
+
class TaskNotFound < StandardError; end
|
|
3
3
|
|
|
4
4
|
class Job < ActiveRecord::Base
|
|
5
5
|
|
|
6
|
-
attr_accessible :key, :
|
|
7
|
-
|
|
6
|
+
attr_accessible :key, :task, :timer, :manual_task, :manual_task_description, :error,
|
|
7
|
+
:created_at, :updated_at, :started_at, :ended_at, :canceled_at
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
belongs_to :delayed_job, class_name: '::Delayed::Job'
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
attr_accessor :next_task
|
|
12
|
+
|
|
13
|
+
scope :queued, lambda { where('delayed_job_id is not null') }
|
|
12
14
|
scope :error, lambda { where(error: true) }
|
|
13
15
|
scope :timer, lambda { where(timer: true) }
|
|
14
|
-
scope :
|
|
16
|
+
scope :manual_task, lambda { where(manual_task: true) }
|
|
15
17
|
|
|
16
18
|
serialize :backtrace
|
|
17
19
|
|
|
@@ -20,7 +22,11 @@ module LevelUp
|
|
|
20
22
|
@schema ||= {}
|
|
21
23
|
end
|
|
22
24
|
|
|
23
|
-
def
|
|
25
|
+
def task_classes
|
|
26
|
+
@task_classes ||= {}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def tasks
|
|
24
30
|
self.schema.keys
|
|
25
31
|
end
|
|
26
32
|
|
|
@@ -31,17 +37,18 @@ module LevelUp
|
|
|
31
37
|
end
|
|
32
38
|
end
|
|
33
39
|
|
|
34
|
-
def
|
|
35
|
-
options.reverse_merge!({
|
|
36
|
-
transitions = options[:
|
|
40
|
+
def task(name, options = {})
|
|
41
|
+
options.reverse_merge!({transitions: []})
|
|
42
|
+
transitions = options[:transitions].kind_of?(Symbol) ? Array(options[:transitions]) : options[:transitions]
|
|
37
43
|
schema[name] = transitions
|
|
44
|
+
task_classes[name] = options[:class_name] if options.key?(:class_name)
|
|
38
45
|
end
|
|
39
46
|
|
|
40
|
-
def transitions(
|
|
41
|
-
if self.schema.has_key?(
|
|
42
|
-
self.schema[
|
|
47
|
+
def transitions(task_name)
|
|
48
|
+
if self.schema.has_key?(task_name)
|
|
49
|
+
self.schema[task_name]
|
|
43
50
|
else
|
|
44
|
-
raise
|
|
51
|
+
raise TaskNotFound, task_name
|
|
45
52
|
end
|
|
46
53
|
end
|
|
47
54
|
end
|
|
@@ -55,8 +62,8 @@ module LevelUp
|
|
|
55
62
|
clear!(event_name)
|
|
56
63
|
step!(event_name, allow_transition, allow_retry)
|
|
57
64
|
|
|
58
|
-
if
|
|
59
|
-
event!(
|
|
65
|
+
if next_task
|
|
66
|
+
event!(next_task, allow_transition, allow_retry)
|
|
60
67
|
elsif retry_at
|
|
61
68
|
retry!
|
|
62
69
|
end
|
|
@@ -67,15 +74,15 @@ module LevelUp
|
|
|
67
74
|
clear_error_attributes
|
|
68
75
|
clear_task_attributes
|
|
69
76
|
|
|
70
|
-
self.
|
|
77
|
+
self.next_task = nil
|
|
71
78
|
self.retry_at = nil
|
|
72
|
-
self.
|
|
79
|
+
self.task = event_name if event_name
|
|
73
80
|
save
|
|
74
81
|
end
|
|
75
82
|
|
|
76
83
|
def step!(event_name, allow_transition, allow_retry)
|
|
77
84
|
begin
|
|
78
|
-
|
|
85
|
+
run_task(event_name, allow_transition, allow_retry)
|
|
79
86
|
rescue => ex
|
|
80
87
|
set_error(ex)
|
|
81
88
|
ensure
|
|
@@ -89,7 +96,7 @@ module LevelUp
|
|
|
89
96
|
boot_async!(nil, run_at: retry_at)
|
|
90
97
|
end
|
|
91
98
|
|
|
92
|
-
def boot_async!(event_name
|
|
99
|
+
def boot_async!(event_name=nil, options={})
|
|
93
100
|
begin
|
|
94
101
|
Delayed::Job.transaction do
|
|
95
102
|
self.delayed_job = delay(options).event!(event_name)
|
|
@@ -110,8 +117,8 @@ module LevelUp
|
|
|
110
117
|
save
|
|
111
118
|
end
|
|
112
119
|
|
|
113
|
-
def
|
|
114
|
-
self.
|
|
120
|
+
def task?(name)
|
|
121
|
+
self.task == name.to_s
|
|
115
122
|
end
|
|
116
123
|
|
|
117
124
|
def cancellable?
|
|
@@ -122,59 +129,60 @@ module LevelUp
|
|
|
122
129
|
!self.delayed_job.nil?
|
|
123
130
|
end
|
|
124
131
|
|
|
125
|
-
def
|
|
126
|
-
self.class.
|
|
132
|
+
def tasks
|
|
133
|
+
self.class.tasks
|
|
127
134
|
end
|
|
128
135
|
|
|
129
|
-
def transitions(
|
|
130
|
-
self.class.transitions(
|
|
136
|
+
def transitions(task_name)
|
|
137
|
+
self.class.transitions(task_name)
|
|
131
138
|
end
|
|
132
139
|
|
|
133
|
-
def
|
|
134
|
-
self.transitions(self.
|
|
140
|
+
def task_transitions
|
|
141
|
+
self.transitions(self.task.to_sym)
|
|
135
142
|
end
|
|
136
143
|
|
|
137
144
|
def schema
|
|
138
145
|
self.class.schema
|
|
139
146
|
end
|
|
140
147
|
|
|
141
|
-
def move_to(
|
|
142
|
-
throw :move_to,
|
|
148
|
+
def move_to!(task_name)
|
|
149
|
+
throw :move_to, task_name
|
|
143
150
|
end
|
|
144
151
|
|
|
145
|
-
def retry_in(delay, error=nil)
|
|
152
|
+
def retry_in!(delay, error=nil)
|
|
146
153
|
throw :retry_in, delay: delay, error: error
|
|
147
154
|
end
|
|
148
155
|
|
|
149
|
-
def manual_task(description)
|
|
150
|
-
throw :
|
|
156
|
+
def manual_task!(description)
|
|
157
|
+
throw :manual_task, description
|
|
151
158
|
end
|
|
152
159
|
|
|
153
160
|
protected
|
|
154
|
-
def
|
|
155
|
-
|
|
156
|
-
|
|
161
|
+
def run_task(task_name, allow_transition, allow_retry)
|
|
162
|
+
task_name ||= self.task
|
|
163
|
+
|
|
164
|
+
if respond_to?(task_name)
|
|
165
|
+
Task.new(self, allow_transition, allow_retry).execute(self, task_name)
|
|
157
166
|
else
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
elsif
|
|
161
|
-
|
|
162
|
-
elsif
|
|
163
|
-
|
|
167
|
+
task_class = if self.class.task_classes.key?(task_name.to_sym)
|
|
168
|
+
self.class.task_classes[task_name.to_sym].constantize
|
|
169
|
+
elsif task?(:start)
|
|
170
|
+
Task::Start
|
|
171
|
+
elsif task?(:end)
|
|
172
|
+
Task::End
|
|
173
|
+
elsif task?(:cancel)
|
|
174
|
+
Task::Cancel
|
|
164
175
|
else
|
|
165
|
-
|
|
176
|
+
next_task_class(task_name)
|
|
166
177
|
end
|
|
167
|
-
state = state_class.new(self, allow_transition, allow_retry)
|
|
168
|
-
state.execute(state, :run)
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
178
|
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
task_instance = task_class.new(self, allow_transition, allow_retry)
|
|
180
|
+
task_instance.execute(task_instance, :run)
|
|
181
|
+
end
|
|
174
182
|
end
|
|
175
183
|
|
|
176
|
-
def
|
|
177
|
-
"#{self.class.name}::#{
|
|
184
|
+
def next_task_class(task_name)
|
|
185
|
+
"#{self.class.name}::#{task_name.camelize}".constantize
|
|
178
186
|
end
|
|
179
187
|
|
|
180
188
|
def set_error(error=nil)
|
|
@@ -189,8 +197,10 @@ module LevelUp
|
|
|
189
197
|
|
|
190
198
|
def set_error_details(error)
|
|
191
199
|
self.failed_at = DateTime.now.utc
|
|
192
|
-
self.failed_in =
|
|
193
|
-
|
|
200
|
+
self.failed_in = self.task
|
|
201
|
+
if Configuration.backtrace_size > 0
|
|
202
|
+
self.backtrace = [error.message] | error.backtrace.take(Configuration.backtrace_size - 1)
|
|
203
|
+
end
|
|
194
204
|
end
|
|
195
205
|
|
|
196
206
|
def clear_error_attributes
|
|
@@ -205,8 +215,8 @@ module LevelUp
|
|
|
205
215
|
end
|
|
206
216
|
|
|
207
217
|
def clear_task_attributes
|
|
208
|
-
self.
|
|
209
|
-
self.
|
|
218
|
+
self.manual_task = false
|
|
219
|
+
self.manual_task_description = nil
|
|
210
220
|
end
|
|
211
221
|
end
|
|
212
222
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module LevelUp
|
|
2
|
-
class
|
|
2
|
+
class Task
|
|
3
3
|
|
|
4
4
|
attr_accessor :job, :allow_transition, :allow_retry
|
|
5
5
|
|
|
@@ -10,11 +10,11 @@ module LevelUp
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def execute(receiver, method_name)
|
|
13
|
-
|
|
13
|
+
task_name = retry_params = task_description = nil
|
|
14
14
|
ActiveRecord::Base.transaction do
|
|
15
|
-
|
|
15
|
+
task_name = catch(:move_to) do
|
|
16
16
|
retry_params = catch(:retry_in) do
|
|
17
|
-
task_description = catch(:
|
|
17
|
+
task_description = catch(:manual_task) do
|
|
18
18
|
receiver.send(method_name)
|
|
19
19
|
nil
|
|
20
20
|
end
|
|
@@ -25,7 +25,7 @@ module LevelUp
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
if self.allow_transition
|
|
28
|
-
self.job.
|
|
28
|
+
self.job.next_task = task_name
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
if retry_params and self.allow_retry
|
|
@@ -33,21 +33,21 @@ module LevelUp
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
if task_description
|
|
36
|
-
self.job.
|
|
37
|
-
self.job.
|
|
36
|
+
self.job.manual_task = true
|
|
37
|
+
self.job.manual_task_description = task_description
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
def move_to(
|
|
42
|
-
self.job.move_to(
|
|
41
|
+
def move_to!(task_name)
|
|
42
|
+
self.job.move_to!(task_name)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
def retry_in(delay, error=nil)
|
|
46
|
-
self.job.retry_in(delay, error)
|
|
45
|
+
def retry_in!(delay, error=nil)
|
|
46
|
+
self.job.retry_in!(delay, error)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
def manual_task(description)
|
|
50
|
-
self.job.manual_task(description)
|
|
49
|
+
def manual_task!(description)
|
|
50
|
+
self.job.manual_task!(description)
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
53
|
end
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
|
|
28
28
|
<div class="grid-6">
|
|
29
29
|
<div class="job-status-stats task">
|
|
30
|
-
<div class="title"><h2><span class="bullet"></span>Task</h2></div>
|
|
31
|
-
<div class="number"><%= link_to LevelUp::Job.
|
|
30
|
+
<div class="title"><h2><span class="bullet"></span>Manual Task</h2></div>
|
|
31
|
+
<div class="number"><%= link_to LevelUp::Job.manual_task.size, jobs_path(search: {manual_task_eq: true}) %></div>
|
|
32
32
|
<div class="stats">
|
|
33
33
|
<ul>
|
|
34
|
-
<li><label>Most recent:</label><%= job_entry(LevelUp::Job.
|
|
35
|
-
<li><label>Oldest:</label><%= job_entry(LevelUp::Job.
|
|
34
|
+
<li><label>Most recent:</label><%= job_entry(LevelUp::Job.manual_task.first) %></li>
|
|
35
|
+
<li><label>Oldest:</label><%= job_entry(LevelUp::Job.manual_task.last) %></li>
|
|
36
36
|
</ul>
|
|
37
37
|
</div>
|
|
38
38
|
</div>
|
|
@@ -20,16 +20,16 @@
|
|
|
20
20
|
<%= f.text_field :key %>
|
|
21
21
|
</li>
|
|
22
22
|
<li>
|
|
23
|
-
<%= f.label :
|
|
24
|
-
<%= f.select :
|
|
23
|
+
<%= f.label :task %>
|
|
24
|
+
<%= f.select :task, options_for_select(@job.tasks, @job.task) %>
|
|
25
25
|
</li>
|
|
26
26
|
<li>
|
|
27
27
|
<%= f.label :timer %>
|
|
28
28
|
<%= f.check_box :timer %>
|
|
29
29
|
</li>
|
|
30
30
|
<li>
|
|
31
|
-
<%= f.label :
|
|
32
|
-
<%= f.check_box :
|
|
31
|
+
<%= f.label :manual_task %>
|
|
32
|
+
<%= f.check_box :manual_task %>
|
|
33
33
|
</li>
|
|
34
34
|
<li>
|
|
35
35
|
<%= f.label :error %>
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
<%= f.select :timer_eq,
|
|
22
22
|
options_for_select([["Any", nil], ["Yes", true], ["No", false]], @search_params[:timer_eq]) %>
|
|
23
23
|
|
|
24
|
-
<%= f.label :
|
|
25
|
-
<%= f.select :
|
|
26
|
-
options_for_select([["Any", nil], ["Yes", true], ["No", false]], @search_params[:
|
|
24
|
+
<%= f.label :manual_task_eq, "Manual Task" %>:
|
|
25
|
+
<%= f.select :manual_task_eq,
|
|
26
|
+
options_for_select([["Any", nil], ["Yes", true], ["No", false]], @search_params[:manual_task_eq]) %>
|
|
27
27
|
|
|
28
28
|
<%= f.label :error_eq, "Error" %>:
|
|
29
29
|
<%= f.select :error_eq,
|
|
@@ -40,10 +40,10 @@
|
|
|
40
40
|
<tr>
|
|
41
41
|
<th>Key</th>
|
|
42
42
|
<th>Type</th>
|
|
43
|
-
<th>
|
|
43
|
+
<th>Task</th>
|
|
44
44
|
<th>Queued</th>
|
|
45
45
|
<th>Timer</th>
|
|
46
|
-
<th>Task</th>
|
|
46
|
+
<th>Manual Task</th>
|
|
47
47
|
<th>Error</th>
|
|
48
48
|
<th>Created at</th>
|
|
49
49
|
</tr>
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
<tr>
|
|
54
54
|
<td><%= link_to job.key, job_path(job) %></td>
|
|
55
55
|
<td><%= job.type %></td>
|
|
56
|
-
<td><%= job.
|
|
56
|
+
<td><%= job.task.humanize.downcase %></td>
|
|
57
57
|
<td><%= status_tag job.delayed_job, :purple %></td>
|
|
58
58
|
<td><%= status_tag job.timer, :blue %></td>
|
|
59
|
-
<td><%= status_tag job.
|
|
59
|
+
<td><%= status_tag job.manual_task, :orange %></td>
|
|
60
60
|
<td><%= status_tag job.error, :red %></td>
|
|
61
61
|
<td><%= l(job.created_at, format: :long) %></td>
|
|
62
62
|
</tr>
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
<li><label>ID:</label><%= @job.id %></li>
|
|
17
17
|
<li><label>Key:</label><%= @job.key %></li>
|
|
18
18
|
<li><label>Type:</label><%= @job.type %></li>
|
|
19
|
-
<li><label>
|
|
19
|
+
<li><label>Task:</label><%= @job.task.humanize.downcase %></li>
|
|
20
20
|
<li><label>Queued:</label><%= status_tag @job.delayed_job, :purple %></li>
|
|
21
21
|
<li><label>Timer:</label><%= status_tag @job.timer, :blue %></li>
|
|
22
|
-
<li><label>Task:</label><%= status_tag @job.
|
|
22
|
+
<li><label>Manual Task:</label><%= status_tag @job.manual_task, :orange %></li>
|
|
23
23
|
<li><label>Error:</label><%= status_tag @job.error, :red %></li>
|
|
24
24
|
<li><label>Created at:</label><%= l(@job.created_at) %></li>
|
|
25
25
|
<li><label>Updated at:</label><%= l(@job.updated_at) %></li>
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
<div class="grid-4">
|
|
48
48
|
<div class="simple-panel job-commands">
|
|
49
49
|
<h2>Job commands</h2>
|
|
50
|
-
<% if @job.
|
|
50
|
+
<% if @job.task?(:end) %>
|
|
51
51
|
Ended
|
|
52
52
|
<% elsif @job.queued? %>
|
|
53
53
|
<div class="command">
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
<% else %>
|
|
59
59
|
<div class="command">
|
|
60
60
|
<%= form_tag run_job_path(@job) do %>
|
|
61
|
-
<%= submit_tag "Rerun the current
|
|
61
|
+
<%= submit_tag "Rerun the current task" %>
|
|
62
62
|
<% end %>
|
|
63
63
|
</div>
|
|
64
64
|
<div class="command">
|
|
@@ -66,9 +66,9 @@
|
|
|
66
66
|
<%= submit_tag "Reboot" %>
|
|
67
67
|
<% end %>
|
|
68
68
|
</div>
|
|
69
|
-
<% @job.
|
|
69
|
+
<% @job.task_transitions.each do |t| %>
|
|
70
70
|
<div class="command">
|
|
71
|
-
<%= form_tag move_job_path(@job,
|
|
71
|
+
<%= form_tag move_job_path(@job, task: t) do %>
|
|
72
72
|
<% submit_tag "Move to: #{t.to_s.titleize}" %>
|
|
73
73
|
<% end %>
|
|
74
74
|
</div>
|
|
@@ -4,11 +4,11 @@ class CreateLevelUpJobs < ActiveRecord::Migration
|
|
|
4
4
|
t.string :type
|
|
5
5
|
t.integer :delayed_job_id
|
|
6
6
|
t.string :key
|
|
7
|
-
t.string :
|
|
7
|
+
t.string :task, default: 'start'
|
|
8
8
|
t.boolean :error, default: false
|
|
9
9
|
t.boolean :timer, default: false
|
|
10
|
-
t.boolean :
|
|
11
|
-
t.text :
|
|
10
|
+
t.boolean :manual_task, default: false
|
|
11
|
+
t.text :manual_task_description
|
|
12
12
|
t.datetime :failed_at
|
|
13
13
|
t.string :failed_in
|
|
14
14
|
t.text :backtrace
|
|
@@ -21,7 +21,7 @@ class CreateLevelUpJobs < ActiveRecord::Migration
|
|
|
21
21
|
|
|
22
22
|
add_index :level_up_jobs, :type
|
|
23
23
|
add_index :level_up_jobs, :delayed_job_id
|
|
24
|
-
add_index :level_up_jobs, :
|
|
24
|
+
add_index :level_up_jobs, :task
|
|
25
25
|
add_index :level_up_jobs, :key
|
|
26
26
|
end
|
|
27
27
|
end
|