navvy 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,200 @@
1
+ require 'rubygems'
2
+ require 'sequel'
3
+ require 'yaml'
4
+
5
+ module Navvy
6
+ class Job < Sequel::Model
7
+ class << self
8
+ attr_writer :limit
9
+ attr_accessor :keep
10
+ end
11
+
12
+ ##
13
+ # Default limit of jobs to be fetched
14
+ #
15
+ # @return [Integer] limit
16
+
17
+ def self.limit
18
+ @limit || 100
19
+ end
20
+
21
+ ##
22
+ # Should the job be kept?
23
+ #
24
+ # @return [true, false] keep
25
+
26
+ def self.keep?
27
+ keep = (@keep || false)
28
+ return (Time.now + keep) >= Time.now if keep.is_a? Fixnum
29
+ keep
30
+ end
31
+
32
+ ##
33
+ # Add a job to the job queue.
34
+ #
35
+ # @param [Object] object the object you want to run a method from
36
+ # @param [Symbol, String] method_name the name of the method you want to
37
+ # run
38
+ # @param [*] arguments optional arguments you want to pass to the method
39
+ #
40
+ # @return [true, false]
41
+
42
+ def self.enqueue(object, method_name, *args)
43
+ create(
44
+ :object => object.to_s,
45
+ :method_name => method_name.to_s,
46
+ :arguments => YAML::dump(args),
47
+ :run_at => Time.now,
48
+ :created_at => Time.now
49
+ )
50
+ end
51
+
52
+ ##
53
+ # Find the next available jobs in the queue. This will not include failed
54
+ # jobs (where :failed_at is not nil) and jobs that should run in the future
55
+ # (where :run_at is greater than the current time).
56
+ #
57
+ # @param [Integer] limit the limit of jobs to be fetched. Defaults to
58
+ # Navvy::Job.limit
59
+ #
60
+ # @return [array, nil] the next available jobs in an array or nil if no
61
+ # jobs were found.
62
+
63
+ def self.next(limit = self.limit)
64
+ filter(
65
+ '`failed_at` IS NULL AND `completed_at` IS NULL AND `run_at` <= ?',
66
+ Time.now
67
+ ).order(:created_at).first(limit)
68
+ end
69
+
70
+ ##
71
+ # Clean up jobs that we don't need to keep anymore. If Navvy::Job.keep is
72
+ # false it'll delete every completed job, if it's a timestamp it'll only
73
+ # delete completed jobs that have passed their keeptime.
74
+ #
75
+ # @return [true, false] delete_all the result of the delete_all call
76
+
77
+ def self.cleanup
78
+ if keep.is_a? Fixnum
79
+ filter('`completed_at` <= ?', (Time.now - keep)).delete
80
+ else
81
+ filter('`completed_at` IS NOT NULL').delete unless keep?
82
+ end
83
+ end
84
+
85
+ ##
86
+ # Run the job. Will delete the Navvy::Job record and return its return
87
+ # value if it runs successfully unless Navvy::Job.keep is set. If a job
88
+ # fails, it'll update the Navvy::Job record to include the exception
89
+ # message it sent back and set the :failed_at date. Failed jobs never get
90
+ # deleted.
91
+ #
92
+ # @example
93
+ # job = Navvy::Job.next # finds the next available job in the queue
94
+ # job.run # runs the job and returns the job's return value
95
+ #
96
+ # @return [String] return value of the called method.
97
+
98
+ def run
99
+ begin
100
+ update(:started_at => Time.now)
101
+ result = Kernel.const_get(object).send(method_name, *args)
102
+ Navvy::Job.keep? ? completed : destroy
103
+ result
104
+ rescue Exception => exception
105
+ failed(exception.message)
106
+ end
107
+ end
108
+
109
+ ##
110
+ # Mark the job as completed. Will set completed_at to the current time and
111
+ # optionally add the return value if provided.
112
+ #
113
+ # @param [String] return_value the return value you want to store.
114
+ #
115
+ # @return [true, false] update_attributes the result of the
116
+ # update_attributes call
117
+
118
+ def completed(return_value = nil)
119
+ update({
120
+ :completed_at => Time.now,
121
+ :return => return_value
122
+ })
123
+ end
124
+
125
+ ##
126
+ # Mark the job as failed. Will set failed_at to the current time and
127
+ # optionally add the exception message if provided.
128
+ #
129
+ # @param [String] exception the exception message you want to store.
130
+ #
131
+ # @return [true, false] update_attributes the result of the
132
+ # update_attributes call
133
+
134
+ def failed(message = nil)
135
+ update({
136
+ :failed_at => Time.now,
137
+ :exception => message
138
+ })
139
+ end
140
+
141
+ ##
142
+ # Check if the job has been run.
143
+ #
144
+ # @return [true, false] ran
145
+
146
+ def ran?
147
+ completed? || failed?
148
+ end
149
+
150
+ ##
151
+ # Check how long it took for a job to complete or fail
152
+ #
153
+ # @return [Time, Integer] time the time it took
154
+
155
+ def duration
156
+ ran? ? (completed_at || failed_at) - started_at : 0
157
+ end
158
+
159
+ ##
160
+ # Check if completed_at is set
161
+ #
162
+ # @return [true, false] set?
163
+
164
+ def completed_at?
165
+ !completed_at.nil?
166
+ end
167
+
168
+ ##
169
+ # Check if failed_at is set
170
+ #
171
+ # @return [true, false] set?
172
+
173
+ def failed_at?
174
+ !failed_at.nil?
175
+ end
176
+
177
+ ##
178
+ # Get the job arguments as an array
179
+ #
180
+ # @return [array] arguments
181
+
182
+ def args
183
+ arguments.first.is_a?(Array) ? arguments : YAML.load(arguments)
184
+ end
185
+
186
+ ##
187
+ # Get the job status
188
+ #
189
+ # @return [:pending, :completed, :failed] status
190
+
191
+ def status
192
+ return :completed if completed?
193
+ return :failed if failed?
194
+ :pending
195
+ end
196
+
197
+ alias_method :completed?, :completed_at?
198
+ alias_method :failed?, :failed_at?
199
+ end
200
+ end
data/lib/navvy/log.rb ADDED
@@ -0,0 +1,60 @@
1
+ module Navvy
2
+ class Log
3
+ class << self
4
+ attr_writer :logger
5
+ attr_accessor :quiet
6
+ end
7
+
8
+ class LoggerNotFound < StandardError; end
9
+
10
+ def self.logger
11
+ @logger
12
+ end
13
+
14
+ ##
15
+ # Pass a log to the logger. It will check if self.logger is an array. If it
16
+ # is, it'll loop through it and log to every logger. If it's not, it'll
17
+ # just log once.
18
+ #
19
+ # @param [String] message the message you want to log
20
+ # @param [Integer] color an optional color code to use in the terminal
21
+ # output
22
+
23
+ def self.info(message, color = nil)
24
+ if logger.is_a? Array
25
+ logger.each do |logger|
26
+ write(logger, message, color)
27
+ end
28
+ else
29
+ write(logger, message, color)
30
+ end
31
+ end
32
+
33
+ ##
34
+ # Actually write the log to the logger. It'll check self.logger and use
35
+ # that to define a logger
36
+ #
37
+ # @param [Symbol] logger the logger you want to use
38
+ # @param [String] message the message you want to log
39
+ # @param [Integer] color an optional color code to use in the terminal
40
+ # output
41
+
42
+ def self.write(logger, message, color = nil)
43
+ puts "\e[#{color}m#{message}\e[0m" unless quiet
44
+ case logger
45
+ when :justlogging
46
+ raise(
47
+ LoggerNotFound,
48
+ 'JustLogging could not be found. No logs were created.'
49
+ ) unless defined? Justlogging.log
50
+ Justlogging.log(message)
51
+ when :rails
52
+ raise(
53
+ LoggerNotFound,
54
+ 'RAILS_DEFAULT_LOGGER could not be found. No logs were created.'
55
+ ) unless defined? RAILS_DEFAULT_LOGGER.info
56
+ RAILS_DEFAULT_LOGGER.info(message)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,13 @@
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
data/lib/navvy/worker.rb CHANGED
@@ -1,22 +1,37 @@
1
1
  module Navvy
