level_up 0.1.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 +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>
|