dj_mon 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 AKSHAY RAWAT
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,14 @@
1
+ = DJ Mon
2
+
3
+ A Rails engine based frontend for Delayed Job.
4
+
5
+ Coming very soon.
6
+
7
+ = Notes
8
+
9
+ - app.config.assets.precompile += ['dj_mon.js', 'dj_mon.css'] for apps <= 3.1 or if config.assets.initialize_on_precompile is set to false
10
+ - gem install dj_mon
11
+ - mount DjMon::Engine => 'dj_mon'
12
+ - basic auth using initializers
13
+ --MyApp::Application.config.dj_mon.username = "foo"
14
+ --MyApp::Application.config.dj_mon.password = "bar"
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rake'
10
+ require 'rake/rdoctask'
11
+
12
+ require 'rake/testtask'
13
+
14
+ Rake::TestTask.new(:test) do |t|
15
+ t.libs << 'lib'
16
+ t.libs << 'test'
17
+ t.pattern = 'test/**/*_test.rb'
18
+ t.verbose = false
19
+ end
20
+
21
+ task :default => :test
22
+
23
+ Rake::RDocTask.new(:rdoc) do |rdoc|
24
+ rdoc.rdoc_dir = 'rdoc'
25
+ rdoc.title = 'DjMon'
26
+ rdoc.options << '--line-numbers' << '--inline-source'
27
+ rdoc.rdoc_files.include('README.rdoc')
28
+ rdoc.rdoc_files.include('lib/**/*.rb')
29
+ end
@@ -0,0 +1,25 @@
1
+ //= require jquery
2
+ //= require bootstrap-tab
3
+ //= require mustache
4
+
5
+ $(function(){
6
+
7
+ $('a[data-toggle="tab"]').bind('shown', function(e) {
8
+ var currentTab = e.target;
9
+ var tabContent = $($(currentTab).attr('href'));
10
+ var dataUrl = tabContent.data('url');
11
+
12
+ $.getJSON(dataUrl).success(function(data){
13
+ var template = $('#delayed_jobs_template').html();
14
+ if(data.length > 0)
15
+ var output = Mustache.render(template, data);
16
+ else
17
+ var output = "<div class='alert alert-info centered'>No Jobs</div>";
18
+ tabContent.html(output);
19
+ });
20
+ })
21
+
22
+ $('.nav.nav-tabs li.active a[data-toggle="tab"]').trigger('shown');
23
+
24
+ })
25
+
@@ -0,0 +1,536 @@
1
+ /*!
2
+ * mustache.js - Logic-less {{mustache}} templates with JavaScript
3
+ * http://github.com/janl/mustache.js
4
+ */
5
+ var Mustache = (typeof module !== "undefined" && module.exports) || {};
6
+
7
+ (function (exports) {
8
+
9
+ exports.name = "mustache.js";
10
+ exports.version = "0.5.0-dev";
11
+ exports.tags = ["{{", "}}"];
12
+ exports.parse = parse;
13
+ exports.compile = compile;
14
+ exports.render = render;
15
+ exports.clearCache = clearCache;
16
+
17
+ // This is here for backwards compatibility with 0.4.x.
18
+ exports.to_html = function (template, view, partials, send) {
19
+ var result = render(template, view, partials);
20
+
21
+ if (typeof send === "function") {
22
+ send(result);
23
+ } else {
24
+ return result;
25
+ }
26
+ };
27
+
28
+ var _toString = Object.prototype.toString;
29
+ var _isArray = Array.isArray;
30
+ var _forEach = Array.prototype.forEach;
31
+ var _trim = String.prototype.trim;
32
+
33
+ var isArray;
34
+ if (_isArray) {
35
+ isArray = _isArray;
36
+ } else {
37
+ isArray = function (obj) {
38
+ return _toString.call(obj) === "[object Array]";
39
+ };
40
+ }
41
+
42
+ var forEach;
43
+ if (_forEach) {
44
+ forEach = function (obj, callback, scope) {
45
+ return _forEach.call(obj, callback, scope);
46
+ };
47
+ } else {
48
+ forEach = function (obj, callback, scope) {
49
+ for (var i = 0, len = obj.length; i < len; ++i) {
50
+ callback.call(scope, obj[i], i, obj);
51
+ }
52
+ };
53
+ }
54
+
55
+ var spaceRe = /^\s*$/;
56
+
57
+ function isWhitespace(string) {
58
+ return spaceRe.test(string);
59
+ }
60
+
61
+ var trim;
62
+ if (_trim) {
63
+ trim = function (string) {
64
+ return string == null ? "" : _trim.call(string);
65
+ };
66
+ } else {
67
+ var trimLeft, trimRight;
68
+
69
+ if (isWhitespace("\xA0")) {
70
+ trimLeft = /^\s+/;
71
+ trimRight = /\s+$/;
72
+ } else {
73
+ // IE doesn't match non-breaking spaces with \s, thanks jQuery.
74
+ trimLeft = /^[\s\xA0]+/;
75
+ trimRight = /[\s\xA0]+$/;
76
+ }
77
+
78
+ trim = function (string) {
79
+ return string == null ? "" :
80
+ String(string).replace(trimLeft, "").replace(trimRight, "");
81
+ };
82
+ }
83
+
84
+ var escapeMap = {
85
+ "&": "&amp;",
86
+ "<": "&lt;",
87
+ ">": "&gt;",
88
+ '"': '&quot;',
89
+ "'": '&#39;'
90
+ };
91
+
92
+ function escapeHTML(string) {
93
+ return String(string).replace(/&(?!\w+;)|[<>"']/g, function (s) {
94
+ return escapeMap[s] || s;
95
+ });
96
+ }
97
+
98
+ /**
99
+ * Adds the `template`, `line`, and `file` properties to the given error
100
+ * object and alters the message to provide more useful debugging information.
101
+ */
102
+ function debug(e, template, line, file) {
103
+ file = file || "<template>";
104
+
105
+ var lines = template.split("\n"),
106
+ start = Math.max(line - 3, 0),
107
+ end = Math.min(lines.length, line + 3),
108
+ context = lines.slice(start, end);
109
+
110
+ var c;
111
+ for (var i = 0, len = context.length; i < len; ++i) {
112
+ c = i + start + 1;
113
+ context[i] = (c === line ? " >> " : " ") + context[i];
114
+ }
115
+
116
+ e.template = template;
117
+ e.line = line;
118
+ e.file = file;
119
+ e.message = [file + ":" + line, context.join("\n"), "", e.message].join("\n");
120
+
121
+ return e;
122
+ }
123
+
124
+ /**
125
+ * Looks up the value of the given `name` in the given context `stack`.
126
+ */
127
+ function lookup(name, stack, defaultValue) {
128
+ if (name === ".") {
129
+ return stack[stack.length - 1];
130
+ }
131
+
132
+ var names = name.split(".");
133
+ var lastIndex = names.length - 1;
134
+ var target = names[lastIndex];
135
+
136
+ var value, context, i = stack.length, j, localStack;
137
+ while (i) {
138
+ localStack = stack.slice(0);
139
+ context = stack[--i];
140
+
141
+ j = 0;
142
+ while (j < lastIndex) {
143
+ context = context[names[j++]];
144
+
145
+ if (context == null) {
146
+ break;
147
+ }
148
+
149
+ localStack.push(context);
150
+ }
151
+
152
+ if (context && typeof context === "object" && target in context) {
153
+ value = context[target];
154
+ break;
155
+ }
156
+ }
157
+
158
+ // If the value is a function, call it in the current context.
159
+ if (typeof value === "function") {
160
+ value = value.call(localStack[localStack.length - 1]);
161
+ }
162
+
163
+ if (value == null) {
164
+ return defaultValue;
165
+ }
166
+
167
+ return value;
168
+ }
169
+
170
+ function renderSection(name, stack, callback, inverted) {
171
+ var buffer = "";
172
+ var value = lookup(name, stack);
173
+
174
+ if (inverted) {
175
+ // From the spec: inverted sections may render text once based on the
176
+ // inverse value of the key. That is, they will be rendered if the key
177
+ // doesn't exist, is false, or is an empty list.
178
+ if (value == null || value === false || (isArray(value) && value.length === 0)) {
179
+ buffer += callback();
180
+ }
181
+ } else if (isArray(value)) {
182
+ forEach(value, function (value) {
183
+ stack.push(value);
184
+ buffer += callback();
185
+ stack.pop();
186
+ });
187
+ } else if (typeof value === "object") {
188
+ stack.push(value);
189
+ buffer += callback();
190
+ stack.pop();
191
+ } else if (typeof value === "function") {
192
+ var scope = stack[stack.length - 1];
193
+ var scopedRender = function (template) {
194
+ return render(template, scope);
195
+ };
196
+ buffer += value.call(scope, callback(), scopedRender) || "";
197
+ } else if (value) {
198
+ buffer += callback();
199
+ }
200
+
201
+ return buffer;
202
+ }
203
+
204
+ /**
205
+ * Parses the given `template` and returns the source of a function that,
206
+ * with the proper arguments, will render the template. Recognized options
207
+ * include the following:
208
+ *
209
+ * - file The name of the file the template comes from (displayed in
210
+ * error messages)
211
+ * - tags An array of open and close tags the `template` uses. Defaults
212
+ * to the value of Mustache.tags
213
+ * - debug Set `true` to log the body of the generated function to the
214
+ * console
215
+ * - space Set `true` to preserve whitespace from lines that otherwise
216
+ * contain only a {{tag}}. Defaults to `false`
217
+ */
218
+ function parse(template, options) {
219
+ options = options || {};
220
+
221
+ var tags = options.tags || exports.tags,
222
+ openTag = tags[0],
223
+ closeTag = tags[tags.length - 1];
224
+
225
+ var code = [
226
+ 'var buffer = "";', // output buffer
227
+ "\nvar line = 1;", // keep track of source line number
228
+ "\ntry {",
229
+ '\nbuffer += "'
230
+ ];
231
+
232
+ var spaces = [], // indices of whitespace in code on the current line
233
+ hasTag = false, // is there a {{tag}} on the current line?
234
+ nonSpace = false; // is there a non-space char on the current line?
235
+
236
+ // Strips all space characters from the code array for the current line
237
+ // if there was a {{tag}} on it and otherwise only spaces.
238
+ var stripSpace = function () {
239
+ if (hasTag && !nonSpace && !options.space) {
240
+ while (spaces.length) {
241
+ code.splice(spaces.pop(), 1);
242
+ }
243
+ } else {
244
+ spaces = [];
245
+ }
246
+
247
+ hasTag = false;
248
+ nonSpace = false;
249
+ };
250
+
251
+ var sectionStack = [], updateLine, nextOpenTag, nextCloseTag;
252
+
253
+ var setTags = function (source) {
254
+ tags = trim(source).split(/\s+/);
255
+ nextOpenTag = tags[0];
256
+ nextCloseTag = tags[tags.length - 1];
257
+ };
258
+
259
+ var includePartial = function (source) {
260
+ code.push(
261
+ '";',
262
+ updateLine,
263
+ '\nvar partial = partials["' + trim(source) + '"];',
264
+ '\nif (partial) {',
265
+ '\n buffer += render(partial,stack[stack.length - 1],partials);',
266
+ '\n}',
267
+ '\nbuffer += "'
268
+ );
269
+ };
270
+
271
+ var openSection = function (source, inverted) {
272
+ var name = trim(source);
273
+
274
+ if (name === "") {
275
+ throw debug(new Error("Section name may not be empty"), template, line, options.file);
276
+ }
277
+
278
+ sectionStack.push({name: name, inverted: inverted});
279
+
280
+ code.push(
281
+ '";',
282
+ updateLine,
283
+ '\nvar name = "' + name + '";',
284
+ '\nvar callback = (function () {',
285
+ '\n return function () {',
286
+ '\n var buffer = "";',
287
+ '\nbuffer += "'
288
+ );
289
+ };
290
+
291
+ var openInvertedSection = function (source) {
292
+ openSection(source, true);
293
+ };
294
+
295
+ var closeSection = function (source) {
296
+ var name = trim(source);
297
+ var openName = sectionStack.length != 0 && sectionStack[sectionStack.length - 1].name;
298
+
299
+ if (!openName || name != openName) {
300
+ throw debug(new Error('Section named "' + name + '" was never opened'), template, line, options.file);
301
+ }
302
+
303
+ var section = sectionStack.pop();
304
+
305
+ code.push(
306
+ '";',
307
+ '\n return buffer;',
308
+ '\n };',
309
+ '\n})();'
310
+ );
311
+
312
+ if (section.inverted) {
313
+ code.push("\nbuffer += renderSection(name,stack,callback,true);");
314
+ } else {
315
+ code.push("\nbuffer += renderSection(name,stack,callback);");
316
+ }
317
+
318
+ code.push('\nbuffer += "');
319
+ };
320
+
321
+ var sendPlain = function (source) {
322
+ code.push(
323
+ '";',
324
+ updateLine,
325
+ '\nbuffer += lookup("' + trim(source) + '",stack,"");',
326
+ '\nbuffer += "'
327
+ );
328
+ };
329
+
330
+ var sendEscaped = function (source) {
331
+ code.push(
332
+ '";',
333
+ updateLine,
334
+ '\nbuffer += escapeHTML(lookup("' + trim(source) + '",stack,""));',
335
+ '\nbuffer += "'
336
+ );
337
+ };
338
+
339
+ var line = 1, c, callback;
340
+ for (var i = 0, len = template.length; i < len; ++i) {
341
+ if (template.slice(i, i + openTag.length) === openTag) {
342
+ i += openTag.length;
343
+ c = template.substr(i, 1);
344
+ updateLine = '\nline = ' + line + ';';
345
+ nextOpenTag = openTag;
346
+ nextCloseTag = closeTag;
347
+ hasTag = true;
348
+
349
+ switch (c) {
350
+ case "!": // comment
351
+ i++;
352
+ callback = null;
353
+ break;
354
+ case "=": // change open/close tags, e.g. {{=<% %>=}}
355
+ i++;
356
+ closeTag = "=" + closeTag;
357
+ callback = setTags;
358
+ break;
359
+ case ">": // include partial
360
+ i++;
361
+ callback = includePartial;
362
+ break;
363
+ case "#": // start section
364
+ i++;
365
+ callback = openSection;
366
+ break;
367
+ case "^": // start inverted section
368
+ i++;
369
+ callback = openInvertedSection;
370
+ break;
371
+ case "/": // end section
372
+ i++;
373
+ callback = closeSection;
374
+ break;
375
+ case "{": // plain variable
376
+ closeTag = "}" + closeTag;
377
+ // fall through
378
+ case "&": // plain variable
379
+ i++;
380
+ nonSpace = true;
381
+ callback = sendPlain;
382
+ break;
383
+ default: // escaped variable
384
+ nonSpace = true;
385
+ callback = sendEscaped;
386
+ }
387
+
388
+ var end = template.indexOf(closeTag, i);
389
+
390
+ if (end === -1) {
391
+ throw debug(new Error('Tag "' + openTag + '" was not closed properly'), template, line, options.file);
392
+ }
393
+
394
+ var source = template.substring(i, end);
395
+
396
+ if (callback) {
397
+ callback(source);
398
+ }
399
+
400
+ // Maintain line count for \n in source.
401
+ var n = 0;
402
+ while (~(n = source.indexOf("\n", n))) {
403
+ line++;
404
+ n++;
405
+ }
406
+
407
+ i = end + closeTag.length - 1;
408
+ openTag = nextOpenTag;
409
+ closeTag = nextCloseTag;
410
+ } else {
411
+ c = template.substr(i, 1);
412
+
413
+ switch (c) {
414
+ case '"':
415
+ case "\\":
416
+ nonSpace = true;
417
+ code.push("\\" + c);
418
+ break;
419
+ case "\r":
420
+ // Ignore carriage returns.
421
+ break;
422
+ case "\n":
423
+ spaces.push(code.length);
424
+ code.push("\\n");
425
+ stripSpace(); // Check for whitespace on the current line.
426
+ line++;
427
+ break;
428
+ default:
429
+ if (isWhitespace(c)) {
430
+ spaces.push(code.length);
431
+ } else {
432
+ nonSpace = true;
433
+ }
434
+
435
+ code.push(c);
436
+ }
437
+ }
438
+ }
439
+
440
+ if (sectionStack.length != 0) {
441
+ throw debug(new Error('Section "' + sectionStack[sectionStack.length - 1].name + '" was not closed properly'), template, line, options.file);
442
+ }
443
+
444
+ // Clean up any whitespace from a closing {{tag}} that was at the end
445
+ // of the template without a trailing \n.
446
+ stripSpace();
447
+
448
+ code.push(
449
+ '";',
450
+ "\nreturn buffer;",
451
+ "\n} catch (e) { throw {error: e, line: line}; }"
452
+ );
453
+
454
+ // Ignore `buffer += "";` statements.
455
+ var body = code.join("").replace(/buffer \+= "";\n/g, "");
456
+
457
+ if (options.debug) {
458
+ if (typeof console != "undefined" && console.log) {
459
+ console.log(body);
460
+ } else if (typeof print === "function") {
461
+ print(body);
462
+ }
463
+ }
464
+
465
+ return body;
466
+ }
467
+
468
+ /**
469
+ * Used by `compile` to generate a reusable function for the given `template`.
470
+ */
471
+ function _compile(template, options) {
472
+ var args = "view,partials,stack,lookup,escapeHTML,renderSection,render";
473
+ var body = parse(template, options);
474
+ var fn = new Function(args, body);
475
+
476
+ // This anonymous function wraps the generated function so we can do
477
+ // argument coercion, setup some variables, and handle any errors
478
+ // encountered while executing it.
479
+ return function (view, partials) {
480
+ partials = partials || {};
481
+
482
+ var stack = [view]; // context stack
483
+
484
+ try {
485
+ return fn(view, partials, stack, lookup, escapeHTML, renderSection, render);
486
+ } catch (e) {
487
+ throw debug(e.error, template, e.line, options.file);
488
+ }
489
+ };
490
+ }
491
+
492
+ // Cache of pre-compiled templates.
493
+ var _cache = {};
494
+
495
+ /**
496
+ * Clear the cache of compiled templates.
497
+ */
498
+ function clearCache() {
499
+ _cache = {};
500
+ }
501
+
502
+ /**
503
+ * Compiles the given `template` into a reusable function using the given
504
+ * `options`. In addition to the options accepted by Mustache.parse,
505
+ * recognized options include the following:
506
+ *
507
+ * - cache Set `false` to bypass any pre-compiled version of the given
508
+ * template. Otherwise, a given `template` string will be cached
509
+ * the first time it is parsed
510
+ */
511
+ function compile(template, options) {
512
+ options = options || {};
513
+
514
+ // Use a pre-compiled version from the cache if we have one.
515
+ if (options.cache !== false) {
516
+ if (!_cache[template]) {
517
+ _cache[template] = _compile(template, options);
518
+ }
519
+
520
+ return _cache[template];
521
+ }
522
+
523
+ return _compile(template, options);
524
+ }
525
+
526
+ /**
527
+ * High-level function that renders the given `template` using the given
528
+ * `view` and `partials`. If you need to use any of the template options (see
529
+ * `compile` above), you must compile in a separate step, and then call that
530
+ * compiled function.
531
+ */
532
+ function render(template, view, partials) {
533
+ return compile(template)(view, partials);
534
+ }
535
+
536
+ })(Mustache);
@@ -0,0 +1,21 @@
1
+ @import "bootstrap";
2
+
3
+ body {
4
+ padding: 90px;
5
+ }
6
+
7
+ table.table {
8
+ td, th {
9
+ text-align: center;
10
+ }
11
+ th.date, td.date {
12
+ text-align: left;
13
+ }
14
+ td.date {
15
+ font-size: 90%;
16
+ }
17
+ }
18
+
19
+ .centered {
20
+ text-align: center;
21
+ }
@@ -0,0 +1,38 @@
1
+ module DjMon
2
+ class DelayedJobsController < ActionController::Base
3
+ respond_to :json
4
+ layout 'dj_mon'
5
+
6
+ before_filter :authenticate
7
+
8
+ def index
9
+ @delayed_jobs = []
10
+ end
11
+
12
+ def all
13
+ respond_with Delayed::Job.all
14
+ end
15
+
16
+ def failed
17
+ respond_with Delayed::Job.where('delayed_jobs.failed_at IS NOT NULL')
18
+ end
19
+
20
+ def active
21
+ respond_with Delayed::Job.where('delayed_jobs.locked_by IS NOT NULL')
22
+ end
23
+
24
+ def queued
25
+ respond_with Delayed::Job.where('delayed_jobs.failed_at IS NULL AND delayed_jobs.locked_by IS NULL')
26
+ end
27
+
28
+ protected
29
+
30
+ def authenticate
31
+ authenticate_or_request_with_http_basic do |username, password|
32
+ username == Rails.configuration.dj_mon.username &&
33
+ password == Rails.configuration.dj_mon.password
34
+ end
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,59 @@
1
+ %header
2
+ %p.lead Delayed Jobs
3
+ .subnav
4
+ %ul.nav.nav-tabs
5
+ %li.active
6
+ = link_to "All", "#all", "data-toggle" => "tab"
7
+ %li
8
+ = link_to "Failed", "#failed", "data-toggle" => "tab"
9
+ %li
10
+ = link_to "Queued", "#queued", "data-toggle" => "tab"
11
+ %li
12
+ = link_to "Active", "#active", "data-toggle" => "tab"
13
+
14
+ .tab-content
15
+ .tab-pane#all.active{ 'data-url' => all_delayed_jobs_url }
16
+ .tab-pane#failed{ 'data-url'=> failed_delayed_jobs_url }
17
+ .tab-pane#active{ 'data-url'=> active_delayed_jobs_url }
18
+ .tab-pane#queued{ 'data-url'=> queued_delayed_jobs_url }
19
+
20
+ %script#delayed_jobs_template{ :type=> "text/x-handlebars-template" }
21
+ %table.table.table-striped#delayed-jobs-table
22
+ %thead
23
+ %tr
24
+ %th
25
+ ID
26
+ %th
27
+ Priority
28
+ %th
29
+ Attempts
30
+ %th
31
+ Queue
32
+ %th
33
+ Last Error
34
+ %th.date
35
+ Failed at
36
+ %th.date
37
+ Run at
38
+ %th.date
39
+ Created at
40
+ %tbody
41
+ {{#.}}
42
+ %tr
43
+ %td
44
+ {{id}}
45
+ %td
46
+ {{priority}}
47
+ %td
48
+ {{attempts}}
49
+ %td
50
+ {{queue}}
51
+ %td
52
+ {{last_error}}
53
+ %td.date
54
+ {{}} {{failed_at}}
55
+ %td.date
56
+ {{job.run_at}}
57
+ %td.date
58
+ {{created_at}}
59
+ {{/.}}
@@ -0,0 +1,17 @@
1
+ !!! 5
2
+ %html
3
+ %head{:lang => "en"}
4
+ = stylesheet_link_tag "dj_mon", :media => "screen, projection"
5
+ = yield :head
6
+
7
+ %body
8
+ %header
9
+ .navbar.navbar-fixed-top
10
+ .navbar-inner
11
+ .container
12
+ = link_to "DJ Mon", delayed_jobs_url, class: "brand"
13
+
14
+ .container
15
+ = yield
16
+
17
+ = javascript_include_tag "dj_mon.js"
data/config/routes.rb ADDED
@@ -0,0 +1,13 @@
1
+ DjMon::Engine.routes.draw do
2
+
3
+ resources :delayed_jobs, :only=> [ :index ] do
4
+ collection do
5
+ get :all
6
+ get :failed
7
+ get :active
8
+ get :queued
9
+ end
10
+ end
11
+
12
+ root :to => 'delayed_jobs#index', :as => :delayed_jobs
13
+ end
@@ -0,0 +1,17 @@
1
+ module DjMon
2
+ class Engine < Rails::Engine
3
+
4
+ isolate_namespace DjMon
5
+
6
+ config.dj_mon = ActiveSupport::OrderedOptions.new
7
+ config.dj_mon.username = "dj_mon"
8
+ config.dj_mon.password = "password"
9
+
10
+ if Rails.version > "3.1"
11
+ initializer "DJMon precompile hook" do |app|
12
+ app.config.assets.precompile += ['dj_mon.js', 'dj_mon.css']
13
+ end
14
+ end
15
+
16
+ end
17
+ end
data/lib/dj_mon.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'haml'
2
+ require 'dj_mon/engine'
3
+ require 'jquery-rails'
4
+ require 'sass-rails'
5
+ require 'bootstrap-sass'
6
+
7
+ module DjMon
8
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dj_mon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Akshay Rawat
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '3.1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '3.1'
30
+ - !ruby/object:Gem::Dependency
31
+ name: haml
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '3.1'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.1'
46
+ - !ruby/object:Gem::Dependency
47
+ name: jquery-rails
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: sass-rails
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '3.1'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '3.1'
78
+ - !ruby/object:Gem::Dependency
79
+ name: bootstrap-sass
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 2.0.2
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 2.0.2
94
+ description: A Rails engine based frontend for Delayed Job
95
+ email:
96
+ - projects@akshay.cc
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - app/assets/javascripts/dj_mon.js
102
+ - app/assets/javascripts/mustache.js
103
+ - app/assets/stylesheets/dj_mon.css.scss
104
+ - app/controllers/dj_mon/delayed_jobs_controller.rb
105
+ - app/views/dj_mon/delayed_jobs/index.html.haml
106
+ - app/views/layouts/dj_mon.html.haml
107
+ - lib/dj_mon/engine.rb
108
+ - lib/dj_mon.rb
109
+ - config/routes.rb
110
+ - MIT-LICENSE
111
+ - Rakefile
112
+ - Gemfile
113
+ - README.rdoc
114
+ homepage:
115
+ licenses: []
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ requirements: []
133
+ rubyforge_project:
134
+ rubygems_version: 1.8.23
135
+ signing_key:
136
+ specification_version: 3
137
+ summary: A Frontend for Delayed Job.
138
+ test_files: []