2
2
  class Worker
3
-
3
+
4
4
  ##
5
5
  # Start the worker.
6
-
6
+
7
7
  def self.start
8
+ Navvy::Log.info '*** Starting ***'
9
+ trap('TERM') { Navvy::Log.info '*** Exiting ***'; $exit = true }
10
+ trap('INT') { Navvy::Log.info '*** Exiting ***'; $exit = true }
11
+
8
12
  loop do
9
13
  fetch_and_run_jobs
14
+
15
+ if $exit
16
+ Navvy::Log.info '*** Cleaning up ***'
17
+ Navvy::Job.cleanup
18
+ break
19
+ end
10
20
  sleep 5
11
21
  end
12
22
  end
13
-
23
+
14
24
  ##
15
- # Fetch jobs an run them.
16
-
25
+ # Fetch jobs and run them.
26
+
17
27
  def self.fetch_and_run_jobs
18
28
  Job.next.each do |job|
19
- job.run
29
+ result = job.run
30
+ Navvy::Log.info(
31
+ "* #{job.object.to_s}.#{job.method_name}" <<
32
+ "(#{job.args.join(', ')}) => #{(job.exception || result).to_s}",
33
+ job.failed? ? 31 : 32
34
+ )
20
35
  end
21
36
  end
22
37
  end
