level_up 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/CHANGELOG.txt +2 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.md +190 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/images/level_up/search.png +0 -0
  6. data/app/assets/javascripts/level_up/application.js +15 -0
  7. data/app/assets/javascripts/level_up/home.js +2 -0
  8. data/app/assets/javascripts/level_up/jobs.js +2 -0
  9. data/app/assets/stylesheets/level_up/application.css.scss +446 -0
  10. data/app/assets/stylesheets/level_up/home.css.scss +119 -0
  11. data/app/assets/stylesheets/level_up/jobs.css.scss +159 -0
  12. data/app/assets/stylesheets/level_up/rwdgrid.css +403 -0
  13. data/app/controllers/level_up/application_controller.rb +4 -0
  14. data/app/controllers/level_up/home_controller.rb +22 -0
  15. data/app/controllers/level_up/jobs_controller.rb +117 -0
  16. data/app/helpers/level_up/application_helper.rb +7 -0
  17. data/app/helpers/level_up/home_helper.rb +7 -0
  18. data/app/helpers/level_up/jobs_helper.rb +4 -0
  19. data/app/models/level_up/job.rb +212 -0
  20. data/app/models/level_up/state/cancel.rb +7 -0
  21. data/app/models/level_up/state/end.rb +7 -0
  22. data/app/models/level_up/state/start.rb +9 -0
  23. data/app/models/level_up/state.rb +53 -0
  24. data/app/views/layouts/level_up/_footer.html.erb +9 -0
  25. data/app/views/layouts/level_up/_header.html.erb +23 -0
  26. data/app/views/layouts/level_up/application.html.erb +36 -0
  27. data/app/views/level_up/home/index.html.erb +85 -0
  28. data/app/views/level_up/home/workflow.html.erb +5 -0
  29. data/app/views/level_up/jobs/edit.html.erb +65 -0
  30. data/app/views/level_up/jobs/index.html.erb +73 -0
  31. data/app/views/level_up/jobs/show.html.erb +93 -0
  32. data/config/routes.rb +13 -0
  33. data/db/migrate/20130212111454_create_level_up_jobs.rb +27 -0
  34. data/lib/level_up/configuration.rb +27 -0
  35. data/lib/level_up/engine.rb +17 -0
  36. data/lib/level_up/version.rb +3 -0
  37. data/lib/level_up.rb +7 -0
  38. data/lib/tasks/level_up_tasks.rake +4 -0
  39. data/test/dummy/README.rdoc +261 -0
  40. data/test/dummy/Rakefile +7 -0
  41. data/test/dummy/app/assets/javascripts/application.js +15 -0
  42. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  43. data/test/dummy/app/controllers/application_controller.rb +3 -0
  44. data/test/dummy/app/helpers/application_helper.rb +2 -0
  45. data/test/dummy/app/jobs/account_banish.rb +18 -0
  46. data/test/dummy/app/jobs/account_downgrade.rb +13 -0
  47. data/test/dummy/app/jobs/account_upgrade.rb +15 -0
  48. data/test/dummy/app/jobs/mailing_list_subscription.rb +15 -0
  49. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  50. data/test/dummy/config/application.rb +59 -0
  51. data/test/dummy/config/boot.rb +10 -0
  52. data/test/dummy/config/database.yml +25 -0
  53. data/test/dummy/config/environment.rb +5 -0
  54. data/test/dummy/config/environments/development.rb +41 -0
  55. data/test/dummy/config/environments/production.rb +67 -0
  56. data/test/dummy/config/environments/test.rb +37 -0
  57. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  58. data/test/dummy/config/initializers/inflections.rb +15 -0
  59. data/test/dummy/config/initializers/mime_types.rb +5 -0
  60. data/test/dummy/config/initializers/secret_token.rb +7 -0
  61. data/test/dummy/config/initializers/session_store.rb +8 -0
  62. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  63. data/test/dummy/config/locales/en.yml +5 -0
  64. data/test/dummy/config/routes.rb +4 -0
  65. data/test/dummy/config.ru +4 -0
  66. data/test/dummy/db/migrate/20130212113009_create_delayed_jobs.rb +22 -0
  67. data/test/dummy/db/migrate/20130215111404_create_level_up_jobs.level_up.rb +28 -0
  68. data/test/dummy/db/schema.rb +57 -0
  69. data/test/dummy/public/404.html +26 -0
  70. data/test/dummy/public/422.html +26 -0
  71. data/test/dummy/public/500.html +25 -0
  72. data/test/dummy/public/favicon.ico +0 -0
  73. data/test/dummy/script/delayed_job +5 -0
  74. data/test/dummy/script/rails +6 -0
  75. data/test/fixtures/level_up/jobs.yml +11 -0
  76. data/test/functional/level_up/home_controller_test.rb +9 -0
  77. data/test/functional/level_up/jobs_controller_test.rb +9 -0
  78. data/test/integration/navigation_test.rb +10 -0
  79. data/test/level_up_test.rb +7 -0
  80. data/test/test_helper.rb +15 -0
  81. data/test/unit/helpers/level_up/home_helper_test.rb +6 -0
  82. data/test/unit/helpers/level_up/jobs_helper_test.rb +6 -0
  83. data/test/unit/level_up/job_test.rb +208 -0
  84. 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,7 @@
1
+ module LevelUp
2
+ class State::Cancel < LevelUp::State
3
+ def run
4
+ job.canceled_at = DateTime.now.utc
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module LevelUp
2
+ class State::End < LevelUp::State
3
+ def run
4
+ job.ended_at = DateTime.now.utc
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module LevelUp
2
+ class State::Start < LevelUp::State
3
+ def run
4
+ job.started_at = DateTime.now.utc
5
+ next_state = job.transitions(:start).first
6
+ move_to next_state
7
+ end
8
+ end
9
+ 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,9 @@
1
+ <footer>
2
+ <div class="container-12">
3
+ <div class="grid-12">
4
+ <p class="powered-by">
5
+ Powered by <%= link_to "LevelUp", "#" %> <%= LevelUp::VERSION %>
6
+ </p>
7
+ </div>
8
+ </div>
9
+ </footer>
@@ -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,5 @@
1
+ <div class="container-12">
2
+ <div class="grid-12">
3
+ <%= image_tag graph_path %>
4
+ </div>
5
+ </div>
@@ -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>