job_boss 0.2 → 0.4

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 (56) hide show
  1. data/README.markdown +25 -2
  2. data/Rakefile +21 -8
  3. data/bin/job_boss +1 -0
  4. data/doc/ActiveSupport/TestCase.html +584 -0
  5. data/doc/{Passenger.html → ActiveSupport.html} +10 -30
  6. data/doc/CreateJobs.html +31 -16
  7. data/doc/{Mongrel.html → DaemonTest.html} +23 -39
  8. data/doc/JobBoss/Boss.html +466 -68
  9. data/doc/JobBoss/Config.html +77 -39
  10. data/doc/JobBoss/Job.html +375 -97
  11. data/doc/JobBoss/Queuer.html +35 -21
  12. data/doc/JobBoss.html +14 -311
  13. data/doc/{ActiveRecord.html → MathJobs.html} +66 -36
  14. data/doc/Rakefile.html +43 -9
  15. data/doc/SleepJobs.html +251 -0
  16. data/doc/{PhusionPassenger.html → StringJobs.html} +66 -38
  17. data/doc/bin/job_boss.html +1 -1
  18. data/doc/created.rid +14 -8
  19. data/doc/index.html +63 -7
  20. data/doc/lib/job_boss/boss_rb.html +5 -1
  21. data/doc/lib/job_boss/{configuror_rb.html → config_rb.html} +2 -2
  22. data/doc/lib/job_boss/job_rb.html +3 -1
  23. data/doc/lib/job_boss/queuer_rb.html +1 -1
  24. data/doc/lib/migrate_rb.html +1 -1
  25. data/doc/{vendor/spawn/lib/spawn_rb.html → test/app_root/app/jobs/math_jobs_rb.html} +7 -7
  26. data/doc/{vendor/spawn/lib/patches_rb.html → test/app_root/app/jobs/sleep_jobs_rb.html} +8 -12
  27. data/doc/test/app_root/app/jobs/string_jobs_rb.html +52 -0
  28. data/doc/test/test_helper_rb.html +62 -0
  29. data/doc/{vendor/spawn/init_rb.html → test/unit/daemon_test_rb.html} +3 -3
  30. data/doc/test/unit/job_test_rb.html +60 -0
  31. data/job_boss.gemspec +3 -2
  32. data/lib/job_boss/boss.rb +29 -7
  33. data/lib/job_boss/config.rb +49 -0
  34. data/lib/job_boss/job.rb +57 -13
  35. data/lib/migrate.rb +3 -1
  36. data/test/app_root/app/jobs/math_jobs.rb +5 -0
  37. data/test/app_root/app/jobs/sleep_jobs.rb +9 -0
  38. data/test/app_root/app/jobs/string_jobs.rb +5 -0
  39. data/test/app_root/config/database.yml +22 -0
  40. data/test/test_helper.rb +113 -0
  41. data/test/unit/daemon_test.rb +29 -0
  42. data/test/unit/job_test.rb +73 -0
  43. metadata +46 -27
  44. data/doc/ActiveRecord/Base.html +0 -343
  45. data/doc/Mongrel/HttpServer.html +0 -275
  46. data/doc/Passenger/Railz/RequestHandler.html +0 -271
  47. data/doc/Passenger/Railz.html +0 -185
  48. data/doc/PhusionPassenger/Rack/RequestHandler.html +0 -271
  49. data/doc/PhusionPassenger/Rack.html +0 -185
  50. data/doc/PhusionPassenger/Railz/RequestHandler.html +0 -271
  51. data/doc/PhusionPassenger/Railz.html +0 -185
  52. data/doc/Spawn/SpawnId.html +0 -276
  53. data/doc/Spawn.html +0 -742
  54. data/doc/vendor/spawn/CHANGELOG.html +0 -275
  55. data/doc/vendor/spawn/LICENSE.html +0 -151
  56. data/lib/job_boss/configuror.rb +0 -40
@@ -24,17 +24,21 @@
24
24
  <div id="metadata">
25
25
  <dl>
26
26
  <dt class="modified-date">Last Modified</dt>
