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.
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>