navvy 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -1,12 +1,12 @@
1
1
  h1. Navvy
2
2
 
3
- Navvy is a simple background job processor inspired by "delayed_job":http://github.com/tobi/delayed_job, but aiming for database agnosticism. Currently Navvy supports ActiveRecord, MongoMapper and Sequel but it's extremely easy to write an adapter for your favorite ORM. Also, It completely supports Rails Edge.
3
+ Navvy is a simple Ruby background job processor inspired by "delayed_job":http://github.com/tobi/delayed_job, but aiming for database agnosticism. Currently Navvy supports ActiveRecord, MongoMapper, Sequel and DataMapper but it's extremely easy to write an adapter for your favorite ORM. Besides plain Ruby (1.8 & 1.9) it completely supports Rails Edge.
4
4
 
5
5
  ??“Navvy is a shorter form of navigator (UK) or navigational engineer (USA) and is particularly applied to describe the manual labourers working on major civil engineering projects. The term was coined in the late 18th century in Britain when numerous canals were being built, which were also sometimes known as "navigations". Canal navvies typically worked with shovels, pickaxes and barrows.”?? - "Wikipedia":http://en.wikipedia.org/wiki/Navvy
6
6
 
7
7
  h2. Using Navvy
8
8
 
9
- There's an "Installation Guide":http://wiki.github.com/jeffkreeftmeijer/navvy/installation (also for "Rails 3":http://wiki.github.com/jeffkreeftmeijer/navvy/installation-on-rails-edge) and a "Getting Started Guide":http://wiki.github.com/jeffkreeftmeijer/navvy/getting-started to put you on the right track. Any questions? Don't hesitate to "ask":http://github.com/inbox/new/jeffkreeftmeijer.
9
+ There are some "Installation Guide":http://wiki.github.com/jeffkreeftmeijer/navvy/installation (even for "Rails 3":http://wiki.github.com/jeffkreeftmeijer/navvy/installation-on-rails-edge) and a "Getting Started Guide":http://wiki.github.com/jeffkreeftmeijer/navvy/getting-started to put you on the right track. Any questions? Don't hesitate to "ask":http://github.com/inbox/new/jeffkreeftmeijer.
10
10
 
11
11
  h2. Contributing
12
12
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.2.0
@@ -4,8 +4,10 @@ class CreateJobs < ActiveRecord::Migration
4
4
  t.string :object
5
5
  t.string :method_name
6
6
  t.text :arguments
7
+ t.integer :priority, :default => 0
7
8
  t.string :return
8
9
  t.string :exception
10
+ t.integer :parent_id
9
11
  t.datetime :created_at
10
12
  t.datetime :run_at
11
13
  t.datetime :started_at
@@ -13,7 +15,7 @@ class CreateJobs < ActiveRecord::Migration
13
15
  t.datetime :failed_at
14
16
  end
15
17
  end
16
-
18
+
17
19
  def self.down
18
20
  drop_table :jobs
19
21
  end
@@ -0,0 +1,15 @@
1
+ module Navvy
2
+ class Configuration
3
+ attr_accessor :job_limit, :keep_jobs, :logger, :quiet, :sleep_time,
4
+ :max_attempts
5
+
6
+ def initialize
7
+ @job_limit = 100
8
+ @keep_jobs = false
9
+ @logger = nil
10
+ @quiet = false
11
+ @sleep_time = 5
12
+ @max_attempts = 25
13
+ end
14
+ end
15
+ end
@@ -4,17 +4,34 @@ require 'active_record'
4
4
  module Navvy
5
5
  class Job < ActiveRecord::Base
6
6
  class << self
7
- attr_writer :limit
8
- attr_accessor :keep
7
+ attr_writer :limit, :keep, :max_attempts
9
8
  end
10
9
 
11
10
  ##
12
- # Default limit of jobs to be fetched
11
+ # Default limit of jobs to be fetched.
13
12
  #
14
13
  # @return [Integer] limit
15
14
 
16
15
  def self.limit
17
- @limit || 100
16
+ @limit || Navvy.configuration.job_limit
17
+ end
18
+
19
+ ##
20
+ # If and how long the jobs should be kept.
21
+ #
22
+ # @return [Fixnum, true, false] keep
23
+
24
+ def self.keep
25
+ @keep || Navvy.configuration.keep_jobs
26
+ end
27
+
28
+ ##
29
+ # How often should a job be retried?
30
+ #
31
+ # @return [Fixnum] max_attempts
32
+
33
+ def self.max_attempts
34
+ @max_attempts || Navvy.configuration.max_attempts
18
35
  end
19
36
 
20
37
  ##
@@ -23,7 +40,7 @@ module Navvy
23
40
  # @return [true, false] keep
24
41
 
25
42
  def self.keep?
26
- keep = (@keep || false)
43
+ keep = (self.keep || false)
27
44
  return keep.from_now >= Time.now if keep.is_a? Fixnum
28
45
  keep
29
46
  end
@@ -38,11 +55,19 @@ module Navvy
38
55
  # @return [true, false]
39
56
 
