mcmire-delayed_job_web 1.1.3.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://rubygems.org"
2
+ gem "sinatra", '>= 0.9.2'
3
+ gem "haml", '~> 3.1.3'
4
+ gem 'activerecord', '> 3.0.0'
5
+ gem 'delayed_job', '> 2.0.3'
6
+ gem 'rdoc'
7
+
8
+ group :development do
9
+ gem "shoulda", ">= 0"
10
+ gem "bundler", "> 1.0.0"
11
+ gem "jeweler", "~> 1.6.4"
12
+ gem "rcov", ">= 0"
13
+ gem "rack-test", ">= 0"
14
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Erick Schmitt
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.markdown ADDED
@@ -0,0 +1,61 @@
1
+ delayed_job_web
2
+ ===============
3
+
4
+ A [resque][0] inspired (read: stolen) interface for delayed_job.
5
+ This gem is written to work with rails 3 applications using
6
+ activerecord.
7
+
8
+ Some features:
9
+
10
+ * Easily view jobs enqueued, working, pending, and failed.
11
+ * Queue any single job. or all pending jobs, to run immediately.
12
+ * Remove a failed job, or easily remove all failed jobs.
13
+ * Watch delayed_job operation with live ajax polling.
14
+
15
+ Quick Start For Rails 3 Applications
16
+ ------------------------------------
17
+
18
+ Add the dependency to your Gemfile
19
+
20
+ ```ruby
21
+ gem "delayed_job_web"
22
+ ```
23
+
24
+ Install it...
25
+
26
+ ```ruby
27
+ bundle
28
+ ```
29
+
30
+ Add a route to your application for accessing the interface
31
+
32
+ ```ruby
33
+ match "/delayed_job" => DelayedJobWeb, :anchor => false
34
+ ```
35
+
36
+ You probably want to password protect the interface, an easy way is to add something like this your config.ru file
37
+
38
+ ```ruby
39
+ if Rails.env.production?
40
+ DelayedJobWeb.use Rack::Auth::Basic do |username, password|
41
+ username == 'username' && password == 'password'
42
+ end
43
+ end
44
+ ```
45
+
46
+ The Interface - Yea, a ripoff of resque-web
47
+ ------------------------------------
48
+
49
+ ![Screen shot](http://dl.dropbox.com/u/1506097/Screenshots/delayed_job_web_1.png)
50
+
51
+ ![Screen shot](http://dl.dropbox.com/u/1506097/Screenshots/delayed_job_web_2.png)
52
+
53
+
54
+ Author
55
+ ------
56
+
57
+ Erick Schmitt - [@ejschmitt][1]
58
+
59
+
60
+ [0]: https://github.com/defunkt/resque
61
+ [1]: http://twitter.com/ejschmitt
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "delayed_job_web"
18
+ gem.homepage = "http://github.com/ejschmitt/delayed_job_web"
19
+ gem.license = "MIT"
20
+ gem.summary = "Web interface for delayed_job"
21
+ gem.description = "Web interface for delayed_job inspired by resque"
22
+ gem.email = "ejschmitt@gmail.com"
23
+ gem.authors = ["Erick Schmitt"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ test.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rdoc/task'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "delayed_job_web #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.2
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ begin
4
+ require 'delayed_job_web/application/app.rb'
5
+ rescue LoadError => e
6
+ require 'rubygems'
7
+ path = File.expand_path '../../lib', __FILE__
8
+ $:.unshift(path) if File.directory?(path) && !$:.include?(path)
9
+ require 'delayed_job_web/application/app.rb'
10
+ end
@@ -0,0 +1,156 @@
1
+ require 'sinatra/base'
2
+ require 'active_support'
3
+ require 'active_record'
4
+ require 'delayed_job'
5
+ require 'haml'
6
+
7
+ class DelayedJobWeb < Sinatra::Base
8
+ set :root, File.dirname(__FILE__)
9
+ set :static, true
10
+ set :public_folder, File.expand_path('../public', __FILE__)
11
+ set :views, File.expand_path('../views', __FILE__)
12
+ set :haml, { :format => :html5 }
13
+
14
+ def current_page
15
+ url_path request.path_info.sub('/','')
16
+ end
17
+
18
+ def start
19
+ params[:start].to_i
20
+ end
21
+
22
+ def per_page
23
+ 20
24
+ end
25
+
26
+ def url_path(*path_parts)
27
+ [ path_prefix, path_parts ].join("/").squeeze('/')
28
+ end
29
+ alias_method :u, :url_path
30
+
31
+ def path_prefix
32
+ request.env['SCRIPT_NAME']
33
+ end
34
+
35
+ def tabs
36
+ [
37
+ {:name => 'Overview', :path => '/overview'},
38
+ {:name => 'Enqueued', :path => '/enqueued'},
39
+ {:name => 'Working', :path => '/working'},
40
+ {:name => 'Pending', :path => '/pending'},
41
+ {:name => 'Failed', :path => '/failed'},
42
+ {:name => 'Stats', :path => '/stats'}
43
+ ]
44
+ end
45
+
46
+ def delayed_job
47
+ begin
48
+ Delayed::Job
49
+ rescue
50
+ false
51
+ end
52
+ end
53
+
54
+ get '/overview' do
55
+ if delayed_job
56
+ haml :overview
57
+ else
58
+ @message = "Unable to connected to Delayed::Job database"
59
+ haml :error
60
+ end
61
+ end
62
+
63
+ get '/stats' do
64
+ haml :stats
65
+ end
66
+
67
+ %w(enqueued working pending failed).each do |page|
68
+ get "/#{page}" do
69
+ @jobs = delayed_jobs(page.to_sym).order('created_at desc, id desc').offset(start).limit(per_page)
70
+ @all_jobs = delayed_jobs(page.to_sym)
71
+ haml page.to_sym
72
+ end
73
+ end
74
+
75
+ get "/remove/:id" do
76
+ delayed_job.find(params[:id]).delete
77
+ redirect back
78
+ end
79
+
80
+ get "/requeue/:id" do
81
+ job = delayed_job.find(params[:id])
82
+ job.update_attributes(:run_at => Time.now, :failed_at => nil)
83
+ redirect back
84
+ end
85
+
86
+ post "/failed/clear" do
87
+ delayed_job.destroy_all(delayed_job_sql(:failed))
88
+ redirect u('failed')
89
+ end
90
+
91
+ post "/requeue/all" do
92
+ delayed_jobs(:failed).update_all(:run_at => Time.now, :failed_at => nil)
93
+ redirect back
94
+ end
95
+
96
+ def delayed_jobs(type)
97
+ delayed_job.where(delayed_job_sql(type))
98
+ end
99
+
100
+ def delayed_job_sql(type)
101
+ case type
102
+ when :enqueued
103
+ ''
104
+ when :working
105
+ 'locked_at is not null'
106
+ when :failed
107
+ 'last_error is not null'
108
+ when :pending
109
+ 'attempts = 0'
110
+ end
111
+ end
112
+
113
+ get "/?" do
114
+ redirect u(:overview)
115
+ end
116
+
117
+ def partial(template, local_vars = {})
118
+ @partial = true
119
+ haml(template.to_sym, {:layout => false}, local_vars)
120
+ ensure
121
+ @partial = false
122
+ end
123
+
124
+ %w(overview enqueued working pending failed stats) .each do |page|
125
+ get "/#{page}.poll" do
126
+ show_for_polling(page)
127
+ end
128
+
129
+ get "/#{page}/:id.poll" do
130
+ show_for_polling(page)
131
+ end
132
+ end
133
+
134
+ def poll
135
+ if @polling
136
+ text = "Last Updated: #{Time.now.strftime("%H:%M:%S")}"
137
+ else
138
+ text = "<a href='#{u(request.path_info)}.poll' rel='poll'>Live Poll</a>"
139
+ end
140
+ "<p class='poll'>#{text}</p>"
141
+ end
142
+
143
+ def show_for_polling(page)
144
+ content_type "text/html"
145
+ @polling = true
146
+ # show(page.to_sym, false).gsub(/\s{1,}/, ' ')
147
+ @jobs = delayed_jobs(page.to_sym)
148
+ haml(page.to_sym, {:layout => false})
149
+ end
150
+
151
+ end
152
+
153
+ # Run the app!
154
+ #
155
+ # puts "Hello, you're running delayed_job_web"
156
+ # DelayedJobWeb.run!
@@ -0,0 +1,64 @@
1
+ $(function() {
2
+ var poll_interval = 3;
3
+
4
+ var relatizer = function(){
5
+ var dt = $(this).text(), relatized = $.relatizeDate(this)
6
+ if ($(this).parents("a").length > 0 || $(this).is("a")) {
7
+ $(this).relatizeDate()
8
+ if (!$(this).attr('title')) {
9
+ $(this).attr('title', dt)
10
+ }
11
+ } else {
12
+ $(this)
13
+ .text('')
14
+ .append( $('<a href="#" class="toggle_format" title="' + dt + '" />')
15
+ .append('<span class="date_time">' + dt +
16
+ '</span><span class="relatized_time">' +
17
+ relatized + '</span>') )
18
+ }
19
+ };
20
+
21
+ $('.time').each(relatizer);
22
+
23
+ $('.time a.toggle_format .date_time').hide();
24
+
25
+ var format_toggler = function(){
26
+ $('.time a.toggle_format span').toggle();
27
+ $(this).attr('title', $('span:hidden',this).text());
28
+ return false;
29
+ };
30
+
31
+ $('.time a.toggle_format').click(format_toggler);
32
+
33
+ $('ul li.job').hover(function() {
34
+ $(this).addClass('hover');
35
+ }, function() {
36
+ $(this).removeClass('hover');
37
+ })
38
+
39
+ $('a.backtrace').click(function (e) {
40
+ e.preventDefault();
41
+ if($(this).prev('div.backtrace:visible').length > 0) {
42
+ $(this).next('div.backtrace').show();
43
+ $(this).prev('div.backtrace').hide();
44
+ } else {
45
+ $(this).next('div.backtrace').hide();
46
+ $(this).prev('div.backtrace').show();
47
+ }
48
+ });
49
+
50
+ $('a[rel=poll]').click(function(e) {
51
+ e.preventDefault();
52
+ var href = $(this).attr('href')
53
+ $(this).parent().text('Starting...')
54
+ $("#main").addClass('polling')
55
+
56
+ setInterval(function() {
57
+ $.ajax({dataType: 'text', type: 'get', url: href, success: function(data) {
58
+ $('#main').html(data);
59
+ }})
60
+ }, poll_interval * 1000)
61
+
62
+ return false
63
+ })
64
+ })