27
- <dd class="modified-date">2010-11-26 16:58:46 -0500</dd>
27
+ <dd class="modified-date">2010-11-28 17:49:41 -0500</dd>
28
28
 
29
29
 
30
30
  <dt class="requires">Requires</dt>
31
31
  <dd class="requires">
32
32
  <ul>
33
33
 
34
+ <li>active_support</li>
35
+
34
36
  <li>job_boss/config</li>
35
37
 
36
38
  <li>job_boss/queuer</li>
37
39
 
40
+ <li>logger</li>
41
+
38
42
  <li>active_record</li>
39
43
 
40
44
  <li>yaml</li>
@@ -6,7 +6,7 @@
6
6
  <head>
7
7
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
8
 
9
- <title>File: configuror.rb [RDoc Documentation]</title>
9
+ <title>File: config.rb [RDoc Documentation]</title>
10
10
 
11
11
  <link type="text/css" media="screen" href="../../rdoc.css" rel="stylesheet" />
12
12
 
@@ -24,7 +24,7 @@
24
24
  <div id="metadata">
25
25
  <dl>
26
26
  <dt class="modified-date">Last Modified</dt>
27
- <dd class="modified-date">2010-11-26 10:24:05 -0500</dd>
27
+ <dd class="modified-date">2010-11-28 10:39:29 -0500</dd>
28
28
 
29
29
 
30
30
  <dt class="requires">Requires</dt>
@@ -24,13 +24,15 @@
24
24
  <div id="metadata">
25
25
  <dl>
26
26
  <dt class="modified-date">Last Modified</dt>
27
- <dd class="modified-date">2010-11-26 16:59:52 -0500</dd>
27
+ <dd class="modified-date">2010-11-28 17:56:05 -0500</dd>
28
28
 
29
29
 
30
30
  <dt class="requires">Requires</dt>
31
31
  <dd class="requires">
32
32
  <ul>
33
33
 
34
+ <li>yaml</li>
35
+
34
36
  <li>socket</li>
35
37
 
36
38
  <li>active_support</li>
@@ -24,7 +24,7 @@
24
24
  <div id="metadata">
25
25
  <dl>
26
26
  <dt class="modified-date">Last Modified</dt>
27
- <dd class="modified-date">2010-11-26 14:59:20 -0500</dd>
27
+ <dd class="modified-date">2010-11-26 17:21:49 -0500</dd>
28
28
 
29
29
 
30
30
  <dt class="requires">Requires</dt>
@@ -24,7 +24,7 @@
24
24
  <div id="metadata">
25
25
  <dl>
26
26
  <dt class="modified-date">Last Modified</dt>
27
- <dd class="modified-date">2010-11-26 10:07:52 -0500</dd>
27
+ <dd class="modified-date">2010-11-28 17:25:41 -0500</dd>
28
28
 
29
29
 
30
30
  <dt class="requires">Requires</dt>
@@ -6,17 +6,17 @@
6
6
  <head>
7
7
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
8
 
9
- <title>File: spawn.rb [RDoc Documentation]</title>
9
+ <title>File: math_jobs.rb [RDoc Documentation]</title>
10
10
 
11
- <link type="text/css" media="screen" href="../../../rdoc.css" rel="stylesheet" />
11
+ <link type="text/css" media="screen" href="../../../../rdoc.css" rel="stylesheet" />
12
12
 
13
- <script src="../../../js/jquery.js" type="text/javascript"
13
+ <script src="../../../../js/jquery.js" type="text/javascript"
14
14
  charset="utf-8"></script>
15
- <script src="../../../js/thickbox-compressed.js" type="text/javascript"
15
+ <script src="../../../../js/thickbox-compressed.js" type="text/javascript"
16
16
  charset="utf-8"></script>
17
- <script src="../../../js/quicksearch.js" type="text/javascript"
17
+ <script src="../../../../js/quicksearch.js" type="text/javascript"
18
18
  charset="utf-8"></script>
19
- <script src="../../../js/darkfish.js" type="text/javascript"
19
+ <script src="../../../../js/darkfish.js" type="text/javascript"
20
20
  charset="utf-8"></script>
21
21
  </head>
22
22
 