40
57
  def self.enqueue(object, method_name, *args)
58
+ options = {}
59
+ if args.last.is_a?(Hash)
60
+ options = args.last.delete(:job_options) || {}
61
+ args.pop if args.last.empty?
62
+ end
63
+
41
64
  create(
42
65
  :object => object.to_s,
43
66
  :method_name => method_name.to_s,
44
67
  :arguments => args,
45
- :run_at => Time.now,
68
+ :priority => options[:priority] || 0,
69
+ :parent_id => options[:parent_id],
70
+ :run_at => options[:run_at] || Time.now,
46
71
  :created_at => Time.now
47
72
  )
48
73
  end
@@ -65,7 +90,7 @@ module Navvy
65
90
  Time.now
66
91
  ],
67
92
  :limit => limit,
68
- :order => 'created_at'
93
+ :order => 'priority desc, created_at'
69
94
  )
70
95
  end
71
96
 
@@ -135,7 +160,8 @@ module Navvy
135
160
 
136
161
  ##
137
162
  # Mark the job as failed. Will set failed_at to the current time and
138
- # optionally add the exception message if provided.
163
+ # optionally add the exception message if provided. Also, it will retry
164
+ # the job unless max_attempts has been reached.
139
165
  #
140
166
  # @param [String] exception the exception message you want to store.
141
167
  #
@@ -143,10 +169,44 @@ module Navvy
143
169
  # update_attributes call
144
170
 
145
171
  def failed(message = nil)
146
- update_attributes({
172
+ self.retry unless times_failed >= self.class.max_attempts
173
+ update_attributes(
147
174
  :failed_at => Time.now,
148
175
  :exception => message
149
- })
176
+ )
177
+ end
178
+
179
+ ##
180
+ # Retry the current job. Will add self to the queue again, giving the clone
181
+ # a parend_id equal to self.id.
182
+ #
183
+ # @return [true, false]
184
+
185
+ def retry
186
+ self.class.enqueue(
187
+ object,
188
+ method_name,
189
+ *(args << {
190
+ :job_options => {
191
+ :parent_id => parent_id || id,
192
+ :run_at => Time.now + times_failed ** 4,
193
+ :priority => priority
194
+ }
195
+ })
196
+ )
197
+ end
198
+
199
+ ##
200
+ # Check how many times the job has failed. Will try to find jobs with a
201
+ # parent_id that's the same as self.id and count them
202
+ #
203
+ # @return [Integer] count the amount of times the job has failed
204
+
205
+ def times_failed
206
+ i = parent_id || id
207
+ self.class.count(
208
+ :conditions => "(`id` = '#{i}' OR `parent_id` = '#{i}') AND `failed_at` IS NOT NULL"
209
+ )
150
210
  end
151
211
 
152
212
  ##
@@ -194,6 +254,17 @@ module Navvy
194
254
  arguments.is_a?(Array) ? arguments : YAML.load(arguments)
195
255
  end
196
256
 
257
+ ##
258
+ # Get the job status
259
+ #
260
+ # @return [:pending, :completed, :failed] status
261
+
262
+ def status
263
+ return :completed if completed?
264
+ return :failed if failed?
265
+ :pending
266
+ end
267
+
197
268
  alias_method :completed?, :completed_at?
198
269
  alias_method :failed?, :failed_at?
199
270
  end
@@ -4,10 +4,8 @@ require 'dm-core'
4
4
  module Navvy
5
5
  class Job
6
6
  include DataMapper::Resource
7
-
8
7
  class << self
9
- attr_writer :limit
10
- attr_accessor :keep
8
+ attr_writer :limit, :keep, :max_attempts
11
9
  end
12
10
 
13
11
  property :id, Serial
@@ -17,6 +15,7 @@ module Navvy
17
15
  property :priority, Integer, :default => 0
18
16
  property :return, String
19
17
  property :exception, String
18
+ property :parent_id, Integer
20
19
  property :created_at, Time
21
20
  property :run_at, Time
22
21
  property :started_at, Time
@@ -24,12 +23,30 @@ module Navvy
24
23
  property :failed_at, Time
25
24
 
26
25
  ##
27
- # Default limit of jobs to be fetched
26
+ # Default limit of jobs to be fetched.
28
27
  #
29
28
  # @return [Integer] limit
30
29
 
31
30
  def self.limit
32
- @limit || 100
31
+ @limit || Navvy.configuration.job_limit
32
+ end
33
+
34
+ ##
35
+ # If and how long the jobs should be kept.
36
+ #
37
+ # @return [Fixnum, true, false] keep
38
+
39
+ def self.keep
40
+ @keep || Navvy.configuration.keep_jobs
41
+ end
42
+
43
+ ##
44
+ # How often should a job be retried?
45
+ #
46
+ # @return [Fixnum] max_attempts
47
+
48
+ def self.max_attempts
49
+ @max_attempts || Navvy.configuration.max_attempts
33
50
  end
34
51
 
35
52
  ##
@@ -38,7 +55,7 @@ module Navvy
38
55
  # @return [true, false] keep
