navvy-sequelhooks 0.3.3
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.
- data/LICENSE +3 -0
- data/README.textile +15 -0
- data/generators/navvy/navvy_generator.rb +20 -0
- data/generators/navvy/templates/active_record_migration.rb +22 -0
- data/generators/navvy/templates/sequel_migration.rb +23 -0
- data/lib/generators/navvy_generator.rb +32 -0
- data/lib/navvy.rb +34 -0
- data/lib/navvy/configuration.rb +13 -0
- data/lib/navvy/job.rb +173 -0
- data/lib/navvy/job/active_record.rb +136 -0
- data/lib/navvy/job/data_mapper.rb +154 -0
- data/lib/navvy/job/mongo_mapper.rb +150 -0
- data/lib/navvy/job/mongoid.rb +139 -0
- data/lib/navvy/job/sequel.rb +138 -0
- data/lib/navvy/job/sequelhooks.rb +144 -0
- data/lib/navvy/logger.rb +38 -0
- data/lib/navvy/tasks.rb +16 -0
- data/lib/navvy/worker.rb +50 -0
- data/spec/configuration_spec.rb +68 -0
- data/spec/job_spec.rb +505 -0
- data/spec/logger_spec.rb +23 -0
- data/spec/setup/active_record.rb +20 -0
- data/spec/setup/data_mapper.rb +4 -0
- data/spec/setup/mongo_mapper.rb +2 -0
- data/spec/setup/mongoid.rb +9 -0
- data/spec/setup/sequel.rb +21 -0
- data/spec/setup/sequelhooks.rb +21 -0
- data/spec/spec_helper.rb +39 -0
- data/spec/worker_spec.rb +27 -0
- data/spec/zhooks.rb +47 -0
- metadata +160 -0
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
##
|
5
|
+
# Use this adapter if you want Sequel::Model hooks, like before_create, around_update or after_destroy.
|
6
|
+
|
7
|
+
module Navvy
|
8
|
+
class Job < Sequel::Model
|
9
|
+
|
10
|
+
##
|
11
|
+
# Add a job to the job queue.
|
12
|
+
#
|
13
|
+
# @param [Object] object the object you want to run a method from
|
14
|
+
# @param [Symbol, String] method_name the name of the method you want to
|
15
|
+
# run
|
16
|
+
# @param [*] arguments optional arguments you want to pass to the method
|
17
|
+
#
|
18
|
+
# @return [Job, false] created Job or false if failed
|
19
|
+
|
20
|
+
def self.enqueue(object, method_name, *args)
|
21
|
+
options = {}
|
22
|
+
if args.last.is_a?(Hash)
|
23
|
+
options = args.last.delete(:job_options) || {}
|
24
|
+
args.pop if args.last.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
Job[create(
|
28
|
+
:object => object.to_s,
|
29
|
+
:method_name => method_name.to_s,
|
30
|
+
:arguments => args.to_yaml,
|
31
|
+
:priority => options[:priority] || 0,
|
32
|
+
:parent_id => options[:parent_id],
|
33
|
+
:run_at => options[:run_at] || Time.now,
|
34
|
+
:created_at => Time.now
|
35
|
+
).id]
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Find the next available jobs in the queue. This will not include failed
|
40
|
+
# jobs (where :failed_at is not nil) and jobs that should run in the future
|
41
|
+
# (where :run_at is greater than the current time).
|
42
|
+
#
|
43
|
+
# @param [Integer] limit the limit of jobs to be fetched. Defaults to
|
44
|
+
# Navvy::Job.limit
|
45
|
+
#
|
46
|
+
# @return [array, nil] the next available jobs in an array or nil if no
|
47
|
+
# jobs were found.
|
48
|
+
|
49
|
+
def self.next(limit = self.limit)
|
50
|
+
filter(:failed_at => nil).
|
51
|
+
filter(:completed_at => nil).
|
52
|
+
filter{run_at <= Time.now}.
|
53
|
+
order(:priority.desc, :created_at).
|
54
|
+
first(limit)
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Clean up jobs that we don't need to keep anymore. If Navvy::Job.keep is
|
59
|
+
# false it'll delete every completed job, if it's a timestamp it'll only
|
60
|
+
# delete completed jobs that have passed their keeptime.
|
61
|
+
#
|
62
|
+
# @return [true, false] delete_all the result of the delete_all call
|
63
|
+
|
64
|
+
def self.cleanup
|
65
|
+
if keep.is_a? Fixnum
|
66
|
+
time = Time.now - keep
|
67
|
+
filter{completed_at <= time}.destroy
|
68
|
+
else
|
69
|
+
filter(~{:completed_at => nil}).destroy unless keep?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Deletes all jobs.
|
75
|
+
#
|
76
|
+
# @return [Integer] amount the amount of jobs that were deleted
|
77
|
+
|
78
|
+
def self.delete_all
|
79
|
+
Navvy::Job.destroy
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Mark the job as started. Will set started_at to the current time.
|
84
|
+
#
|
85
|
+
# @return [true, false] update_attributes the result of the
|
86
|
+
# update_attributes call
|
87
|
+
|
88
|
+
def started
|
89
|
+
update(:started_at => Time.now)
|
90
|
+
save()
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
# Mark the job as completed. Will set completed_at to the current time and
|
95
|
+
# optionally add the return value if provided.
|
96
|
+
#
|
97
|
+
# @param [String] return_value the return value you want to store.
|
98
|
+
#
|
99
|
+
# @return [true, false] update_attributes the result of the
|
100
|
+
# update_attributes call
|
101
|
+
|
102
|
+
def completed(return_value = nil)
|
103
|
+
update(
|
104
|
+
:completed_at => Time.now,
|
105
|
+
:return => return_value
|
106
|
+
)
|
107
|
+
save()
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# Mark the job as failed. Will set failed_at to the current time and
|
112
|
+
# optionally add the exception message if provided. Also, it will retry
|
113
|
+
# the job unless max_attempts has been reached.
|
114
|
+
#
|
115
|
+
# @param [String] exception the exception message you want to store.
|
116
|
+
#
|
117
|
+
# @return [true, false] update_attributes the result of the
|
118
|
+
# update_attributes call
|
119
|
+
|
120
|
+
def failed(message = nil)
|
121
|
+
self.retry unless times_failed >= self.class.max_attempts
|
122
|
+
update(
|
123
|
+
:failed_at => Time.now,
|
124
|
+
:exception => message
|
125
|
+
)
|
126
|
+
save()
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Check how many times the job has failed. Will try to find jobs with a
|
131
|
+
# parent_id that's the same as self.id and count them
|
132
|
+
#
|
133
|
+
# @return [Integer] count the amount of times the job has failed
|
134
|
+
|
135
|
+
def times_failed
|
136
|
+
i = parent_id || id
|
137
|
+
self.class.filter({:id => i} | {:parent_id => i}).
|
138
|
+
filter(~{:failed_at => nil}).
|
139
|
+
count
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
require 'navvy/job'
|
data/lib/navvy/logger.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Navvy
|
4
|
+
class Logger < Logger
|
5
|
+
##
|
6
|
+
# Create a new logger. Works like Logger from Ruby's standard library, but
|
7
|
+
# defaults to STDOUT instead of failing. You can pass a filename to log to.
|
8
|
+
#
|
9
|
+
# @param [String] logdev a filename to log to, defaults to STDOUT
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# logger = Navvy::Logger.new
|
13
|
+
# logger = Navvy::Logger.new('~/file.log')
|
14
|
+
|
15
|
+
def initialize(logdev = STDOUT)
|
16
|
+
super logdev
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Send colored logs to the logger. Will only colorize output sent to
|
21
|
+
# STDOUT and will call the regular info method when writing to file.
|
22
|
+
#
|
23
|
+
# @param [String] message the message you want to log
|
24
|
+
# @param [String] color the color code you want to use to color your
|
25
|
+
# message
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# logger = Navvy::Logger.new
|
29
|
+
# logger.colorized_info "I'm green!", 32
|
30
|
+
|
31
|
+
def colorized_info(message, color)
|
32
|
+
unless @logdev.filename
|
33
|
+
return info("\e[#{color}m#{message}\e[0m")
|
34
|
+
end
|
35
|
+
info(message)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/navvy/tasks.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
task :environment
|
2
|
+
|
3
|
+
namespace :navvy do
|
4
|
+
desc "Clear the Navvy queue."
|
5
|
+
task :clear => :environment do
|
6
|
+
Navvy::Job.delete_all
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Start a Navvy worker."
|
10
|
+
task :work => :environment do
|
11
|
+
Navvy::Worker.start
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# heroku background jobs use jobs:work
|
16
|
+
task 'jobs:work' => 'navvy:work'
|
data/lib/navvy/worker.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module Navvy
|
2
|
+
class Worker
|
3
|
+
class << self
|
4
|
+
attr_writer :sleep_time
|
5
|
+
end
|
6
|
+
|
7
|
+
##
|
8
|
+
# Sleep time of the worker.
|
9
|
+
#
|
10
|
+
# @return [Integer] sleep
|
11
|
+
|
12
|
+
def self.sleep_time
|
13
|
+
@sleep_time || Navvy.configuration.sleep_time
|
14
|
+
end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Start the worker.
|
18
|
+
|
19
|
+
def self.start
|
20
|
+
Navvy.logger.info '*** Starting ***'
|
21
|
+
trap('TERM') { Navvy.logger.info '*** Exiting ***'; $exit = true }
|
22
|
+
trap('INT') { Navvy.logger.info '*** Exiting ***'; $exit = true }
|
23
|
+
|
24
|
+
loop do
|
25
|
+
fetch_and_run_jobs
|
26
|
+
|
27
|
+
if $exit
|
28
|
+
Navvy.logger.info '*** Cleaning up ***'
|
29
|
+
Navvy::Job.cleanup
|
30
|
+
break
|
31
|
+
end
|
32
|
+
sleep sleep_time
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# Fetch jobs and run them.
|
38
|
+
|
39
|
+
def self.fetch_and_run_jobs
|
40
|
+
Job.next.each do |job|
|
41
|
+
result = job.run
|
42
|
+
Navvy.logger.colorized_info(
|
43
|
+
"* #{job.object.to_s}.#{job.method_name}" <<
|
44
|
+
"(#{job.args.join(', ')}) => #{(job.exception || result).to_s}",
|
45
|
+
job.failed? ? 31 : 32
|
46
|
+
)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Navvy::Configuration do
|
4
|
+
after do
|
5
|
+
Navvy.configure do |config|
|
6
|
+
config.job_limit = 100
|
7
|
+
config.keep_jobs = false
|
8
|
+
config.logger = Navvy::Logger.new('/dev/null')
|
9
|
+
config.sleep_time = 5
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should have a job limit of 100 by default' do
|
14
|
+
Navvy::Job.limit.should == 100
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should set the job limit' do
|
18
|
+
Navvy.configure do |config|
|
19
|
+
config.job_limit = 10
|
20
|
+
end
|
21
|
+
|
22
|
+
Navvy::Job.limit.should == 10
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should have keep_jobs off by default' do
|
26
|
+
Navvy::Job.keep.should == false
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should set keep_jobs' do
|
30
|
+
Navvy.configure do |config|
|
31
|
+
config.keep_jobs = 10
|
32
|
+
end
|
33
|
+
|
34
|
+
Navvy::Job.keep.should == 10
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should set the logger' do
|
38
|
+
Navvy.configure do |config|
|
39
|
+
config.logger = Navvy::Logger.new
|
40
|
+
end
|
41
|
+
|
42
|
+
Navvy.logger.instance_variable_get(:@logdev).filename.should == nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should have a default sleep time of 5' do
|
46
|
+
Navvy::Worker.sleep_time.should == 5
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should turn quiet off' do
|
50
|
+
Navvy.configure do |config|
|
51
|
+
config.sleep_time = 10
|
52
|
+
end
|
53
|
+
|
54
|
+
Navvy::Worker.sleep_time.should == 10
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should have a default max_attempts of 25' do
|
58
|
+
Navvy::Job.max_attempts.should == 25
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should set max_attempts to 15' do
|
62
|
+
Navvy.configure do |config|
|
63
|
+
config.max_attempts = 15
|
64
|
+
end
|
65
|
+
|
66
|
+
Navvy::Job.max_attempts.should == 15
|
67
|
+
end
|
68
|
+
end
|
data/spec/job_spec.rb
ADDED
@@ -0,0 +1,505 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Navvy::Job' do
|
4
|
+
before do
|
5
|
+
Timecop.freeze(Time.local(2010, 1, 1))
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
Timecop.return
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.keep?' do
|
13
|
+
after(:each) do
|
14
|
+
Navvy::Job.keep = false
|
15
|
+
Navvy.configure do |config|
|
16
|
+
config.keep_jobs = false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'when configured using Navvy::Job.keep=' do
|
21
|
+
it 'should return false' do
|
22
|
+
Navvy::Job.keep = false
|
23
|
+
Navvy::Job.keep?.should == false
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should return true' do
|
27
|
+
Navvy::Job.keep = true
|
28
|
+
Navvy::Job.keep?.should == true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'when configured with Navvy.configure' do
|
33
|
+
it 'should return false' do
|
34
|
+
Navvy.configure do |config|
|
35
|
+
config.keep_jobs = false
|
36
|
+
end
|
37
|
+
Navvy::Job.keep?.should == false
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should return true' do
|
41
|
+
Navvy.configure do |config|
|
42
|
+
config.keep_jobs = true
|
43
|
+
end
|
44
|
+
Navvy::Job.keep?.should == true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '.enqueue' do
|
50
|
+
before(:each) do
|
51
|
+
Navvy::Job.delete_all
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should enqueue a job' do
|
55
|
+
Navvy::Job.enqueue(Cow, :speak)
|
56
|
+
job_count.should == 1
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should set the object and the method_name' do
|
60
|
+
Navvy::Job.enqueue(Cow, :speak)
|
61
|
+
job = first_job
|
62
|
+
job.object.should == 'Cow'
|
63
|
+
job.method_name.to_s.should == 'speak'
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should turn the method_name into a symbol' do
|
67
|
+
Navvy::Job.enqueue(Cow, 'speak')
|
68
|
+
job = first_job
|
69
|
+
job.method_name.to_s.should == 'speak'
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should set the arguments' do
|
73
|
+
Navvy::Job.enqueue(Cow, :speak, true, false)
|
74
|
+
job = first_job
|
75
|
+
job.args.should == [true, false]
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should set the created_at date' do
|
79
|
+
Navvy::Job.enqueue(Cow, :speak, true, false)
|
80
|
+
job = first_job
|
81
|
+
job.created_at.should == Time.now
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should set the run_at date' do
|
85
|
+
Navvy::Job.enqueue(Cow, :speak, true, false)
|
86
|
+
job = first_job
|
87
|
+
job.run_at.should == Time.now
|
88
|
+
job.created_at.should == Time.now
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should return the enqueued job' do
|
92
|
+
Navvy::Job.enqueue(Cow, :speak, true, false).
|
93
|
+
should be_instance_of Navvy::Job
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '.next' do
|
98
|
+
before(:each) do
|
99
|
+
Navvy::Job.delete_all
|
100
|
+
Navvy::Job.create(
|
101
|
+
:object => 'Cow',
|
102
|
+
:method_name => :last,
|
103
|
+
:created_at => Time.now + (60 * 60),
|
104
|
+
:run_at => Time.now
|
105
|
+
)
|
106
|
+
Navvy::Job.create(
|
107
|
+
:object => 'Cow',
|
108
|
+
:method_name => :break,
|
109
|
+
:completed_at => Time.now,
|
110
|
+
:run_at => Time.now
|
111
|
+
)
|
112
|
+
Navvy::Job.create(
|
113
|
+
:object => 'Cow',
|
114
|
+
:method_name => :break,
|
115
|
+
:failed_at => Time.now,
|
116
|
+
:run_at => Time.now
|
117
|
+
)
|
118
|
+
Navvy::Job.create(
|
119
|
+
:object => 'Cow',
|
120
|
+
:method_name => :tomorrow,
|
121
|
+
:run_at => Time.now + (60 * 60)
|
122
|
+
)
|
123
|
+
120.times do
|
124
|
+
Navvy::Job.enqueue(Cow, :speak)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should find the next 10 available jobs' do
|
129
|
+
jobs = Navvy::Job.next
|
130
|
+
jobs.length.should == 100
|
131
|
+
jobs.each do |job|
|
132
|
+
job.should be_instance_of Navvy::Job
|
133
|
+
job.method_name.to_s.should == 'speak'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'should find the next 2 available jobs' do
|
138
|
+
Navvy::Job.next(2).length.should == 2
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should find the next 4 available jobs' do
|
142
|
+
Navvy::Job.limit = 4
|
143
|
+
Navvy::Job.next.length.should == 4
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '.cleanup' do
|
148
|
+
before(:each) do
|
149
|
+
Navvy::Job.delete_all
|
150
|
+
Navvy::Job.create(
|
151
|
+
:object => 'Cow',
|
152
|
+
:method_name => :speak,
|
153
|
+
:completed_at => Time.now - (2 * 60 * 60)
|
154
|
+
)
|
155
|
+
Navvy::Job.create(
|
156
|
+
:object => 'Cow',
|
157
|
+
:method_name => :speak,
|
158
|
+
:completed_at => Time.now
|
159
|
+
)
|
160
|
+
Navvy::Job.create(
|
161
|
+
:object => 'Cow',
|
162
|
+
:method_name => :speak
|
163
|
+
)
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should delete all complete jobs when "keep" is false' do
|
167
|
+
Navvy::Job.cleanup
|
168
|
+
job_count.should == 1
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'should not delete any complete jobs when "keep" is true' do
|
172
|
+
Navvy::Job.keep = true
|
173
|
+
Navvy::Job.cleanup
|
174
|
+
job_count.should == 3
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'should delete all complete jobs where "keep" has passed' do
|
178
|
+
Navvy::Job.keep = (60 * 60)
|
179
|
+
Navvy::Job.cleanup
|
180
|
+
job_count.should == 2
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe '.delete_all' do
|
185
|
+
it 'should delete all jobs' do
|
186
|
+
3.times do; Navvy::Job.create; end
|
187
|
+
Navvy::Job.delete_all
|
188
|
+
job_count.should == 0
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe '#run' do
|
193
|
+
it 'should pass the arguments' do
|
194
|
+
Navvy::Job.delete_all
|
195
|
+
job = Navvy::Job.enqueue(Cow, :name, 'Betsy')
|
196
|
+
Cow.should_receive(:name).with('Betsy')
|
197
|
+
job.run
|
198
|
+
end
|
199
|
+
|
200
|
+
describe 'when everything goes well' do
|
201
|
+
before(:each) do
|
202
|
+
Navvy::Job.delete_all
|
203
|
+
Navvy::Job.enqueue(Cow, :speak)
|
204
|
+
Navvy::Job.keep = false
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should run the job and delete it' do
|
208
|
+
jobs = Navvy::Job.next
|
209
|
+
jobs.first.run.should == '"moo"'
|
210
|
+
job_count.should == 0
|
211
|
+
end
|
212
|
+
|
213
|
+
describe 'when Navvy::Job.keep is set' do
|
214
|
+
it 'should call #completed with the return value after processing the job' do
|
215
|
+
Navvy::Job.keep = true
|
216
|
+
jobs = Navvy::Job.next
|
217
|
+
jobs.first.should_receive(:completed).with('"moo"')
|
218
|
+
jobs.first.run
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'should mark the job as complete when keep is true' do
|
222
|
+
Navvy::Job.keep = true
|
223
|
+
jobs = Navvy::Job.next
|
224
|
+
jobs.first.run
|
225
|
+
job_count.should == 1
|
226
|
+
jobs.first.started_at.should == Time.now
|
227
|
+
jobs.first.completed_at.should == Time.now
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'should mark the job as complete when keep has not passed yet' do
|
231
|
+
Navvy::Job.keep = (60 * 60)
|
232
|
+
jobs = Navvy::Job.next
|
233
|
+
jobs.first.run
|
234
|
+
job_count.should == 1
|
235
|
+
jobs.first.started_at.should == Time.now
|
236
|
+
jobs.first.completed_at.should == Time.now
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'should delete the job when the "keep" flag has passed' do
|
240
|
+
Navvy::Job.keep = -(60 * 60)
|
241
|
+
jobs = Navvy::Job.next
|
242
|
+
jobs.first.run
|
243
|
+
job_count.should == 0
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
describe 'when a job fails' do
|
249
|
+
before(:each) do
|
250
|
+
Navvy::Job.delete_all
|
251
|
+
Navvy::Job.enqueue(Cow, :broken)
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'should store the exception and current time' do
|
255
|
+
jobs = Navvy::Job.next
|
256
|
+
jobs.first.run
|
257
|
+
jobs.first.exception.should == 'this method is broken'
|
258
|
+
jobs.first.started_at.should == Time.now
|
259
|
+
jobs.first.failed_at.should == Time.now
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe '#started' do
|
265
|
+
before(:each) do
|
266
|
+
Navvy::Job.delete_all
|
267
|
+
Navvy::Job.enqueue(Cow, :speak)
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'should update the jobs started_at date' do
|
271
|
+
jobs = Navvy::Job.next
|
272
|
+
jobs.first.started
|
273
|
+
jobs.first.started_at.should_not be_nil
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
describe '#completed' do
|
278
|
+
before(:each) do
|
279
|
+
Navvy::Job.delete_all
|
280
|
+
Navvy::Job.enqueue(Cow, :speak)
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'should update the jobs completed_at date' do
|
284
|
+
jobs = Navvy::Job.next
|
285
|
+
jobs.first.completed
|
286
|
+
jobs.first.completed_at.should_not be_nil
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'should set the return if provided' do
|
290
|
+
jobs = Navvy::Job.next
|
291
|
+
jobs.first.completed('woo!')
|
292
|
+
jobs.first.return.should == 'woo!'
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
describe '#failed' do
|
297
|
+
before(:each) do
|
298
|
+
Navvy::Job.delete_all
|
299
|
+
Navvy::Job.enqueue(Cow, :speak)
|
300
|
+
end
|
301
|
+
|
302
|
+
it 'should update the jobs failed_at date' do
|
303
|
+
jobs = Navvy::Job.next
|
304
|
+
jobs.first.failed
|
305
|
+
jobs.first.failed_at.should_not be_nil
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'should set the exception message if provided' do
|
309
|
+
jobs = Navvy::Job.next
|
310
|
+
jobs.first.failed('broken')
|
311
|
+
jobs.first.exception.should == 'broken'
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'should retry' do
|
315
|
+
jobs = Navvy::Job.next
|
316
|
+
jobs.first.should_receive(:retry)
|
317
|
+
jobs.first.failed('broken')
|
318
|
+
end
|
319
|
+
|
320
|
+
it 'should not retry when the job has failed 25 times already' do
|
321
|
+
jobs = Navvy::Job.next
|
322
|
+
jobs.first.stub!(:times_failed).and_return 25
|
323
|
+
jobs.first.should_not_receive(:retry)
|
324
|
+
jobs.first.failed('broken')
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'should not retry when the job has failed 10 times' do
|
328
|
+
Navvy::Job.max_attempts = 10
|
329
|
+
jobs = Navvy::Job.next
|
330
|
+
jobs.first.stub!(:times_failed).and_return 10
|
331
|
+
jobs.first.should_not_receive(:retry)
|
332
|
+
jobs.first.failed('broken')
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
describe '#retry' do
|
337
|
+
before(:each) do
|
338
|
+
Navvy::Job.delete_all
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'should enqueue a child for the failed job' do
|
342
|
+
failed_job = Navvy::Job.enqueue(Cow, :speak, true, false)
|
343
|
+
job = failed_job.retry
|
344
|
+
job.object.should == 'Cow'
|
345
|
+
job.method_name.to_s.should == 'speak'
|
346
|
+
job.args.should == [true, false]
|
347
|
+
job.parent_id.should == failed_job.id
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'should handle hashes correctly' do
|
351
|
+
failed_job = Navvy::Job.enqueue(Cow, :speak, 'name' => 'Betsy')
|
352
|
+
job = failed_job.retry
|
353
|
+
job.args.should == [{'name' => 'Betsy'}]
|
354
|
+
job.parent_id.should == failed_job.id
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'should set the priority' do
|
358
|
+
failed_job = Navvy::Job.enqueue(
|
359
|
+
Cow,
|
360
|
+
:speak,
|
361
|
+
'name' => 'Betsy',
|
362
|
+
:job_options => {
|
363
|
+
:priority => 2
|
364
|
+
}
|
365
|
+
)
|
366
|
+
job = failed_job.retry
|
367
|
+
job.priority.should == 2
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'should set the run_at date to about 16 seconds from now' do
|
371
|
+
failed_job = Navvy::Job.enqueue(Cow, :speak, 'name' => 'Betsy')
|
372
|
+
failed_job.stub!(:times_failed).and_return 2
|
373
|
+
job = failed_job.retry
|
374
|
+
job.run_at.to_i.should == (Time.now + 16).to_i
|
375
|
+
end
|
376
|
+
|
377
|
+
it 'should set the run_at date to about 256 seconds from now' do
|
378
|
+
failed_job = Navvy::Job.enqueue(Cow, :speak, 'name' => 'Betsy')
|
379
|
+
failed_job.stub!(:times_failed).and_return 4
|
380
|
+
job = failed_job.retry
|
381
|
+
job.run_at.to_i.should == (Time.now + 256).to_i
|
382
|
+
end
|
383
|
+
|
384
|
+
it 'should set the run_at date to about 4096 seconds from now' do
|
385
|
+
failed_job = Navvy::Job.enqueue(Cow, :speak, 'name' => 'Betsy')
|
386
|
+
failed_job.stub!(:times_failed).and_return 8
|
387
|
+
job = failed_job.retry
|
388
|
+
job.run_at.to_i.should == (Time.now + 4096).to_i
|
389
|
+
end
|
390
|
+
|
391
|
+
it 'should set the parent_id to the master job id' do
|
392
|
+
failed_job = Navvy::Job.enqueue(Cow, :speak, 'name' => 'Betsy')
|
393
|
+
failed_child = failed_job.retry
|
394
|
+
failed_child.retry.parent_id.should == failed_job.id
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
describe '#times_failed' do
|
399
|
+
before(:each) do
|
400
|
+
Navvy::Job.delete_all
|
401
|
+
@failed_job = Navvy::Job.create(
|
402
|
+
:failed_at => Time.now
|
403
|
+
)
|
404
|
+
end
|
405
|
+
|
406
|
+
it 'should return 1' do
|
407
|
+
@failed_job.times_failed.should == 1
|
408
|
+
end
|
409
|
+
|
410
|
+
it 'should return 3 when having 2 failed children' do
|
411
|
+
2.times do
|
412
|
+
Navvy::Job.create(
|
413
|
+
:failed_at => Time.now,
|
414
|
+
:parent_id => @failed_job.id
|
415
|
+
)
|
416
|
+
end
|
417
|
+
|
418
|
+
@failed_job.times_failed.should == 3
|
419
|
+
end
|
420
|
+
|
421
|
+
it 'should return 2 when having 1 failed and one pending child' do
|
422
|
+
Navvy::Job.create(
|
423
|
+
:failed_at => Time.now,
|
424
|
+
:parent_id => @failed_job.id
|
425
|
+
)
|
426
|
+
|
427
|
+
Navvy::Job.create(
|
428
|
+
:parent_id => @failed_job.id
|
429
|
+
)
|
430
|
+
|
431
|
+
Navvy::Job.create(
|
432
|
+
:parent_id => @failed_job.id
|
433
|
+
)
|
434
|
+
|
435
|
+
@failed_job.times_failed.should == 2
|
436
|
+
end
|
437
|
+
|
438
|
+
it 'should return 2 when having failed and having a failed parent' do
|
439
|
+
failed_child = Navvy::Job.create(
|
440
|
+
:failed_at => Time.now,
|
441
|
+
:parent_id => @failed_job.id
|
442
|
+
)
|
443
|
+
failed_child.times_failed.should == 2
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
describe '#ran?' do
|
448
|
+
it 'should return false when failed_at? and completed_at? are false' do
|
449
|
+
job = Navvy::Job.create
|
450
|
+
job.ran?.should be_false
|
451
|
+
end
|
452
|
+
|
453
|
+
it 'should return true when failed_at? or completed_at? is true' do
|
454
|
+
[
|
455
|
+
Navvy::Job.create(:failed_at => Time.now),
|
456
|
+
Navvy::Job.create(:completed_at => Time.now)
|
457
|
+
].each do |job|
|
458
|
+
job.ran?.should be_true
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
describe '#duration' do
|
464
|
+
it 'should return a duration if started_at and completed_at are set' do
|
465
|
+
job = Navvy::Job.create(
|
466
|
+
:started_at => Time.now - 2,
|
467
|
+
:completed_at => Time.now
|
468
|
+
)
|
469
|
+
|
470
|
+
job.duration.should == 2
|
471
|
+
end
|
472
|
+
|
473
|
+
it 'should return a duration if started_at and failed_at are set' do
|
474
|
+
job = Navvy::Job.create(
|
475
|
+
:started_at => Time.now - 3,
|
476
|
+
:failed_at => Time.now
|
477
|
+
)
|
478
|
+
|
479
|
+
job.duration.should == 3
|
480
|
+
end
|
481
|
+
|
482
|
+
it 'should return 0 if only started_at is set' do
|
483
|
+
job = Navvy::Job.create(
|
484
|
+
:started_at => Time.now - 4
|
485
|
+
)
|
486
|
+
|
487
|
+
job.duration.should == 0
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
describe '#args' do
|
492
|
+
it 'should return an array of arguments' do
|
493
|
+
job = Navvy::Job.enqueue(Cow, :speak, true, false)
|
494
|
+
job.args.should be_instance_of Array
|
495
|
+
job.args.count.should == 2
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
describe '#namespaced' do
|
500
|
+
it 'should accept a namespaced class name' do
|
501
|
+
job = Navvy::Job.enqueue(Animals::Cow, :speak)
|
502
|
+
job.run.should == '"moo"'
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|