level_up 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +2 -0
- data/LICENSE.txt +20 -0
- data/README.md +190 -0
- data/Rakefile +40 -0
- data/app/assets/images/level_up/search.png +0 -0
- data/app/assets/javascripts/level_up/application.js +15 -0
- data/app/assets/javascripts/level_up/home.js +2 -0
- data/app/assets/javascripts/level_up/jobs.js +2 -0
- data/app/assets/stylesheets/level_up/application.css.scss +446 -0
- data/app/assets/stylesheets/level_up/home.css.scss +119 -0
- data/app/assets/stylesheets/level_up/jobs.css.scss +159 -0
- data/app/assets/stylesheets/level_up/rwdgrid.css +403 -0
- data/app/controllers/level_up/application_controller.rb +4 -0
- data/app/controllers/level_up/home_controller.rb +22 -0
- data/app/controllers/level_up/jobs_controller.rb +117 -0
- data/app/helpers/level_up/application_helper.rb +7 -0
- data/app/helpers/level_up/home_helper.rb +7 -0
- data/app/helpers/level_up/jobs_helper.rb +4 -0
- data/app/models/level_up/job.rb +212 -0
- data/app/models/level_up/state/cancel.rb +7 -0
- data/app/models/level_up/state/end.rb +7 -0
- data/app/models/level_up/state/start.rb +9 -0
- data/app/models/level_up/state.rb +53 -0
- data/app/views/layouts/level_up/_footer.html.erb +9 -0
- data/app/views/layouts/level_up/_header.html.erb +23 -0
- data/app/views/layouts/level_up/application.html.erb +36 -0
- data/app/views/level_up/home/index.html.erb +85 -0
- data/app/views/level_up/home/workflow.html.erb +5 -0
- data/app/views/level_up/jobs/edit.html.erb +65 -0
- data/app/views/level_up/jobs/index.html.erb +73 -0
- data/app/views/level_up/jobs/show.html.erb +93 -0
- data/config/routes.rb +13 -0
- data/db/migrate/20130212111454_create_level_up_jobs.rb +27 -0
- data/lib/level_up/configuration.rb +27 -0
- data/lib/level_up/engine.rb +17 -0
- data/lib/level_up/version.rb +3 -0
- data/lib/level_up.rb +7 -0
- data/lib/tasks/level_up_tasks.rake +4 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/jobs/account_banish.rb +18 -0
- data/test/dummy/app/jobs/account_downgrade.rb +13 -0
- data/test/dummy/app/jobs/account_upgrade.rb +15 -0
- data/test/dummy/app/jobs/mailing_list_subscription.rb +15 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/migrate/20130212113009_create_delayed_jobs.rb +22 -0
- data/test/dummy/db/migrate/20130215111404_create_level_up_jobs.level_up.rb +28 -0
- data/test/dummy/db/schema.rb +57 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/delayed_job +5 -0
- data/test/dummy/script/rails +6 -0
- data/test/fixtures/level_up/jobs.yml +11 -0
- data/test/functional/level_up/home_controller_test.rb +9 -0
- data/test/functional/level_up/jobs_controller_test.rb +9 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/level_up_test.rb +7 -0
- data/test/test_helper.rb +15 -0
- data/test/unit/helpers/level_up/home_helper_test.rb +6 -0
- data/test/unit/helpers/level_up/jobs_helper_test.rb +6 -0
- data/test/unit/level_up/job_test.rb +208 -0
- metadata +333 -0
@@ -0,0 +1,212 @@
|
|
1
|
+
module LevelUp
|
2
|
+
class StateNotFound < StandardError; end
|
3
|
+
|
4
|
+
class Job < ActiveRecord::Base
|
5
|
+
|
6
|
+
attr_accessible :key, :state, :timer, :task, :error, :created_at, :updated_at, :started_at, :ended_at, :canceled_at
|
7
|
+
belongs_to :delayed_job, class_name: "::Delayed::Job"
|
8
|
+
|
9
|
+
attr_accessor :next_state
|
10
|
+
|
11
|
+
scope :queued, lambda { where("delayed_job_id is not null") }
|
12
|
+
scope :error, lambda { where(error: true) }
|
13
|
+
scope :timer, lambda { where(timer: true) }
|
14
|
+
scope :task, lambda { where(task: true) }
|
15
|
+
|
16
|
+
serialize :backtrace
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def schema
|
20
|
+
@schema ||= {}
|
21
|
+
end
|
22
|
+
|
23
|
+
def states
|
24
|
+
self.schema.keys
|
25
|
+
end
|
26
|
+
|
27
|
+
def job(&block)
|
28
|
+
if block_given?
|
29
|
+
class_eval(&block)
|
30
|
+
schema[:end] = []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def state(name, options = {})
|
35
|
+
options.reverse_merge!({moves_to: []})
|
36
|
+
transitions = options[:moves_to].kind_of?(Symbol) ? Array(options[:moves_to]) : options[:moves_to]
|
37
|
+
schema[name] = transitions
|
38
|
+
end
|
39
|
+
|
40
|
+
def transitions(state)
|
41
|
+
if self.schema.has_key?(state)
|
42
|
+
self.schema[state]
|
43
|
+
else
|
44
|
+
raise StateNotFound, state
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def boot!
|
50
|
+
event!(nil)
|
51
|
+
end
|
52
|
+
|
53
|
+
def event!(event_name, allow_transition=true, allow_retry=true)
|
54
|
+
event_name = event_name.to_s if event_name
|
55
|
+
clear!(event_name)
|
56
|
+
step!(event_name, allow_transition, allow_retry)
|
57
|
+
|
58
|
+
if next_state
|
59
|
+
event!(next_state, allow_transition, allow_retry)
|
60
|
+
elsif retry_at
|
61
|
+
retry!
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def clear!(event_name)
|
66
|
+
clear_timer_attributes
|
67
|
+
clear_error_attributes
|
68
|
+
clear_task_attributes
|
69
|
+
|
70
|
+
self.next_state = nil
|
71
|
+
self.retry_at = nil
|
72
|
+
self.state = event_name if event_name
|
73
|
+
save
|
74
|
+
end
|
75
|
+
|
76
|
+
def step!(event_name, allow_transition, allow_retry)
|
77
|
+
begin
|
78
|
+
run_state(event_name, allow_transition, allow_retry)
|
79
|
+
rescue => ex
|
80
|
+
set_error(ex)
|
81
|
+
ensure
|
82
|
+
save
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def retry!
|
87
|
+
set_timer
|
88
|
+
save
|
89
|
+
boot_async!(nil, run_at: retry_at)
|
90
|
+
end
|
91
|
+
|
92
|
+
def boot_async!(event_name = nil, options = {})
|
93
|
+
begin
|
94
|
+
Delayed::Job.transaction do
|
95
|
+
self.delayed_job = delay(options).event!(event_name)
|
96
|
+
save
|
97
|
+
end
|
98
|
+
true
|
99
|
+
rescue
|
100
|
+
false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def unqueue!
|
105
|
+
if self.delayed_job
|
106
|
+
self.delayed_job.destroy
|
107
|
+
self.delayed_job = nil
|
108
|
+
end
|
109
|
+
clear_timer_attributes
|
110
|
+
save
|
111
|
+
end
|
112
|
+
|
113
|
+
def state?(name)
|
114
|
+
self.state == name.to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
def cancellable?
|
118
|
+
self.timer? or self.error? or self.delayed_job.nil?
|
119
|
+
end
|
120
|
+
|
121
|
+
def queued?
|
122
|
+
!self.delayed_job.nil?
|
123
|
+
end
|
124
|
+
|
125
|
+
def states
|
126
|
+
self.class.states
|
127
|
+
end
|
128
|
+
|
129
|
+
def transitions(state)
|
130
|
+
self.class.transitions(state)
|
131
|
+
end
|
132
|
+
|
133
|
+
def state_transitions
|
134
|
+
self.transitions(self.state.to_sym)
|
135
|
+
end
|
136
|
+
|
137
|
+
def schema
|
138
|
+
self.class.schema
|
139
|
+
end
|
140
|
+
|
141
|
+
def move_to(state_name)
|
142
|
+
throw :move_to, state_name
|
143
|
+
end
|
144
|
+
|
145
|
+
def retry_in(delay, error=nil)
|
146
|
+
throw :retry_in, delay: delay, error: error
|
147
|
+
end
|
148
|
+
|
149
|
+
def manual_task(description)
|
150
|
+
throw :task, description
|
151
|
+
end
|
152
|
+
|
153
|
+
protected
|
154
|
+
def run_state(state_name, allow_transition, allow_retry)
|
155
|
+
if state_name and respond_to?(state_name)
|
156
|
+
State.new(self, allow_transition, allow_retry).execute(self, state_name)
|
157
|
+
else
|
158
|
+
state_class = if state?(:start)
|
159
|
+
State::Start
|
160
|
+
elsif state?(:end)
|
161
|
+
State::End
|
162
|
+
elsif state?(:cancel)
|
163
|
+
State::Cancel
|
164
|
+
else
|
165
|
+
state_name ? next_state_class(state_name) : current_state_class
|
166
|
+
end
|
167
|
+
state = state_class.new(self, allow_transition, allow_retry)
|
168
|
+
state.execute(state, :run)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def current_state_class
|
173
|
+
"#{self.class.name}::#{state.camelize}".constantize
|
174
|
+
end
|
175
|
+
|
176
|
+
def next_state_class(state_name)
|
177
|
+
"#{self.class.name}::#{state_name.camelize}".constantize
|
178
|
+
end
|
179
|
+
|
180
|
+
def set_error(error=nil)
|
181
|
+
self.error = true
|
182
|
+
self.delayed_job = nil
|
183
|
+
set_error_details(error) if error
|
184
|
+
end
|
185
|
+
|
186
|
+
def set_timer
|
187
|
+
self.timer = true
|
188
|
+
end
|
189
|
+
|
190
|
+
def set_error_details(error)
|
191
|
+
self.failed_at = DateTime.now.utc
|
192
|
+
self.failed_in = state
|
193
|
+
self.backtrace = [error.message] | error.backtrace.take(5)
|
194
|
+
end
|
195
|
+
|
196
|
+
def clear_error_attributes
|
197
|
+
self.error = false
|
198
|
+
self.failed_at = nil
|
199
|
+
self.failed_in = nil
|
200
|
+
self.backtrace = nil
|
201
|
+
end
|
202
|
+
|
203
|
+
def clear_timer_attributes
|
204
|
+
self.timer = false
|
205
|
+
end
|
206
|
+
|
207
|
+
def clear_task_attributes
|
208
|
+
self.task = false
|
209
|
+
self.task_description = nil
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module LevelUp
|
2
|
+
class State
|
3
|
+
|
4
|
+
attr_accessor :job, :allow_transition, :allow_retry
|
5
|
+
|
6
|
+
def initialize(job, allow_transition, allow_retry)
|
7
|
+
self.job = job
|
8
|
+
self.allow_transition = allow_transition
|
9
|
+
self.allow_retry = allow_retry
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(receiver, method_name)
|
13
|
+
next_state = retry_params = task_description = nil
|
14
|
+
ActiveRecord::Base.transaction do
|
15
|
+
next_state = catch(:move_to) do
|
16
|
+
retry_params = catch(:retry_in) do
|
17
|
+
task_description = catch(:task) do
|
18
|
+
receiver.send(method_name)
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if self.allow_transition
|
28
|
+
self.job.next_state = next_state
|
29
|
+
end
|
30
|
+
|
31
|
+
if retry_params and self.allow_retry
|
32
|
+
self.job.retry_at = retry_params[:delay].from_now
|
33
|
+
end
|
34
|
+
|
35
|
+
if task_description
|
36
|
+
self.job.task = true
|
37
|
+
self.job.task_description = task_description
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def move_to(state_name)
|
42
|
+
self.job.move_to(state_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def retry_in(delay, error=nil)
|
46
|
+
self.job.retry_in(delay, error)
|
47
|
+
end
|
48
|
+
|
49
|
+
def manual_task(description)
|
50
|
+
self.job.manual_task(description)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<header>
|
2
|
+
<div class="top-bar">
|
3
|
+
<div class="container-12">
|
4
|
+
<div class="grid-2">
|
5
|
+
<h1>Level<span class="colored-part">Up</span></h1>
|
6
|
+
</div>
|
7
|
+
<div class="grid-3">
|
8
|
+
<ul>
|
9
|
+
<li><%= link_to "Dashboard", root_path, class: "app-link" %></li>
|
10
|
+
<li><%= link_to "Jobs", jobs_path, class: "app-link" %></li>
|
11
|
+
</ul>
|
12
|
+
</div>
|
13
|
+
<div class="grid-7">
|
14
|
+
<div class="search-bar">
|
15
|
+
<%= form_tag jobs_path, method: :get do %>
|
16
|
+
<%= text_field_tag 'search[key_contains]', nil, placeholder: "Search for jobs by key" %>
|
17
|
+
<%= submit_tag "Search" %>
|
18
|
+
<% end %>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
</header>
|
@@ -0,0 +1,36 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
3
|
+
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
4
|
+
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
|
5
|
+
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
|
6
|
+
<head>
|
7
|
+
<meta charset="utf-8">
|
8
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
9
|
+
<title>LevelUp</title>
|
10
|
+
<%= stylesheet_link_tag "level_up/application", :media => "all" %>
|
11
|
+
<%= javascript_include_tag "level_up/application" %>
|
12
|
+
|
13
|
+
<%= csrf_meta_tags %>
|
14
|
+
<meta name="viewport" content="width=device-width">
|
15
|
+
|
16
|
+
<!-- html5.js for IE less than 9 -->
|
17
|
+
<!--[if lt IE 9]>
|
18
|
+
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
|
19
|
+
<![endif]-->
|
20
|
+
|
21
|
+
<!-- css3-mediaqueries.js for IE less than 9 -->
|
22
|
+
<!--[if lt IE 9]>
|
23
|
+
<script src="http://css3-mediaqueries-js.googlecode.com/svn/trunk/css3-mediaqueries.js"></script>
|
24
|
+
<![endif]-->
|
25
|
+
|
26
|
+
</head>
|
27
|
+
<body>
|
28
|
+
|
29
|
+
<%= render "layouts/level_up/header" %>
|
30
|
+
|
31
|
+
<%= yield %>
|
32
|
+
|
33
|
+
<%= render "layouts/level_up/footer" %>
|
34
|
+
|
35
|
+
</body>
|
36
|
+
</html>
|
@@ -0,0 +1,85 @@
|
|
1
|
+
<div class="container-12">
|
2
|
+
<div class="grid-6">
|
3
|
+
<div class="job-status-stats queued">
|
4
|
+
<div class="title"><h2><span class="bullet"></span>Queued</h2></div>
|
5
|
+
<div class="number"><%= link_to LevelUp::Job.queued.size, jobs_path(search: {delayed_job_id_eq: true}) %></div>
|
6
|
+
<div class="stats">
|
7
|
+
<ul>
|
8
|
+
<li><label>Most recent:</label><%= job_entry(LevelUp::Job.queued.first) %></li>
|
9
|
+
<li><label>Oldest:</label><%= job_entry(LevelUp::Job.queued.last) %></li>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div class="grid-6">
|
16
|
+
<div class="job-status-stats timer">
|
17
|
+
<div class="title"><h2><span class="bullet"></span>Timer</h2></div>
|
18
|
+
<div class="number"><%= link_to LevelUp::Job.timer.size, jobs_path(search: {timer_eq: true}) %></div>
|
19
|
+
<div class="stats">
|
20
|
+
<ul>
|
21
|
+
<li><label>Most recent:</label><%= job_entry(LevelUp::Job.timer.first) %></li>
|
22
|
+
<li><label>Oldest:</label><%= job_entry(LevelUp::Job.timer.last) %></li>
|
23
|
+
</ul>
|
24
|
+
</div>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
|
28
|
+
<div class="grid-6">
|
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.task.size, jobs_path(search: {task_eq: true}) %></div>
|
32
|
+
<div class="stats">
|
33
|
+
<ul>
|
34
|
+
<li><label>Most recent:</label><%= job_entry(LevelUp::Job.task.first) %></li>
|
35
|
+
<li><label>Oldest:</label><%= job_entry(LevelUp::Job.task.last) %></li>
|
36
|
+
</ul>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<div class="grid-6">
|
42
|
+
<div class="job-status-stats error">
|
43
|
+
<div class="title"><h2><span class="bullet"></span>Error</h2></div>
|
44
|
+
<div class="number"><%= link_to LevelUp::Job.error.size, jobs_path(search: {error_eq: true}) %></div>
|
45
|
+
<div class="stats">
|
46
|
+
<ul>
|
47
|
+
<li><label>Most recent:</label><%= job_entry(LevelUp::Job.error.first) %></li>
|
48
|
+
<li><label>Oldest:</label><%= job_entry(LevelUp::Job.error.last) %></li>
|
49
|
+
</ul>
|
50
|
+
</div>
|
51
|
+
</div>
|
52
|
+
</div>
|
53
|
+
|
54
|
+
<div class="grid-12">
|
55
|
+
<div class="simple-panel">
|
56
|
+
<h2>Job Metrics</h2>
|
57
|
+
<div id="job-metrics"></div>
|
58
|
+
</div>
|
59
|
+
</div>
|
60
|
+
</div>
|
61
|
+
|
62
|
+
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
|
63
|
+
|
64
|
+
<script type="text/javascript">
|
65
|
+
google.load("visualization", "1", {packages:["corechart"]});
|
66
|
+
google.setOnLoadCallback(drawChart);
|
67
|
+
|
68
|
+
function drawChart() {
|
69
|
+
var data = google.visualization.arrayToDataTable([
|
70
|
+
['Date', 'Created', 'Started', 'Ended', 'Canceled'],
|
71
|
+
<% @metrics[:created].each do |date, count| %>
|
72
|
+
['<%= date.split("-")[1..-1].reverse.join("/") %>', <%= count %>, <%= @metrics[:started][date] %>, <%= @metrics[:ended][date] %>, <%= @metrics[:canceled][date] %>],
|
73
|
+
<% end %>
|
74
|
+
]);
|
75
|
+
|
76
|
+
var options = {
|
77
|
+
backgroundColor: '#fafbfb',
|
78
|
+
colors: ['#0048ac', '#482869', '#57a957', '#b40d28'],
|
79
|
+
fontName: 'Ubuntu'
|
80
|
+
};
|
81
|
+
|
82
|
+
var chart = new google.visualization.LineChart(document.getElementById('job-metrics'));
|
83
|
+
chart.draw(data, options);
|
84
|
+
}
|
85
|
+
</script>
|
@@ -0,0 +1,65 @@
|
|
1
|
+
<div class="container-12">
|
2
|
+
<div class="grid-12">
|
3
|
+
<div class="breadcrumbs">
|
4
|
+
<ul>
|
5
|
+
<li><%= link_to "Dashboard", root_path %></li> /
|
6
|
+
<li><%= link_to "Jobs", jobs_path %></li> /
|
7
|
+
<li><%= link_to @job.key, job_path(@job) %></li> /
|
8
|
+
<li>Edit</li>
|
9
|
+
</ul>
|
10
|
+
</div>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<div class="grid-8">
|
14
|
+
<div class="simple-panel job-details">
|
15
|
+
<h2><%= @job.class.name %> #<%= @job.id %></h2>
|
16
|
+
<%= form_for(:job, url: job_path(@job), method: :put) do |f| %>
|
17
|
+
<ul class="attributes-table form">
|
18
|
+
<li>
|
19
|
+
<%= f.label :key %>
|
20
|
+
<%= f.text_field :key %>
|
21
|
+
</li>
|
22
|
+
<li>
|
23
|
+
<%= f.label :state %>
|
24
|
+
<%= f.select :state, options_for_select(@job.states, @job.state) %>
|
25
|
+
</li>
|
26
|
+
<li>
|
27
|
+
<%= f.label :timer %>
|
28
|
+
<%= f.check_box :timer %>
|
29
|
+
</li>
|
30
|
+
<li>
|
31
|
+
<%= f.label :task %>
|
32
|
+
<%= f.check_box :task %>
|
33
|
+
</li>
|
34
|
+
<li>
|
35
|
+
<%= f.label :error %>
|
36
|
+
<%= f.check_box :error %>
|
37
|
+
</li>
|
38
|
+
<li>
|
39
|
+
<%= f.label :created_at %>
|
40
|
+
<%= f.datetime_select :created_at, include_seconds: true, include_blank: true%>
|
41
|
+
</li>
|
42
|
+
<li>
|
43
|
+
<%= f.label :updated_at %>
|
44
|
+
<%= f.datetime_select :updated_at, include_seconds: true, include_blank: true %>
|
45
|
+
</li>
|
46
|
+
<li>
|
47
|
+
<%= f.label :started_at %>
|
48
|
+
<%= f.datetime_select :started_at, include_seconds: true, include_blank: true %>
|
49
|
+
</li>
|
50
|
+
<li>
|
51
|
+
<%= f.label :ended_at %>
|
52
|
+
<%= f.datetime_select :ended_at, include_seconds: true, include_blank: true %>
|
53
|
+
</li>
|
54
|
+
<li>
|
55
|
+
<%= f.label :canceled_at %>
|
56
|
+
<%= f.datetime_select :canceled_at, include_seconds: true, include_blank: true %>
|
57
|
+
</li>
|
58
|
+
<li>
|
59
|
+
<%= f.submit "Update" %>
|
60
|
+
</li>
|
61
|
+
</ul>
|
62
|
+
<% end %>
|
63
|
+
</div>
|
64
|
+
</div>
|
65
|
+
</div>
|
@@ -0,0 +1,73 @@
|
|
1
|
+
<div class="container-12">
|
2
|
+
<div class="grid-12">
|
3
|
+
<div class="breadcrumbs">
|
4
|
+
<ul>
|
5
|
+
<li><%= link_to "Dashboard", root_path %></li> /
|
6
|
+
<li>Jobs</li>
|
7
|
+
</ul>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="grid-12">
|
12
|
+
<div class="search-form">
|
13
|
+
<%= form_for @search, url: jobs_path, id: "search-form" do |f| %>
|
14
|
+
<%= f.text_field :key_contains, placeholder: "Search by key", class: "search-field" %>
|
15
|
+
<%= f.submit "Search", class: "search-button" %>
|
16
|
+
<div class="filters">
|
17
|
+
<%= f.label :delayed_job_id_eq, "Queued" %>:
|
18
|
+
<%= f.select :delayed_job_id_eq,
|
19
|
+
options_for_select([["Any", nil], ["Yes", true], ["No", false]], @search_params[:delayed_job_id_eq]) %>
|
20
|
+
<%= f.label :timer_eq, "Timer" %>:
|
21
|
+
<%= f.select :timer_eq,
|
22
|
+
options_for_select([["Any", nil], ["Yes", true], ["No", false]], @search_params[:timer_eq]) %>
|
23
|
+
|
24
|
+
<%= f.label :task_eq, "Task" %>:
|
25
|
+
<%= f.select :task_eq,
|
26
|
+
options_for_select([["Any", nil], ["Yes", true], ["No", false]], @search_params[:task_eq]) %>
|
27
|
+
|
28
|
+
<%= f.label :error_eq, "Error" %>:
|
29
|
+
<%= f.select :error_eq,
|
30
|
+
options_for_select([["Any", nil], ["Yes", true], ["No", false]], @search_params[:error_eq]) %>
|
31
|
+
<%= link_to "Reset filters", jobs_path, class: "reset-link" %>
|
32
|
+
</div>
|
33
|
+
<% end %>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
<div class="grid-12">
|
37
|
+
<%= paginate @jobs %>
|
38
|
+
<table class="table-bordered jobs">
|
39
|
+
<thead>
|
40
|
+
<tr>
|
41
|
+
<th>Key</th>
|
42
|
+
<th>Type</th>
|
43
|
+
<th>State</th>
|
44
|
+
<th>Queued</th>
|
45
|
+
<th>Timer</th>
|
46
|
+
<th>Task</th>
|
47
|
+
<th>Error</th>
|
48
|
+
<th>Created at</th>
|
49
|
+
</tr>
|
50
|
+
</thead>
|
51
|
+
<tbody>
|
52
|
+
<% @jobs.each do |job| %>
|
53
|
+
<tr>
|
54
|
+
<td><%= link_to job.key, job_path(job) %></td>
|
55
|
+
<td><%= job.type %></td>
|
56
|
+
<td><%= job.state.humanize.downcase %></td>
|
57
|
+
<td><%= status_tag job.delayed_job, :purple %></td>
|
58
|
+
<td><%= status_tag job.timer, :blue %></td>
|
59
|
+
<td><%= status_tag job.task, :orange %></td>
|
60
|
+
<td><%= status_tag job.error, :red %></td>
|
61
|
+
<td><%= l(job.created_at, format: :long) %></td>
|
62
|
+
</tr>
|
63
|
+
<% end %>
|
64
|
+
<% if @jobs.empty? %>
|
65
|
+
<tr>
|
66
|
+
<td colspan="8">No jobs found</td>
|
67
|
+
</tr>
|
68
|
+
<% end %>
|
69
|
+
</tbody>
|
70
|
+
</table>
|
71
|
+
<%= paginate @jobs %>
|
72
|
+
</div>
|
73
|
+
</div>
|
@@ -0,0 +1,93 @@
|
|
1
|
+
<div class="container-12">
|
2
|
+
<div class="grid-12">
|
3
|
+
<div class="breadcrumbs">
|
4
|
+
<ul>
|
5
|
+
<li><%= link_to "Dashboard", root_path %></li> /
|
6
|
+
<li><%= link_to "Jobs", jobs_path %></li> /
|
7
|
+
<li><%= @job.key %></li>
|
8
|
+
</ul>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<div class="grid-8">
|
13
|
+
<div class="simple-panel job-details">
|
14
|
+
<h2>Job details</h2>
|
15
|
+
<ul class="attributes-table">
|
16
|
+
<li><label>ID:</label><%= @job.id %></li>
|
17
|
+
<li><label>Key:</label><%= @job.key %></li>
|
18
|
+
<li><label>Type:</label><%= @job.type %></li>
|
19
|
+
<li><label>State:</label><%= @job.state.humanize.downcase %></li>
|
20
|
+
<li><label>Queued:</label><%= status_tag @job.delayed_job, :purple %></li>
|
21
|
+
<li><label>Timer:</label><%= status_tag @job.timer, :blue %></li>
|
22
|
+
<li><label>Task:</label><%= status_tag @job.task, :orange %></li>
|
23
|
+
<li><label>Error:</label><%= status_tag @job.error, :red %></li>
|
24
|
+
<li><label>Created at:</label><%= l(@job.created_at) %></li>
|
25
|
+
<li><label>Updated at:</label><%= l(@job.updated_at) %></li>
|
26
|
+
<li><label>Started at:</label><%= @job.started_at ? l(@job.started_at) : "-" %></li>
|
27
|
+
<li><label>Ended at:</label><%= @job.ended_at ? l(@job.ended_at) : "-" %></li>
|
28
|
+
<li><label>Canceled at:</label><%= @job.canceled_at ? l(@job.canceled_at) : "-" %></li>
|
29
|
+
</ul>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<% if @job.error? %>
|
33
|
+
<div class="simple-panel job-error-log">
|
34
|
+
<h2>Error Log</h2>
|
35
|
+
<ul class="attributes-table">
|
36
|
+
<li class="error"><label>Failed in</label><%= @job.failed_in %></li>
|
37
|
+
<li class="error"><label>Failed at</label><%= l(@job.failed_at) %></li>
|
38
|
+
<li class="error no-border"><label>Backtrace</label><%= @job.backtrace.first %></li>
|
39
|
+
<% @job.backtrace[1..-1].each do |trace| %>
|
40
|
+
<li class="no-border"><label></label><%= trace %></li>
|
41
|
+
<% end %>
|
42
|
+
</ul>
|
43
|
+
</div>
|
44
|
+
<% end %>
|
45
|
+
</div>
|
46
|
+
|
47
|
+
<div class="grid-4">
|
48
|
+
<div class="simple-panel job-commands">
|
49
|
+
<h2>Job commands</h2>
|
50
|
+
<% if @job.state?(:end) %>
|
51
|
+
Ended
|
52
|
+
<% elsif @job.queued? %>
|
53
|
+
<div class="command">
|
54
|
+
<%= form_tag unqueue_job_path(@job) do %>
|
55
|
+
<%= submit_tag "Unqueue" %>
|
56
|
+
<% end %>
|
57
|
+
</div>
|
58
|
+
<% else %>
|
59
|
+
<div class="command">
|
60
|
+
<%= form_tag run_job_path(@job) do %>
|
61
|
+
<%= submit_tag "Rerun the current state" %>
|
62
|
+
<% end %>
|
63
|
+
</div>
|
64
|
+
<div class="command">
|
65
|
+
<%= form_tag reboot_job_path(@job) do %>
|
66
|
+
<%= submit_tag "Reboot" %>
|
67
|
+
<% end %>
|
68
|
+
</div>
|
69
|
+
<% @job.state_transitions.each do |t| %>
|
70
|
+
<div class="command">
|
71
|
+
<%= form_tag move_job_path(@job, state: t) do %>
|
72
|
+
<% submit_tag "Move to: #{t.to_s.titleize}" %>
|
73
|
+
<% end %>
|
74
|
+
</div>
|
75
|
+
<% end %>
|
76
|
+
<% end %>
|
77
|
+
</div>
|
78
|
+
|
79
|
+
<div class="job-actions">
|
80
|
+
<%= link_to "Edit", edit_job_path(@job), class: "action" %>
|
81
|
+
<%= link_to "Destroy", job_path(@job), method: :delete, class: "action delete", data: {confirm: "Are you sure?"} %>
|
82
|
+
</div>
|
83
|
+
</div>
|
84
|
+
|
85
|
+
<div class="grid-12">
|
86
|
+
<div class="simple-panel">
|
87
|
+
<h2>Job Flow</h2>
|
88
|
+
<div class="svg-graph">
|
89
|
+
<object id="svg-object" data="<%= graphviz_job_path(@job, format: :svg) %>" type="image/svg+xml"></object>
|
90
|
+
</div>
|
91
|
+
</div>
|
92
|
+
</div>
|
93
|
+
</div>
|