39
56
 
40
57
  def self.keep?
41
- keep = (@keep || false)
58
+ keep = (self.keep || false)
42
59
  return (Time.now + keep) >= Time.now if keep.is_a? Fixnum
43
60
  keep
44
61
  end
@@ -54,12 +71,20 @@ module Navvy
54
71
  # @return [true, false]
55
72
 
56
73
  def self.enqueue(object, method_name, *args)
74
+ options = {}
75
+ if args.last.is_a?(Hash)
76
+ options = args.last.delete(:job_options) || {}
77
+ args.pop if args.last.empty?
78
+ end
79
+
57
80
  new_job = self.new
58
81
  new_job.attributes = {
59
82
  :object => object.to_s,
60
83
  :method_name => method_name.to_s,
61
84
  :arguments => args.to_yaml,
62
- :run_at => Time.now,
85
+ :priority => options[:priority] || 0,
86
+ :parent_id => options[:parent_id],
87
+ :run_at => options[:run_at] || Time.now,
63
88
  :created_at => Time.now
64
89
  }
65
90
  new_job.save
@@ -81,13 +106,13 @@ module Navvy
81
106
  all(
82
107
  :failed_at => nil,
83
108
  :completed_at => nil,
84
- :run_at.lte => Time.now,
109
+ :run_at.lte => Time.now,
85
110
  :order => [ :priority.desc, :created_at.asc ],
86
111
  :limit => limit
87
112
  )
88
113
  end
89
114
 
90
- ##
115
+ ##
91
116
  # Clean up jobs that we don't need to keep anymore. If Navvy::Job.keep is
92
117
  # false it'll delete every completed job, if it's a timestamp it'll only
93
118
  # delete completed jobs that have passed their keeptime.
@@ -96,9 +121,9 @@ module Navvy
96
121
 
97
122
  def self.cleanup
98
123
  if keep.is_a? Fixnum
99
- all( :completed_at.lte => (Time.now - keep)).destroy
124
+ all(:completed_at.lte => (Time.now - keep)).destroy
100
125
  else
101
- all( :completed_at.not => nil ).destroy unless keep?
126
+ all(:completed_at.not => nil ).destroy unless keep?
102
127
  end
103
128
  end
104
129
 
@@ -149,6 +174,8 @@ module Navvy
149
174
  ##
150
175
  # Mark the job as failed. Will set failed_at to the current time and
151
176
  # optionally add the exception message if provided.
177
+ # optionally add the exception message if provided. Also, it will retry
178
+ # the job unless max_attempts has been reached.
152
179
  #
153
180
  # @param [String] exception the exception message you want to store.
154
181
  #
@@ -156,12 +183,46 @@ module Navvy
156
183
  # update_attributes call
157
184
 
158
185
  def failed(message = nil)
186
+ self.retry unless times_failed >= self.class.max_attempts
159
187
  update(
160
188
  :failed_at => Time.now,
161
189
  :exception => message
162
190
  )
163
191
  end
164
192
 
193
+ ##
194
+ # Retry the current job. Will add self to the queue again, giving the clone
195
+ # a parend_id equal to self.id.
196
+ #
197
+ # @return [true, false]
198
+
199
+ def retry
200
+ self.class.enqueue(
201
+ object,
202
+ method_name,
203
+ *(args << {
204
+ :job_options => {
205
+ :parent_id => parent_id || id,
206
+ :run_at => Time.now + times_failed ** 4,
207
+ :priority => priority
208
+ }
209
+ })
210
+ )
211
+ end
212
+
213
+ ##
214
+ # Check how many times the job has failed. Will try to find jobs with a
215
+ # parent_id that's the same as self.id and count them
216
+ #
217
+ # @return [Integer] count the amount of times the job has failed
218
+
219
+ def times_failed
220
+ i = parent_id || id
221
+ self.class.all(
222
+ :conditions => ["(`id` = ? OR `parent_id` = ?) AND `failed_at` IS NOT NULL", i, i]
223
+ ).count
224
+ end
225
+
165
226
  ##
166
227
  # Check if the job has been run.
167
228
  #
@@ -207,6 +268,17 @@ module Navvy
207
268
  arguments.is_a?(Array) ? arguments : YAML.load(arguments)
208
269
  end
209
270
 
271
+ ##
272
+ # Get the job status
273
+ #
274
+ # @return [:pending, :completed, :failed] status
275
+
276
+ def status
277
+ return :completed if completed?
278
+ return :failed if failed?
279
+ :pending
280
+ end
281
+
210
282
  alias_method :completed?, :completed_at?
211
283
  alias_method :failed?, :failed_at?
212
284
  end
@@ -5,15 +5,16 @@ module Navvy
5
5
  class Job
6
6
  include MongoMapper::Document
7
7
  class << self
8
- attr_writer :limit
9
- attr_accessor :keep
8
+ attr_writer :limit, :keep, :max_attempts
10
9
  end
11
10
 
12
11
  key :object, String
13
12
  key :method_name, Symbol
