navvy 0.0.0 → 0.1.0

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,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