data/navvy.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{navvy}
8
- s.version = "0.0.0"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jeff Kreeftmeijer"]
12
- s.date = %q{2010-01-11}
12
+ s.date = %q{2010-01-21}
13
13
  s.description = %q{Simple background job processor inspired by delayed_job, but aiming for database agnosticism.}
14
14
  s.email = %q{jeff@kreeftmeijer.nl}
15
15
  s.extra_rdoc_files = [
@@ -23,14 +23,24 @@ Gem::Specification.new do |s|
23
23
  "README.textile",
24
24
  "Rakefile",
25
25
  "VERSION",
26
- "lib/job/mongo_mapper.rb",
26
+ "generators/navvy/navvy_generator.rb",
27
+ "generators/navvy/templates/migration.rb",
28
+ "lib/generators/navvy_generator.rb",
27
29
  "lib/navvy.rb",
28
- "lib/navvy/job.rb",
30
+ "lib/navvy/job/active_record.rb",
29
31
  "lib/navvy/job/mongo_mapper.rb",
32
+ "lib/navvy/job/sequel.rb",
33
+ "lib/navvy/log.rb",
34
+ "lib/navvy/tasks.rb",
30
35
  "lib/navvy/worker.rb",
31
36
  "navvy.gemspec",
37
+ "spec/job/active_record_spec.rb",
32
38
  "spec/job/mongo_mapper_spec.rb",
39
+ "spec/job/sequel_spec.rb",
40
+ "spec/log_spec.rb",
41
+ "spec/setup/active_record.rb",
33
42
  "spec/setup/mongo_mapper.rb",
43
+ "spec/setup/sequel.rb",
34
44
  "spec/spec_helper.rb",
35
45
  "spec/worker_spec.rb"
36
46
  ]
@@ -40,8 +50,13 @@ Gem::Specification.new do |s|
40
50
  s.rubygems_version = %q{1.3.5}
41
51
  s.summary = %q{Simple background job processor inspired by delayed_job, but aiming for database agnosticism.}
42
52
  s.test_files = [
43
- "spec/job/mongo_mapper_spec.rb",
53
+ "spec/job/active_record_spec.rb",
54
+ "spec/job/mongo_mapper_spec.rb",
55
+ "spec/job/sequel_spec.rb",
56
+ "spec/log_spec.rb",
57
+ "spec/setup/active_record.rb",
44
58
  "spec/setup/mongo_mapper.rb",
59
+ "spec/setup/sequel.rb",
45
60
  "spec/spec_helper.rb",
46
61
  "spec/worker_spec.rb"
47
62
  ]
@@ -51,26 +66,29 @@ Gem::Specification.new do |s|
51
66
  s.specification_version = 3
52
67
 
53
68
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
54
- s.add_runtime_dependency(%q<mongo_mapper>, [">= 0.6.10"])
55
69
  s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
56
70
  s.add_development_dependency(%q<yard>, [">= 0.5.2"])
57
71
  s.add_development_dependency(%q<metric_fu>, [">= 1.1.6"])
58
72
  s.add_development_dependency(%q<machinist>, [">= 1.0.6"])
73
+ s.add_development_dependency(%q<mongo_mapper>, [">= 0.6.10"])
59
74
  s.add_development_dependency(%q<machinist_mongomapper>, [">= 0.9.7"])