14
- key :arguments, Array
13
+ key :arguments, String
14
+ key :priority, Integer, :default => 0
15
15
  key :return, String
16
16
  key :exception, String
17
+ key :parent_id, ObjectId
17
18
  key :created_at, Time
18
19
  key :run_at, Time
19
20
  key :started_at, Time
@@ -21,12 +22,30 @@ module Navvy
21
22
  key :failed_at, Time
22
23
 
23
24
  ##
24
- # Default limit of jobs to be fetched
25
+ # Default limit of jobs to be fetched.
25
26
  #
26
27
  # @return [Integer] limit
27
28
 
28
29
  def self.limit
29
- @limit || 100
30
+ @limit || Navvy.configuration.job_limit
31
+ end
32
+
33
+ ##
34
+ # If and how long the jobs should be kept.
35
+ #
36
+ # @return [Fixnum, true, false] keep
37
+
38
+ def self.keep
39
+ @keep || Navvy.configuration.keep_jobs
40
+ end
41
+
42
+ ##
43
+ # How often should a job be retried?
44
+ #
45
+ # @return [Fixnum] max_attempts
46
+
47
+ def self.max_attempts
48
+ @max_attempts || Navvy.configuration.max_attempts
30
49
  end
31
50
 
32
51
  ##
@@ -35,7 +54,7 @@ module Navvy
35
54
  # @return [true, false] keep
36
55
 
37
56
  def self.keep?
38
- keep = (@keep || false)
57
+ keep = (self.keep || false)
39
58
  return keep.from_now >= Time.now if keep.is_a? Fixnum
40
59
  keep
41
60
  end
@@ -51,11 +70,19 @@ module Navvy
51
70
  # @return [true, false]
52
71
 
53
72
  def self.enqueue(object, method_name, *args)
73
+ options = {}
74
+ if args.last.is_a?(Hash)
75
+ options = args.last.delete(:job_options) || {}
76
+ args.pop if args.last.empty?
77
+ end
78
+
54
79
  create(
55
80
  :object => object.to_s,
56
81
  :method_name => method_name.to_sym,
57
- :arguments => args,
58
- :run_at => Time.now,
82
+ :arguments => args.to_yaml,
83
+ :priority => options[:priority] || 0,
84
+ :parent_id => options[:parent_id],
85
+ :run_at => options[:run_at] || Time.now,
59
86
  :created_at => Time.now
60
87
  )
61
88
  end
@@ -76,8 +103,8 @@ module Navvy
76
103
  :failed_at => nil,
77
104
  :completed_at => nil,
78
105
  :run_at => {'$lte' => Time.now},
79
- :limit => limit,
80
- :order => 'created_at'
106
+ :order => 'priority desc, created_at asc',
107
+ :limit => limit
81
108
  )
82
109
  end
83
110
 
@@ -146,7 +173,8 @@ module Navvy
146
173
 
147
174
  ##
148
175
  # Mark the job as failed. Will set failed_at to the current time and
149
- # optionally add the exception message if provided.
176
+ # optionally add the exception message if provided. Also, it will retry
177
+ # the job unless max_attempts has been reached.
150
178
  #
151
179
  # @param [String] exception the exception message you want to store.
152
180
  #
@@ -154,10 +182,45 @@ module Navvy
154
182
  # update_attributes call
155
183
 
156
184
  def failed(message = nil)
157
- update_attributes({
185
+ self.retry unless times_failed >= self.class.max_attempts
186
+ update_attributes(
158
187
  :failed_at => Time.now,
159
188
  :exception => message
160
- })
189
+ )
190
+ end
191
+
192
+ ##
193
+ # Retry the current job. Will add self to the queue again, giving the clone
194
+ # a parend_id equal to self.id.
195
+ #
196
+ # @return [true, false]
197
+
198
+ def retry
199
+ self.class.enqueue(
200
+ object,
201
+ method_name,
202
+ *(args << {
203
+ :job_options => {
204
+ :parent_id => parent_id || id,
205
+ :run_at => Time.now + times_failed ** 4,
206
+ :priority => priority
207
+ }
208
+ })
209
+ )
210
+ end
211
+
212
+ ##
213
+ # Check how many times the job has failed. Will try to find jobs with a
214
+ # parent_id that's the same as self.id and count them
215
+ #
216
+ # @return [Integer] count the amount of times the job has failed
217
+
218
+ def times_failed
219
+ i = parent_id || id
220
+ self.class.count(
221
+ :failed_at => {'$ne' => nil},
222
+ '$where' => "this._id == '#{i}' || this.parent_id == '#{i}'"
223
+ )
161
224
  end
162
225
 
163
226
  ##
@@ -187,6 +250,17 @@ module Navvy
187
250
  arguments.is_a?(Array) ? arguments : YAML.load(arguments)
188
251
  end
189
252
 
253
+ ##
254
+ # Get the job status
255
+ #
256
+ # @return [:pending, :completed, :failed] status
257
+
258
+ def status
259
+ return :completed if completed?
260
+ return :failed if failed?
261
+ :pending
262
+ end
263
+
190
264
  alias_method :completed?, :completed_at?
