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