@@ -24,7 +24,7 @@
24
24
  <div id="metadata">
25
25
  <dl>
26
26
  <dt class="modified-date">Last Modified</dt>
27
- <dd class="modified-date">2010-11-26 13:46:41 -0500</dd>
27
+ <dd class="modified-date">2010-11-28 10:56:16 -0500</dd>
28
28
 
29
29
 
30
30
  <dt class="requires">Requires</dt>
@@ -6,17 +6,17 @@
6
6
  <head>
7
7
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
8
 
9
- <title>File: patches.rb [RDoc Documentation]</title>
9
+ <title>File: sleep_jobs.rb [RDoc Documentation]</title>
10
10
 
11
- <link type="text/css" media="screen" href="../../../rdoc.css" rel="stylesheet" />
11
+ <link type="text/css" media="screen" href="../../../../rdoc.css" rel="stylesheet" />
12
12
 
13
- <script src="../../../js/jquery.js" type="text/javascript"
13
+ <script src="../../../../js/jquery.js" type="text/javascript"
14
14
  charset="utf-8"></script>
15
- <script src="../../../js/thickbox-compressed.js" type="text/javascript"
15
+ <script src="../../../../js/thickbox-compressed.js" type="text/javascript"
16
16
  charset="utf-8"></script>
17
- <script src="../../../js/quicksearch.js" type="text/javascript"
17
+ <script src="../../../../js/quicksearch.js" type="text/javascript"
18
18
  charset="utf-8"></script>
19
- <script src="../../../js/darkfish.js" type="text/javascript"
19
+ <script src="../../../../js/darkfish.js" type="text/javascript"
20
20
  charset="utf-8"></script>
21
21
  </head>
22
22
 
@@ -24,7 +24,7 @@
24
24
  <div id="metadata">
25
25
  <dl>
26
26
  <dt class="modified-date">Last Modified</dt>
27
- <dd class="modified-date">2010-11-26 13:46:41 -0500</dd>
27
+ <dd class="modified-date">2010-11-28 17:28:24 -0500</dd>
28
28
 
29
29
 
30
30
  <dt class="requires">Requires</dt>
@@ -43,11 +43,7 @@
43
43
 
44
44
  <div class="description">
45
45
  <h2>Description</h2>
46
- <p>
47
- see
48
- activerecord/lib/active_record/connection_adaptors/abstract/connection_specification.rb
49
- </p>
50
-
46
+
51
47
  </div>
52
48
 
53
49
  </div>
@@ -0,0 +1,52 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
+
9
+ <title>File: string_jobs.rb [RDoc Documentation]</title>
10
+
11
+ <link type="text/css" media="screen" href="../../../../rdoc.css" rel="stylesheet" />
12
+
13
+ <script src="../../../../js/jquery.js" type="text/javascript"
14
+ charset="utf-8"></script>
15
+ <script src="../../../../js/thickbox-compressed.js" type="text/javascript"
16
+ charset="utf-8"></script>
17
+ <script src="../../../../js/quicksearch.js" type="text/javascript"
18
+ charset="utf-8"></script>
19
+ <script src="../../../../js/darkfish.js" type="text/javascript"
20
+ charset="utf-8"></script>
21
+ </head>
22
+
23
+ <body class="file file-popup">
24
+ <div id="metadata">
25
+ <dl>
26
+ <dt class="modified-date">Last Modified</dt>
27
+ <dd class="modified-date">2010-11-28 14:49:29 -0500</dd>
28
+
29
+
30
+ <dt class="requires">Requires</dt>
31
+ <dd class="requires">
32
+ <ul>
33
+
34
+ </ul>
35
+ </dd>
36
+
37
+
38
+
39
+ </dl>
40
+ </div>
41
+
42
+ <div id="documentation">
43
+
44
+ <div class="description">
45
+ <h2>Description</h2>
46
+
47
+ </div>
48
+
49
+ </div>
50
+ </body>
51
+ </html>
52
+
@@ -0,0 +1,62 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
+
9
+ <title>File: test_helper.rb [RDoc Documentation]</title>
10
+
11
+ <link type="text/css" media="screen" href="../rdoc.css" rel="stylesheet" />
12
+
13
+ <script src="../js/jquery.js" type="text/javascript"
14
+ charset="utf-8"></script>
15
+ <script src="../js/thickbox-compressed.js" type="text/javascript"
16
+ charset="utf-8"></script>
17
+ <script src="../js/quicksearch.js" type="text/javascript"
18
+ charset="utf-8"></script>
19
+ <script src="../js/darkfish.js" type="text/javascript"
20
+ charset="utf-8"></script>
21
+ </head>
22
+
23
+ <body class="file file-popup">
24
+ <div id="metadata">
25
+ <dl>
26
+ <dt class="modified-date">Last Modified</dt>
27
+ <dd class="modified-date">2010-11-28 17:50:45 -0500</dd>
28
+
29
+
30
+ <dt class="requires">Requires</dt>
31
+ <dd class="requires">
32
+ <ul>
33
+
34
+ <li>rubygems</li>
35
+
36
+ <li>test/unit</li>
37
+
38
+ <li>sqlite3</li>
39
+
40
+ <li>fileutils</li>
41
+
42
+ <li>active_support</li>
43
+
44
+ </ul>
45
+ </dd>
46
+
47
+
48
+
49
+ </dl>
50
+ </div>
51
+
52
+ <div id="documentation">
53
+
54
+ <div class="description">
55
+ <h2>Description</h2>
56
+
57
+ </div>
58
+
59
+ </div>
60
+ </body>
61
+ </html>
62
+
@@ -6,7 +6,7 @@
6
6
  <head>