191
265
  alias_method :failed?, :failed_at?
192
266
  end
@@ -5,17 +5,34 @@ require 'yaml'
5
5
  module Navvy
6
6
  class Job < Sequel::Model
7
7
  class << self
8
- attr_writer :limit
9
- attr_accessor :keep
8
+ attr_writer :limit, :keep, :max_attempts
10
9
  end
11
10
 
12
11
  ##
13
- # Default limit of jobs to be fetched
12
+ # Default limit of jobs to be fetched.
14
13
  #
15
14
  # @return [Integer] limit
16
15
 
17
16
  def self.limit
18
- @limit || 100
17
+ @limit || Navvy.configuration.job_limit
18
+ end
19
+
20
+ ##
21
+ # If and how long the jobs should be kept.
22
+ #
23
+ # @return [Fixnum, true, false] keep
24
+
25
+ def self.keep
26
+ @keep || Navvy.configuration.keep_jobs
27
+ end
28
+
29
+ ##
30
+ # How often should a job be retried?
31
+ #
32
+ # @return [Fixnum] max_attempts
33
+
34
+ def self.max_attempts
35
+ @max_attempts || Navvy.configuration.max_attempts
19
36
  end
20
37
 
21
38
  ##
@@ -24,7 +41,7 @@ module Navvy
24
41
  # @return [true, false] keep
25
42
 
26
43
  def self.keep?
27
- keep = (@keep || false)
44
+ keep = (self.keep || false)
28
45
  return (Time.now + keep) >= Time.now if keep.is_a? Fixnum
29
46
  keep
30
47
  end
@@ -40,11 +57,19 @@ module Navvy
40
57
  # @return [true, false]
41
58
 
42
59
  def self.enqueue(object, method_name, *args)
60
+ options = {}
61
+ if args.last.is_a?(Hash)
62
+ options = args.last.delete(:job_options) || {}
63
+ args.pop if args.last.empty?
64
+ end
65
+
43
66
  create(
44
67
  :object => object.to_s,
45
68
  :method_name => method_name.to_s,
46
69
  :arguments => args.to_yaml,
47
- :run_at => Time.now,
70
+ :priority => options[:priority] || 0,
71
+ :parent_id => options[:parent_id],
72
+ :run_at => options[:run_at] || Time.now,
48
73
  :created_at => Time.now
49
74
  )
50
75
  end
@@ -64,7 +89,7 @@ module Navvy
64
89
  filter(
65
90
  '`failed_at` IS NULL AND `completed_at` IS NULL AND `run_at` <= ?',
66
91
  Time.now
67
- ).order(:created_at).first(limit)
92
+ ).order(:priority.desc, :created_at).first(limit)
68
93
  end
69
94
 
70
95
  ##
@@ -124,7 +149,8 @@ module Navvy
124
149
 
125
150
  ##
126
151
  # Mark the job as failed. Will set failed_at to the current time and
127
- # optionally add the exception message if provided.
152
+ # optionally add the exception message if provided. Also, it will retry
153
+ # the job unless max_attempts has been reached.
128
154
  #
129
155
  # @param [String] exception the exception message you want to store.
130
156
  #
@@ -132,10 +158,44 @@ module Navvy
132
158
  # update_attributes call
133
159
 
134
160
  def failed(message = nil)
135
- update({
161
+ self.retry unless times_failed >= self.class.max_attempts
162
+ update(
136
163
  :failed_at => Time.now,
137
164
  :exception => message
138
- })
165
+ )
166
+ end
167
+
168
+ ##
169
+ # Retry the current job. Will add self to the queue again, giving the clone
170
+ # a parend_id equal to self.id.
171
+ #
172
+ # @return [true, false]
173
+
174
+ def retry
175
+ self.class.enqueue(
176
+ object,
177
+ method_name,
178
+ *(args << {
179
+ :job_options => {
180
+ :parent_id => parent_id || id,
181
+ :run_at => Time.now + times_failed ** 4,
182
+ :priority => priority
183
+ }
184
+ })
185
+ )
186
+ end
187
+
188
+ ##
189
+ # Check how many times the job has failed. Will try to find jobs with a
190
+ # parent_id that's the same as self.id and count them
191
+ #
192
+ # @return [Integer] count the amount of times the job has failed
193
+
194
+ def times_failed
195
+ i = parent_id || id
196
+ self.class.filter(
197
+ "(`id` == '#{i}' OR `parent_id` == '#{i}') AND `failed_at` IS NOT NULL"
198
+ ).count
139
199
  end
140
200
 
141
201
  ##
@@ -183,6 +243,17 @@ module Navvy
183
243
  arguments.is_a?(Array) ? arguments : YAML.load(arguments)
184
244
  end
185
245
 
246
+ ##
247
+ # Get the job status
248
+ #
249
+ # @return [:pending, :completed, :failed] status
250
+
251
+ def status
252
+ return :completed if completed?
253
+ return :failed if failed?
254
+ :pending
255
+ end
256
+
186
257
  alias_method :completed?, :completed_at?