75
+ s.add_development_dependency(%q<sequel>, [">= 3.8.0"])
60
76
  else
61
- s.add_dependency(%q<mongo_mapper>, [">= 0.6.10"])
62
77
  s.add_dependency(%q<rspec>, [">= 1.2.9"])
63
78
  s.add_dependency(%q<yard>, [">= 0.5.2"])
64
79
  s.add_dependency(%q<metric_fu>, [">= 1.1.6"])
65
80
  s.add_dependency(%q<machinist>, [">= 1.0.6"])
81
+ s.add_dependency(%q<mongo_mapper>, [">= 0.6.10"])
66
82
  s.add_dependency(%q<machinist_mongomapper>, [">= 0.9.7"])
83
+ s.add_dependency(%q<sequel>, [">= 3.8.0"])
67
84
  end
68
85
  else
69
- s.add_dependency(%q<mongo_mapper>, [">= 0.6.10"])
70
86
  s.add_dependency(%q<rspec>, [">= 1.2.9"])
71
87
  s.add_dependency(%q<yard>, [">= 0.5.2"])
72
88
  s.add_dependency(%q<metric_fu>, [">= 1.1.6"])
73
89
  s.add_dependency(%q<machinist>, [">= 1.0.6"])
90
+ s.add_dependency(%q<mongo_mapper>, [">= 0.6.10"])
74
91
  s.add_dependency(%q<machinist_mongomapper>, [">= 0.9.7"])
92
+ s.add_dependency(%q<sequel>, [">= 3.8.0"])
75
93
  end
76
94
  end