7
7
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
8
 
9
- <title>File: init.rb [RDoc Documentation]</title>
9
+ <title>File: daemon_test.rb [RDoc Documentation]</title>
10
10
 
11
11
  <link type="text/css" media="screen" href="../../rdoc.css" rel="stylesheet" />
12
12
 
@@ -24,14 +24,14 @@
24
24
  <div id="metadata">
25
25
  <dl>
26
26
  <dt class="modified-date">Last Modified</dt>
27
- <dd class="modified-date">2010-11-26 13:46:41 -0500</dd>
27
+ <dd class="modified-date">2010-11-28 17:42:32 -0500</dd>
28
28
 
29
29
 
30
30
  <dt class="requires">Requires</dt>
31
31
  <dd class="requires">
32
32
  <ul>
33
33
 
34
- <li>patches</li>
34
+ <li>test_helper</li>
35
35
 
36
36
  </ul>
37
37
  </dd>
@@ -0,0 +1,60 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
+
9
+ <title>File: job_test.rb [RDoc Documentation]</title>
10
+
11
+ <link type="text/css" media="screen" href="../../rdoc.css" rel="stylesheet" />
12
+
13
+ <script src="../../js/jquery.js" type="text/javascript"
14
+ charset="utf-8"></script>
15
+ <script src="../../js/thickbox-compressed.js" type="text/javascript"
16
+ charset="utf-8"></script>
17
+ <script src="../../js/quicksearch.js" type="text/javascript"
18
+ charset="utf-8"></script>
19
+ <script src="../../js/darkfish.js" type="text/javascript"
20
+ charset="utf-8"></script>
21
+ </head>
22
+
23
+ <body class="file file-popup">
24
+ <div id="metadata">
25
+ <dl>
26
+ <dt class="modified-date">Last Modified</dt>
27
+ <dd class="modified-date">2010-11-28 17:55:00 -0500</dd>
28
+
29
+
30
+ <dt class="requires">Requires</dt>
31
+ <dd class="requires">
32
+ <ul>
33
+
34
+ <li>test_helper</li>
35
+
36
+ <li>active_record</li>
37
+
38
+ <li>job_boss/boss</li>
39
+
40
+ <li>job_boss/job</li>
41
+
42
+ </ul>
43
+ </dd>
44
+
45
+
46
+
47
+ </dl>
48
+ </div>
49
+
50
+ <div id="documentation">
51
+
52
+ <div class="description">
53
+ <h2>Description</h2>
54
+
55
+ </div>
56
+
57
+ </div>
58
+ </body>
59
+ </html>
60
+
data/job_boss.gemspec CHANGED
@@ -4,7 +4,7 @@ $:.unshift lib unless $:.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "job_boss"
7
- s.version = '0.2'
7
+ s.version = '0.4'
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Brian Underwood"]
10
10
  s.email = ["ml+job_boss@semi-sentient.com"]