187
258
  alias_method :failed?, :failed_at?
188
259
  end
data/lib/navvy/log.rb CHANGED
@@ -7,8 +7,22 @@ module Navvy
7
7
 
8
8
  class LoggerNotFound < StandardError; end
9
9
 
10
+ ##
11
+ # Default logger
12
+ #
13
+ # @return [Symbol, nil] logger
14
+
10
15
  def self.logger
11
- @logger
16
+ @logger || Navvy.configuration.logger
17
+ end
18
+
19
+ ##
20
+ # Be quiet?
21
+ #
22
+ # @return [true, false] quiet
23
+
24
+ def self.quiet
25
+ @quiet || Navvy.configuration.quiet
12
26
  end
13
27
 
14
28
  ##
data/lib/navvy/worker.rb CHANGED
@@ -1,6 +1,18 @@
1
1
  module Navvy
2
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
3
11
 
12
+ def self.sleep_time
13
+ @sleep_time || Navvy.configuration.sleep_time
14
+ end
15
+
4
16
  ##
5
17
  # Start the worker.
6
18
 
@@ -17,7 +29,7 @@ module Navvy
17
29
  Navvy::Job.cleanup
18
30
  break
19
31
  end
20
- sleep 5
32
+ sleep sleep_time
21
33
  end
22
34
  end
23
35
 
data/lib/navvy.rb CHANGED
@@ -1,2 +1,17 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/navvy/worker')
2
2
  require File.expand_path(File.dirname(__FILE__) + '/navvy/log')