@@ -0,0 +1,325 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe 'Navvy::Job' do
4
+ before do
5
+ require File.expand_path(File.dirname(__FILE__) + '/../setup/active_record')
6
+ end
7
+
8
+ describe '.enqueue' do
9
+ before(:each) do
10
+ Navvy::Job.delete_all
11
+ end
12
+
13
+ it 'should enqueue a job' do
14
+ Navvy::Job.enqueue(Cow, :speak)
15
+ Navvy::Job.count.should == 1
16
+ end
17
+
18
+ it 'should set the object and the method_name' do
19
+ Navvy::Job.enqueue(Cow, :speak)
20
+ job = Navvy::Job.first
21
+ job.object.should == 'Cow'
22
+ job.method_name.should == 'speak'
23
+ end
24
+
25
+ it 'should turn the method_name into a symbol' do
26
+ Navvy::Job.enqueue(Cow, 'speak')
27
+ job = Navvy::Job.first
28
+ job.method_name.should == 'speak'
29
+ end
30
+
31
+ it 'should set the arguments' do
32
+ Navvy::Job.enqueue(Cow, :speak, true, false)
33
+ job = Navvy::Job.first
34
+ YAML.load(job.arguments).should == [true, false]
35
+ end
36
+
37
+ it 'should set the created_at date' do
38
+ Navvy::Job.enqueue(Cow, :speak, true, false)
39
+ job = Navvy::Job.first
40
+ job.created_at.should be_instance_of Time
41
+ job.created_at.should <= Time.now
42
+ job.created_at.should > 10.seconds.ago
43
+ end
44
+
45
+ it 'should set the run_at date' do
46
+ Navvy::Job.enqueue(Cow, :speak, true, false)
47
+ job = Navvy::Job.first
48
+ job.run_at.should be_instance_of Time
49
+ job.run_at.should <= Time.now
50
+ job.created_at.should > 10.seconds.ago
51
+ end
52
+
53
+ it 'should return the enqueued job' do
54
+ Navvy::Job.enqueue(Cow, :speak, true, false).
55
+ should be_instance_of Navvy::Job
56
+ end
57
+ end
58
+
59
+ describe '.next' do
60
+ before(:each) do
61
+ Navvy::Job.delete_all
62
+ Navvy::Job.create(
63
+ :object => 'Cow',
64
+ :method_name => :last,
65
+ :created_at => Time.now + 1.day,
66
+ :run_at => Time.now
67
+ )
68
+ Navvy::Job.create(
69
+ :object => 'Cow',
70
+ :method_name => :break,
71
+ :completed_at => Time.now,
72
+ :run_at => Time.now
73
+ )
74
+ Navvy::Job.create(
75
+ :object => 'Cow',
76
+ :method_name => :break,
77
+ :failed_at => Time.now,
78
+ :run_at => Time.now
79
+ )
80
+ Navvy::Job.create(
81
+ :object => 'Cow',
82
+ :method_name => :tomorrow,
83
+ :run_at => Time.now + 1.day
84
+ )
85
+ 120.times do
86
+ Navvy::Job.enqueue(Cow, :speak)
87
+ end
88
+ end
89
+
90
+ it 'should find the next 10 available jobs' do
91
+ jobs = Navvy::Job.next
92
+ jobs.count.should == 100
93
+ jobs.each do |job|
94
+ job.should be_instance_of Navvy::Job
95
+ job.method_name.should == 'speak'
96
+ end
97
+ end
98
+
99
+ it 'should find the next 2 available jobs' do
100
+ Navvy::Job.next(2).count.should == 2
101
+ end
102
+
103
+ it 'should find the next 4 available jobs' do
104
+ Navvy::Job.limit = 4
105
+ Navvy::Job.next.count.should == 4
106
+ end
107
+ end
108
+
109
+ describe '.cleanup' do
110
+ before(:each) do
111
+ Navvy::Job.delete_all
112
+ Navvy::Job.create(
113
+ :object => 'Cow',
114
+ :method_name => :speak,
115
+ :completed_at => Time.now - 2.days
116
+ )
117
+ Navvy::Job.create(
118
+ :object => 'Cow',
119
+ :method_name => :speak,
120
+ :completed_at => Time.now
121
+ )
122
+ Navvy::Job.create(
123
+ :object => 'Cow',
124
+ :method_name => :speak
125
+ )
126
+ end
127
+
128
+ it 'should delete all complete jobs when "keep" is false' do
129
+ Navvy::Job.cleanup
130
+ Navvy::Job.count.should == 1
131
+ end
132
+
133
+ it 'should not delete any complete jobs when "keep" is true' do
134
+ Navvy::Job.keep = true
135
+ Navvy::Job.cleanup
136
+ Navvy::Job.count.should == 3
137
+ end
138
+
139
+ it 'should delete all complete jobs where "keep" has passed' do
140
+ Navvy::Job.keep = 1.day
141
+ Navvy::Job.cleanup
142
+ Navvy::Job.count.should == 2
143
+ end
144
+ end
145
+
146
+ describe '#run' do
147
+
148
+ it 'should pass the arguments' do
149
+ Navvy::Job.delete_all
150
+ job = Navvy::Job.enqueue(Cow, :name, 'Betsy')
151
+ Cow.should_receive(:name).with('Betsy')
152
+ job.run
153
+ end
154
+
155
+ describe 'when everything goes well' do
156
+ before(:each) do
157
+ Navvy::Job.delete_all
158
+ Navvy::Job.enqueue(Cow, :speak)
159
+ Navvy::Job.keep = false
160
+ end
161
+
162
+ it 'should run the job and delete it' do
163
+ jobs = Navvy::Job.next
164
+ jobs.first.run.should == 'moo'
165
+ Navvy::Job.count.should == 0
166
+ end
167
+
168
+ describe 'when Navvy::Job.keep is set' do
169
+ it 'should mark the job as complete when keep is true' do
170
+ Navvy::Job.keep = true
171
+ jobs = Navvy::Job.next
172
+ jobs.first.run
173
+ Navvy::Job.count.should == 1
174
+ jobs.first.started_at.should be_instance_of Time
175
+ jobs.first.completed_at.should be_instance_of Time
176
+ end
177
+
178
+ it 'should mark the job as complete when keep has not passed yer' do
179
+ Navvy::Job.keep = 1.day
180
+ jobs = Navvy::Job.next
181
+ jobs.first.run
182
+ Navvy::Job.count.should == 1
183
+ jobs.first.started_at.should be_instance_of Time
184
+ jobs.first.completed_at.should be_instance_of Time
185
+ end
186
+
187
+ it 'should delete the job when the "keep" flag has passed' do
188
+ Navvy::Job.keep = -1.day
189
+ jobs = Navvy::Job.next
190
+ jobs.first.run
191
+ Navvy::Job.count.should == 0
192
+ end
193
+ end
194
+ end
195
+
196
+ describe 'when a job fails' do
197
+ before(:each) do
198
+ Navvy::Job.delete_all
199
+ Navvy::Job.enqueue(Cow, :broken)
200
+ end
201
+
202
+ it 'should store the exception and current time' do
203
+ jobs = Navvy::Job.next
204
+ jobs.first.run
205
+ Navvy::Job.count.should == 1
206
+ jobs.first.exception.should == 'this method is broken'
207
+ jobs.first.started_at.should be_instance_of Time
208
+ jobs.first.failed_at.should be_instance_of Time
209
+ end
210
+ end
211
+ end
212
+
213
+ describe '#completed' do
214
+ before(:each) do
215
+ Navvy::Job.delete_all
216
+ Navvy::Job.enqueue(Cow, :speak)
217
+ end
218
+
219
+ it 'should update the jobs completed_at date' do
220
+ jobs = Navvy::Job.next
221
+ jobs.first.completed
222
+ jobs.first.completed_at.should_not be_nil
223
+ end
224
+
225
+ it 'should set the return if provided' do
226
+ jobs = Navvy::Job.next
227
+ jobs.first.completed('woo!')
228
+ jobs.first.return.should == 'woo!'
229
+ end
230
+ end
231
+
232
+ describe '#failed' do
233
+ before(:each) do
234
+ Navvy::Job.delete_all
235
+ Navvy::Job.enqueue(Cow, :speak)
236
+ end
237
+
238
+ it 'should update the jobs failed_at date' do
239
+ jobs = Navvy::Job.next
240
+ jobs.first.failed
241
+ jobs.first.failed_at.should_not be_nil
242
+ end
243
+
244
+ it 'should set the exception message if provided' do
245
+ jobs = Navvy::Job.next
246
+ jobs.first.failed('broken')
247
+ jobs.first.exception.should == 'broken'
248
+ end
249
+ end
250
+
251
+ describe '#ran?' do
252
+ it 'should return false when failed_at? and completed_at? are false' do
253
+ job = Navvy::Job.create
254
+ job.ran?.should be_false
255
+ end
256
+
257
+ it 'should return true when failed_at? or completed_at? is true' do
258
+ [
259
+ Navvy::Job.create(:failed_at => Time.now),
260
+ Navvy::Job.create(:completed_at => Time.now)
261
+ ].each do |job|
262
+ job.ran?.should be_true
263
+ end
264
+ end
265
+ end
266
+
267
+ describe '#duration' do
268
+ it 'should return a duration if started_at and completed_at are set' do
269
+ job = Navvy::Job.create(
270
+ :started_at => 2.seconds.ago,
271
+ :completed_at => Time.now
272
+ )
273
+
274
+ job.duration.should >= 2.seconds
275
+ end
276
+
277
+ it 'should return a duration if started_at and failed_at are set' do
278
+ job = Navvy::Job.create(
279
+ :started_at => 3.seconds.ago,
280
+ :failed_at => Time.now
281
+ )
282
+
283
+ job.duration.should >= 3.seconds
284
+ end
285
+
286
+ it 'should return 0 if only started_at is set' do
287
+ job = Navvy::Job.create(
288
+ :started_at => 4.seconds.ago
289
+ )
290
+
291
+ job.duration.should == 0
292
+ end
293
+ end
294
+
295
+ describe '#args' do
296
+ it 'should return an array of arguments' do
297
+ job = Navvy::Job.enqueue(Cow, :speak, true, false)
298
+ job.args.should be_instance_of Array
299
+ job.args.count.should == 2
300
+ end
301
+ end
302
+
303
+ describe '#status' do
304
+ before(:each) do
305
+ Navvy::Job.delete_all
306
+ end
307
+
308
+ it 'should return :pending' do
309
+ job = Navvy::Job.enqueue(Cow, :speak)
310
+ job.status.should == :pending
311
+ end
312
+
313
+ it 'should return :completed' do
314
+ job = Navvy::Job.enqueue(Cow, :speak)
315
+ job.completed
316
+ job.status.should == :completed
317
+ end
318
+
319
+ it 'should return :failed' do
320
+ job = Navvy::Job.enqueue(Cow, :speak)
321
+ job.failed
322
+ job.status.should == :failed
323
+ end
324
+ end
325
+ end