@@ -16,7 +16,8 @@ Gem::Specification.new do |s|
16
16
 
17
17
  s.add_dependency "activerecord"
18
18
  s.add_dependency "activesupport"
19
- s.add_dependency "daemons"
19
+ s.add_dependency "daemons-mikehale"
20
+ s.add_dependency "sqlite3-ruby" # For testing
20
21
 
21
22
  s.files = `git ls-files`.split("\n")
22
23
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
data/lib/job_boss/boss.rb CHANGED
@@ -1,6 +1,9 @@
1
+ require 'active_support'
2
+
1
3
  module JobBoss
2
4
  class Boss
3
5
  class << self
6
+ extend ActiveSupport::Memoizable
4
7
  # Used to set Boss configuration
5
8
  # Usage:
6
9
  # Boss.config.sleep_interval = 2
@@ -16,10 +19,27 @@ module JobBoss
16
19
  require 'job_boss/queuer'
17
20
  @@queuer ||= Queuer.new
18
21
  end
22
+
23
+ def logger
24
+ @@config.log_path = resolve_path(@@config.log_path)
25
+
26
+ require 'logger'
27
+ Logger.new(@@config.log_path)
28
+ end
29
+ memoize :logger
30
+
31
+ # If path starts with '/', leave alone. Otherwise, prepend application_root
32
+ def resolve_path(path)
33
+ if path == ?/ || path.match(/^#{@@config.application_root}/)
34
+ path
35
+ else
36
+ File.join(@@config.application_root, path)
37
+ end
38
+ end
19
39
  end
20
40
 
21
41
  def initialize(options = {})
22
- @@config.working_dir ||= options[:working_dir]
42
+ @@config.application_root ||= options[:working_dir]
23
43
  @@config.sleep_interval ||= options[:sleep_interval]
24
44
  @@config.employee_limit ||= options[:employee_limit]
25
45
  @@config.database_yaml_path ||= options[:database_yaml_path]
@@ -49,7 +69,7 @@ module JobBoss
49
69
  stop if Process.pid == BOSS_PID
50
70
  end
51
71
 
52
- puts "Job Boss started"
72
+ Boss.logger.info "Job Boss started"
53
73
 
54
74
  while true
55
75
  unless (children_count = available_employees) > 0 && Job.pending.count > 0
@@ -57,6 +77,8 @@ module JobBoss
57
77
  next
58
78
  end
59
79
 
80
+ # Go through each pending path so that we don't get stuck just processing
81
+ # long running jobs which would leave quicker jobs to suffocate
60
82
  Job.pending_paths.each do |path|
61
83
  job = Job.pending.find_by_path(path)
62
84
  next if job.nil?
@@ -72,11 +94,11 @@ module JobBoss
72
94
  end
73
95
 
74
96
  def stop
75
- puts "Stopping #{@running_jobs.size} running employees..."
97
+ Boss.logger.info "Stopping #{@running_jobs.size} running employees..."
76
98
 
77
99
  shutdown_running_jobs
78
100
 
79
- puts "Job Boss stopped"
101
+ Boss.logger.info "Job Boss stopped"
80
102
  end
81
103
 
82
104
  private
@@ -109,7 +131,7 @@ private
109
131
  end
110
132
 
111
133
  def establish_active_record_connection
112
- @@config.database_yaml_path = File.join(@@config.working_dir, @@config.database_yaml_path) unless @@config.database_yaml_path[0] == ?/
134
+ @@config.database_yaml_path = Boss.resolve_path(@@config.database_yaml_path)
113
135
 
114
136
  raise "Database YAML file missing (#{@@config.database_yaml_path})" unless File.exist?(@@config.database_yaml_path)
115
137
 
@@ -119,7 +141,7 @@ private
119
141
  end
120
142
 
121
143
  def require_job_classes
122
- @@config.jobs_path = File.join(@@config.working_dir, @@config.jobs_path) unless @@config.jobs_path[0] == ?/
144
+ @@config.jobs_path = Boss.resolve_path(@@config.jobs_path)
123
145
 
124
146
  raise "Jobs path missing (#{@@config.jobs_path})" unless File.exist?(@@config.jobs_path)
125
147
 
@@ -135,7 +157,7 @@ private
135
157
 
136
158
  def kill_job(job)
137
159
  begin
138
- Process.kill("HUP", job.employee_pid.to_i)
160
+ Process.kill("TERM", job.employee_pid.to_i)
139
161
  rescue Errno::ESRCH
140
162
  nil
141
163
  end
@@ -0,0 +1,49 @@
1
+ module JobBoss
2
+ class Config
3
+ attr_accessor :application_root, :database_yaml_path, :log_path, :jobs_path, :sleep_interval, :employee_limit, :environment
4
+
5
+ def parse_args(argv, options = {})
6
+ @application_root = options[:working_dir] || Dir.pwd
7
+ @database_yaml_path = 'config/database.yml'
8
+ @log_path = 'log/job_boss.log'
9
+ @jobs_path = 'app/jobs'
10
+ @sleep_interval = 0.5
11
+ @employee_limit = 4
12
+ @environment = 'development'
13
+
14
+ require 'optparse'
15
+
16
+ OptionParser.new do |opts|
17
+ opts.banner = "Usage: job_boss [start|stop|restart|run|zap] [-- <options>]"
18
+
19
+ opts.on("-r", "--application-root PATH", "Path for the application root upon which other paths depend (defaults to .)") do |path|
20
+ @application_root = path
21
+ end
22
+
23
+ opts.on("-d", "--database-yaml PATH", "Path for database YAML (defaults to <application-root>/#{@database_yaml_path})") do |path|
24
+ @database_yaml_path = path
25
+ end
26
+
27
+ opts.on("-l", "--log-path PATH", "Path for log file (defaults to <application-root>/#{@log_path})") do |path|
28
+ @log_path = path
29
+ end
30
+
31
+ opts.on("-j", "--jobs-path PATH", "Path to folder with job classes (defaults to <application-root>/#{@jobs_path})") do |path|
32
+ @jobs_path = path
33
+ end
34
+
35
+ opts.on("-e", "--environment ENV", "Environment to use in database YAML file (defaults to '#{@environment}')") do |env|
36
+ @environment = env
37
+ end
38
+
39
+ opts.on("-s", "--sleep-interval INTERVAL", Integer, "Number of seconds for the boss to sleep between checks of the queue (default #{@sleep_interval})") do |interval|
40
+ @sleep_interval = interval
41
+ end
42
+
43
+ opts.on("-c", "--employee-limit LIMIT", Integer, "Maximum number of employees (default 4)") do |limit|
44
+ @employee_limit = limit
45
+ end
46
+ end.parse!(argv)
47
+ end
48
+ end
49
+ end
data/lib/job_boss/job.rb CHANGED
@@ -7,37 +7,34 @@ module JobBoss
7
7
  serialize :result
8
8
  serialize :error_backtrace
9
9
 
10
- scope :pending, where('started_at IS NULL')
10
+ scope :pending, where('started_at IS NULL AND cancelled_at IS NULL')
11
11
  scope :running, where('started_at IS NOT NULL AND completed_at IS NULL')
12
12
  scope :completed, where('completed_at IS NOT NULL')
13
13
 
14
14
  # Method used by the boss to dispatch an employee
15
15
  def dispatch
16
16
  mark_as_started
17
- puts "Dispatching Job ##{self.id}"
17
+ Boss.logger.info "Dispatching Job ##{self.id}"
18
18
 
19
19
  pid = fork do
20
- $0 = "job_boss - employee (job ##{self.id})"
20
+ $0 = "[job_boss] employee (job ##{self.id})"
21
21
  Process.setpriority(Process::PRIO_PROCESS, 0, 19)
22
22
 
23
23
  begin
24
24
  mark_employee
25
25
 
26
- Signal.trap("HUP") do
27
- mark_for_redo
28
- end
26
+ value = self.class.call_path(self.path, *self.args)
29
27
 
30
- result = self.class.call_path(self.path, *self.args)
31
- self.update_attribute(:result, result)
28
+ self.update_attribute(:result, value)
32
29
  rescue Exception => exception
33
30
  mark_exception(exception)
34
- puts "Error running job ##{self.id}!"
31
+ Boss.logger.error "Error running job ##{self.id}!"
35
32
  ensure
36
33
  until mark_as_completed
37
34
  sleep(1)
38
35
  end
39
36
 
40
- puts "Job ##{self.id} completed, exiting..."
37
+ Boss.logger.info "Job ##{self.id} completed, exiting..."
41
38
  Kernel.exit
42
39
  end
43
40
  end
@@ -45,6 +42,22 @@ module JobBoss
45
42
  Process.detach(pid)
46
43
  end
47
44
 
45
+ # Store result as first and only value of an array so that the value always gets serialized
46
+ # Was having issues with the boolean value of false getting stored as the string "f"
47
+ def result=(value)
48
+ write_attribute(:result, [value])
49
+ end
50
+
51
+ def result
52
+ # If the result is being called for but the job hasn't been completed, reload
53
+ # to check to see if there was a result
54
+ self.reload if !completed? && read_attribute(:result).nil?
55
+
56
+ value = read_attribute(:result)
57
+
58
+ value.is_a?(Array) ? value.first : value
59
+ end
60
+
48
61
  # Clear out the job and put it back onto the queue for processing
49
62
  def mark_for_redo
50
63
  self.reload
@@ -80,16 +93,40 @@ module JobBoss
80
93
  completed_at && (status == 'success')
81
94
  end
82
95
 
96
+ # Has the job been assigned to an employee?
97
+ def assigned?
98
+ # If the #assigned? method is being called for but the job hasn't been completed, reload
99
+ # to check to see if it has been assigned
100
+ self.reload if !completed? && employee_pid.nil?
101
+
102
+ employee_pid && employee_host
103
+ end
104
+
83
105
  # How long did the job take?
84
106
  def time_taken
85
107
  completed_at - started_at if completed_at && started_at
86
108
  end
87
109
 
110
+ # If the job raised an exception, this method will return the instance of that exception
111
+ # with the message and backtrace
112
+ def error
113
+ # If the error is being called for but the job hasn't been completed, reload
114
+ # to check to see if there was a error
115
+ self.reload if !completed? && error_message.nil?
116
+
117
+ if error_class && error_message && error_backtrace
118
+ error = Kernel.const_get(error_class).new(error_message)
119
+ error.set_backtrace(error_backtrace)
120
+ error
121
+ end
122
+ end
123
+
88
124
  class << self
89
125
  def wait_for_jobs(jobs, sleep_interval = 0.5)
90
- running_jobs = jobs.dup
126
+ jobs = [jobs] if jobs.is_a?(Job)
91
127
 
92
- until Job.running.find_all_by_id(running_jobs.collect(&:id)).empty?
128
+ ids = jobs.collect(&:id)
129
+ until Job.completed.find_all_by_id(ids).count == jobs.size
93
130
  sleep(sleep_interval)
94
131
  end
95
132
 
@@ -97,6 +134,13 @@ module JobBoss
97
134
  end
98
135
 
99
136
  def result_hash(jobs)
137
+ jobs = [jobs] if jobs.is_a?(Job)
138
+
139
+ # the #result method automatically reloads the result here if needed but this will
140
+ # do it in one SQL call
141
+ jobs = Job.find(jobs.collect(&:id))
142
+
143
+ require 'yaml'
100
144
  jobs.inject({}) do |hash, job|
101
145
  hash.merge(job.args => job.result)
102
146
  end
@@ -120,7 +164,7 @@ private
120
164
  end
121
165
 
122
166
  def mark_exception(exception)
123
- update_attributes(:status => 'error', :error_message => exception.message, :error_backtrace => exception.backtrace)
167
+ update_attributes(:status => 'error', :error_class => exception.class.to_s, :error_message => exception.message, :error_backtrace => exception.backtrace)
124
168
  end
125
169
 
126
170
  def mark_as_completed