3
+ require File.expand_path(File.dirname(__FILE__) + '/navvy/configuration')
4
+
5
+ module Navvy
6
+ class << self
7
+ attr_writer :configuration
8
+ end
9
+
10
+ def self.configuration
11
+ @configuration ||= Configuration.new
12
+ end
13
+
14
+ def self.configure
15
+ yield(self.configuration)
16
+ end
17
+ 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.1.2"
8
+ s.version = "0.2.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-26}
12
+ s.date = %q{2010-02-08}
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 = [
@@ -27,6 +27,7 @@ Gem::Specification.new do |s|
27
27
  "generators/navvy/templates/migration.rb",
28
28
  "lib/generators/navvy_generator.rb",
29
29
  "lib/navvy.rb",
30
+ "lib/navvy/configuration.rb",
30
31
  "lib/navvy/job/active_record.rb",
31
32
  "lib/navvy/job/data_mapper.rb",
32
33
  "lib/navvy/job/mongo_mapper.rb",
@@ -35,6 +36,7 @@ Gem::Specification.new do |s|
35
36
  "lib/navvy/tasks.rb",
36
37
  "lib/navvy/worker.rb",
37
38
  "navvy.gemspec",
39
+ "spec/configuration_spec.rb",
38
40
  "spec/job_spec.rb",
39
41
  "spec/log_spec.rb",
40
42
  "spec/setup/active_record.rb",
@@ -52,7 +54,8 @@ Gem::Specification.new do |s|
52
54
  s.rubygems_version = %q{1.3.5}
53
55
  s.summary = %q{Simple background job processor inspired by delayed_job, but aiming for database agnosticism.}
54
56
  s.test_files = [
55
- "spec/job_spec.rb",
57
+ "spec/configuration_spec.rb",
58
+ "spec/job_spec.rb",
56
59
  "spec/log_spec.rb",
57
60
  "spec/setup/active_record.rb",
58
61
  "spec/setup/data_mapper.rb",
@@ -0,0 +1,85 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/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 = nil
9
+ config.quiet = true
10
+ config.sleep_time = 5
11
+ end
12
+ end
13
+
14
+ it 'should have a job limit of 100 by default' do
15
+ Navvy::Job.limit.should == 100
16
+ end
17
+
18
+ it 'should set the job limit' do
19
+ Navvy.configure do |config|
20
+ config.job_limit = 10
21
+ end
22
+
23
+ Navvy::Job.limit.should == 10
24
+ end
25
+
26
+ it 'should have keep_jobs off by default' do
27
+ Navvy::Job.keep.should == false
28
+ end
29
+
30
+ it 'should set keep_jobs' do
31
+ Navvy.configure do |config|
32
+ config.keep_jobs = 10
33
+ end
34
+
35
+ Navvy::Job.keep.should == 10
36
+ end
37
+
38
+ it 'should not have a logger by default' do
39
+ Navvy::Log.logger.should == nil
40
+ end
41
+
42
+ it 'should set the keep_jobs' do
43
+ Navvy.configure do |config|
44
+ config.logger = :rails
45
+ end
46
+
47
+ Navvy::Log.logger.should == :rails
48
+ end
49
+
50
+ it 'should be quiet in the specs' do
51
+ Navvy::Log.quiet.should == true
52
+ end
53
+
54
+ it 'should turn quiet off' do
55
+ Navvy.configure do |config|
56
+ config.quiet = false
57
+ end
58
+
59
+ Navvy::Log.quiet.should == false
60
+ end
61
+
62
+ it 'should have a default sleep time of 5' do
63
+ Navvy::Worker.sleep_time.should == 5
64
+ end
65
+
66
+ it 'should turn quiet off' do
67
+ Navvy.configure do |config|
68
+ config.sleep_time = 10
69
+ end
70
+
71
+ Navvy::Worker.sleep_time.should == 10
72
+ end
73
+
74
+ it 'should have a default max_attempts of 25' do
75
+ Navvy::Job.max_attempts.should == 25
76
+ end
77
+
78
+ it 'should set max_attempts to 15' do
79
+ Navvy.configure do |config|
80
+ config.max_attempts = 15
81
+ end
82
+
83
+ Navvy::Job.max_attempts.should == 15
84
+ end
85
+ end
data/spec/job_spec.rb CHANGED
@@ -1,6 +1,44 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe 'Navvy::Job' do
4
+
5
+ describe '.keep?' do
6
+ after(:each) do
7
+ Navvy::Job.keep = false
8
+ Navvy.configure do |config|
9
+ config.keep_jobs = false
10
+ end
11
+ end
12
+
13
+ describe 'when configured using Navvy::Job.keep=' do
14
+ it 'should return false' do
15
+ Navvy::Job.keep = false
16
+ Navvy::Job.keep?.should == false
17
+ end
18
+
19
+ it 'should return true' do
20
+ Navvy::Job.keep = true
21
+ Navvy::Job.keep?.should == true
22
+ end
23
+ end
24
+
25
+ describe 'when configured with Navvy.configure' do
26
+ it 'should return false' do
27
+ Navvy.configure do |config|
28
+ config.keep_jobs = false
29
+ end
30
+ Navvy::Job.keep?.should == false
31
+ end
32
+
33
+ it 'should return true' do
34
+ Navvy.configure do |config|
35
+ config.keep_jobs = true
36
+ end
37
+ Navvy::Job.keep?.should == true
38
+ end
39
+ end
40
+ end
41
+
4
42
  describe '.enqueue' do
5
43
  before(:each) do
6
44
  delete_all_jobs
@@ -197,7 +235,6 @@ describe 'Navvy::Job' do
197
235
  it 'should store the exception and current time' do
198
236
  jobs = Navvy::Job.next
199
237
  jobs.first.run
200
- job_count.should == 1
201
238
  jobs.first.exception.should == 'this method is broken'
202
239
  jobs.first.started_at.should be_instance_of Time
203
240
  jobs.first.failed_at.should be_instance_of Time
@@ -241,6 +278,137 @@ describe 'Navvy::Job' do
241
278
  jobs.first.failed('broken')
242
279
  jobs.first.exception.should == 'broken'
243
280
  end
281
+
282
+ it 'should retry' do
283
+ jobs = Navvy::Job.next
284
+ jobs.first.should_receive(:retry)
285
+ jobs.first.failed('broken')
286
+ end
287
+
288
+ it 'should not retry when the job has failed 25 times already' do
289
+ jobs = Navvy::Job.next
290
+ jobs.first.stub!(:times_failed).and_return 25
291
+ jobs.first.should_not_receive(:retry)
292
+ jobs.first.failed('broken')
293
+ end
294
+
295
+ it 'should not retry when the job has failed 10 times' do
296
+ Navvy::Job.max_attempts = 10
297
+ jobs = Navvy::Job.next
298
+ jobs.first.stub!(:times_failed).and_return 10
299
+ jobs.first.should_not_receive(:retry)
300
+ jobs.first.failed('broken')
301
+ end
302
+ end
303
+
304
+ describe '#retry' do
305
+ before(:each) do
306
+ delete_all_jobs
307
+ end
308
+
309
+ it 'should enqueue a child for the failed job' do
310
+ failed_job = Navvy::Job.enqueue(Cow, :speak, true, false)
311
+ job = failed_job.retry
312
+ job.object.should == 'Cow'
313
+ job.method_name.to_s.should == 'speak'
314
+ job.args.should == [true, false]
315
+ job.parent_id.should == failed_job.id
316
+ end
317
+
318
+ it 'should handle hashes correctly' do
319
+ failed_job = Navvy::Job.enqueue(Cow, :speak, 'name' => 'Betsy')
320
+ job = failed_job.retry
321
+ job.args.should == [{'name' => 'Betsy'}]
322
+ job.parent_id.should == failed_job.id
323
+ end
324
+
325
+ it 'should set the priority' do
326
+ failed_job = Navvy::Job.enqueue(
327
+ Cow,
328
+ :speak,
329
+ 'name' => 'Betsy',
330
+ :job_options => {
331
+ :priority => 2
332
+ }
333
+ )
334
+ job = failed_job.retry
335
+ job.priority.should == 2
336
+ end
337
+
338
+ it 'should set the run_at date to about 16 seconds from now' do
339
+ failed_job = Navvy::Job.enqueue(Cow, :speak, 'name' => 'Betsy')
340
+ failed_job.stub!(:times_failed).and_return 2
341
+ now = Time.now
342
+ job = failed_job.retry
343
+ job.run_at.to_i.should == (now + 16).to_i
344
+ end
345
+
346
+ it 'should set the run_at date to about 256 seconds from now' do
347
+ failed_job = Navvy::Job.enqueue(Cow, :speak, 'name' => 'Betsy')
348
+ failed_job.stub!(:times_failed).and_return 4
349
+ now = Time.now
350
+ job = failed_job.retry
351
+ job.run_at.to_i.should == (now + 256).to_i
352
+ end
353
+
354
+ it 'should set the run_at date to about 4096 seconds from now' do
355
+ failed_job = Navvy::Job.enqueue(Cow, :speak, 'name' => 'Betsy')
356
+ failed_job.stub!(:times_failed).and_return 8
357
+ now = Time.now
358
+ job = failed_job.retry
359
+ job.run_at.to_i.should == (now + 4096).to_i
360
+ end
361
+
362
+ it 'should set the parent_id to the master job id' do
363
+ failed_job = Navvy::Job.enqueue(Cow, :speak, 'name' => 'Betsy')
364
+ failed_child = failed_job.retry
365
+ failed_child.retry.parent_id.should == failed_job.id
366
+ end
367
+ end
368
+
369
+ describe '#times_failed' do
370
+ before(:each) do
371
+ delete_all_jobs
372
+ @failed_job = Navvy::Job.create(
373
+ :failed_at => Time.now
374
+ )
375
+ end
376
+
377
+ it 'should return 1' do
378
+ @failed_job.times_failed.should == 1
379
+ end
380
+
381
+ it 'should return 3 when having 2 failed children' do
382
+ 2.times do
383
+ Navvy::Job.create(
384
+ :failed_at => Time.now,
385
+ :parent_id => @failed_job.id
386
+ )
387
+ end
388
+
389
+ @failed_job.times_failed.should == 3
390
+ end
391
+
392
+ it 'should return 2 when having 1 failed and one pending child' do
393
+ Navvy::Job.create(
394
+ :failed_at => Time.now,
395
+ :parent_id => @failed_job.id
396
+ )
397
+
398
+ Navvy::Job.create(
399
+ :parent_id => @failed_job.id
400
+ )
401
+
402
+ @failed_job.times_failed.should == 2
403
+ end
404
+
405
+ it 'should return 2 when having failed and having a failed parent' do
406
+ failed_child = Navvy::Job.create(
407
+ :failed_at => Time.now,
408
+ :parent_id => @failed_job.id
409
+ )
410
+ failed_child.times_failed.should == 2
411
+ end
244
412
  end
245
413
 
246
414
  describe '#ran?' do
@@ -8,8 +8,10 @@ ActiveRecord::Schema.define do
8
8
  table.string :object
9
9
  table.string :method_name
10
10
  table.text :arguments
11
+ table.integer :priority, :default => 0
11
12
  table.string :return
12
13
  table.string :exception
14
+ table.integer :parent_id
13
15
  table.datetime :created_at
14
16
  table.datetime :run_at
15
17
  table.datetime :started_at
data/spec/setup/sequel.rb CHANGED
@@ -8,8 +8,10 @@ Sequel::DATABASES[0].create_table!(:jobs) do
8
8
  String :object
9
9
  String :method_name
10
10
  String :arguments, :text => true
11
+ Integer :priority, :default => 0
11
12
  String :return
12
13
  String :exception
14
+ Integer :parent_id
13
15
  DateTime :created_at
14
16
  DateTime :run_at
15
17
  DateTime :started_at
data/spec/spec_helper.rb CHANGED
@@ -39,4 +39,6 @@ class Cow
39
39
  end
40
40
  end
41
41
 
42
- Navvy::Log.quiet = true
42
+ Navvy.configure do |config|
43
+ config.quiet = true
44
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: navvy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeff Kreeftmeijer
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-26 00:00:00 +01:00
12
+ date: 2010-02-08 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -82,6 +82,7 @@ files:
82
82
  - generators/navvy/templates/migration.rb
83
83
  - lib/generators/navvy_generator.rb
84
84
  - lib/navvy.rb
85
+ - lib/navvy/configuration.rb
85
86
  - lib/navvy/job/active_record.rb
86
87
  - lib/navvy/job/data_mapper.rb
87
88
  - lib/navvy/job/mongo_mapper.rb
@@ -90,6 +91,7 @@ files:
90
91
  - lib/navvy/tasks.rb
91
92
  - lib/navvy/worker.rb
92
93
  - navvy.gemspec
94
+ - spec/configuration_spec.rb
93
95
  - spec/job_spec.rb
94
96
  - spec/log_spec.rb
95
97
  - spec/setup/active_record.rb
@@ -129,6 +131,7 @@ signing_key:
129
131
  specification_version: 3
130
132
  summary: Simple background job processor inspired by delayed_job, but aiming for database agnosticism.
131
133
  test_files:
134
+ - spec/configuration_spec.rb
132
135
  - spec/job_spec.rb
133
136
  - spec/log_spec.rb
134
137
  - spec/setup/active_record.rb