navvy 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -1,9 +1,17 @@
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.
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.
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
+ h2. Using Navvy
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.
10
+
11
+ h2. Contributing
12
+
13
+ Found any issues? Have a great idea? Want to help? Great! Create an issue "issue":http://github.com/jeffkreeftmeijer/navvy/issues for it, or even better; "fork the project":http://github.com/jeffkreeftmeijer/navvy/fork. Pull requests are always welcome. :)
14
+
7
15
  h2. License
8
16
 
9
17
  Copyright (c) 2009 Jeff Kreeftmeijer, released under the MIT license
data/Rakefile CHANGED
@@ -10,12 +10,13 @@ begin
10
10
  gem.email = "jeff@kreeftmeijer.nl"
11
11
  gem.homepage = "http://github.com/jeffkreeftmeijer/navvy"
12
12
  gem.authors = ["Jeff Kreeftmeijer"]
13
- gem.add_dependency "mongo_mapper", ">= 0.6.10"
14
13
  gem.add_development_dependency "rspec", ">= 1.2.9"
15
14
  gem.add_development_dependency "yard", ">= 0.5.2"
16
15
  gem.add_development_dependency "metric_fu", ">= 1.1.6"
17
16
  gem.add_development_dependency "machinist", ">= 1.0.6"
17
+ gem.add_development_dependency "mongo_mapper", ">= 0.6.10"
18
18
  gem.add_development_dependency "machinist_mongomapper", ">= 0.9.7"
19
+ gem.add_development_dependency "sequel", ">= 3.8.0"
19
20
  end
20
21
  Jeweler::GemcutterTasks.new
21
22
  rescue LoadError
@@ -23,9 +24,25 @@ rescue LoadError
23
24
  end
24
25
 
25
26
  require 'spec/rake/spectask'
26
- Spec::Rake::SpecTask.new(:spec) do |spec|
27
- spec.libs << 'lib' << 'spec'
28
- spec.spec_files = FileList['spec/**/*_spec.rb']
27
+
28
+ task :spec do
29
+ ['spec:active_record', 'spec:mongo_mapper', 'spec:sequel'].each do |spec|
30
+ Rake::Task[spec].invoke
31
+ end
32
+ end
33
+
34
+ namespace :spec do
35
+ Spec::Rake::SpecTask.new(:active_record) do |spec|
36
+ spec.spec_files = FileList['spec/job/active_record_spec.rb', 'spec/*_spec.rb']
37
+ end
38
+
39
+ Spec::Rake::SpecTask.new(:mongo_mapper) do |spec|
40
+ spec.spec_files = FileList['spec/job/mongo_mapper_spec.rb', 'spec/*_spec.rb']
41
+ end
42
+
43
+ Spec::Rake::SpecTask.new(:sequel) do |spec|
44
+ spec.spec_files = FileList['spec/job/sequel_spec.rb', 'spec/*_spec.rb']
45
+ end
29
46
  end
30
47
 
31
48
  Spec::Rake::SpecTask.new(:rcov) do |spec|
@@ -35,7 +52,6 @@ Spec::Rake::SpecTask.new(:rcov) do |spec|
35
52
  end
36
53
 
37
54
  task :spec => :check_dependencies
38
-
39
55
  task :default => :spec
40
56
 
41
57
  begin
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.1.0
@@ -0,0 +1,15 @@
1
+ class NavvyGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ options = {
5
+ :migration_file_name => 'create_jobs'
6
+ }
7
+
8
+ m.migration_template 'migration.rb', 'db/migrate', options
9
+ end
10
+ end
11
+
12
+ def banner
13
+ "Usage: #{$0} #{spec.name}"
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ class CreateJobs < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :jobs, :force => true do |t|
4
+ t.string :object
5
+ t.string :method_name
6
+ t.text :arguments
7
+ t.string :return
8
+ t.string :exception
9
+ t.datetime :created_at
10
+ t.datetime :run_at
11
+ t.datetime :started_at
12
+ t.datetime :completed_at
13
+ t.datetime :failed_at
14
+ end
15
+ end
16
+
17
+ def self.down
18
+ drop_table :jobs
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require 'rails/generators/migration'
2
+ class NavvyGenerator < Rails::Generators::Base
3
+ include Rails::Generators::Migration
4
+
5
+ def self.source_root
6
+ File.join(File.dirname(__FILE__), '..', '..', 'generators', 'navvy', 'templates')
7
+ end
8
+
9
+ def install_navvy
10
+ migration_template(
11
+ 'migration.rb',
12
+ 'db/migrate/create_navvy_table.rb'
13
+ )
14
+ end
15
+
16
+ protected
17
+ def next_migration_number(dirname) #:nodoc:
18
+ "%.3d" % (current_migration_number(dirname) + 1)
19
+ end
20
+ end
data/lib/navvy.rb CHANGED
@@ -1,2 +1,2 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/navvy/job')
2
- require File.expand_path(File.dirname(__FILE__) + '/navvy/worker')
1
+ require File.expand_path(File.dirname(__FILE__) + '/navvy/worker')
2
+ require File.expand_path(File.dirname(__FILE__) + '/navvy/log')
@@ -0,0 +1,212 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+ require 'yaml'
4
+
5
+ module Navvy
6
+ class Job < ActiveRecord::Base
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 keep.from_now >= 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 run
37
+ # @param [*] arguments optional arguments you want to pass to the method
38
+ #
39
+ # @return [true, false]
40
+
41
+ def self.enqueue(object, method_name, *args)
42
+ create(
43
+ :object => object.to_s,
44
+ :method_name => method_name.to_s,
45
+ :arguments => args,
46
+ :run_at => Time.now,
47
+ :created_at => Time.now
48
+ )
49
+ end
50
+
51
+ ##
52
+ # Find the next available jobs in the queue. This will not include failed
53
+ # jobs (where :failed_at is not nil) and jobs that should run in the future
54
+ # (where :run_at is greater than the current time).
55
+ #
56
+ # @param [Integer] limit the limit of jobs to be fetched. Defaults to
57
+ # Navvy::Job.limit
58
+ #
59
+ # @return [array, nil] the next available jobs in an array or nil if no
60
+ # jobs were found.
61
+
62
+ def self.next(limit = self.limit)
63
+ all(
64
+ :conditions => [
65
+ '`failed_at` IS NULL AND `completed_at` IS NULL AND `run_at` <= ?',
66
+ Time.now
67
+ ],
68
+ :limit => limit,
69
+ :order => 'created_at'
70
+ )
71
+ end
72
+
73
+ ##
74
+ # Clean up jobs that we don't need to keep anymore. If Navvy::Job.keep is
75
+ # false it'll delete every completed job, if it's a timestamp it'll only
76
+ # delete completed jobs that have passed their keeptime.
77
+ #
78
+ # @return [true, false] delete_all the result of the delete_all call
79
+
80
+ def self.cleanup
81
+ if keep.is_a? Fixnum
82
+ delete_all([
83
+ '`completed_at` <= ?',
84
+ keep.ago
85
+ ])
86
+ else
87
+ delete_all(
88
+ '`completed_at` IS NOT NULL'
89
+ ) unless keep?
90
+ end
91
+ end
92
+
93
+ ##
94
+ # Run the job. Will delete the Navvy::Job record and return its return
95
+ # value if it runs successfully unless Navvy::Job.keep is set. If a job
96
+ # fails, it'll update the Navvy::Job record to include the exception
97
+ # message it sent back and set the :failed_at date. Failed jobs never get
98
+ # deleted.
99
+ #
100
+ # @example
101
+ # job = Navvy::Job.next # finds the next available job in the queue
102
+ # job.run # runs the job and returns the job's return value
103
+ #
104
+ # @return [String] return value of the called method.
105
+
106
+ def run
107
+ begin
108
+ update_attributes(:started_at => Time.now)
109
+ if args.empty?
110
+ result = object.constantize.send(method_name)
111
+ else
112
+ result = object.constantize.send(method_name, *args)
113
+ end
114
+ Navvy::Job.keep? ? completed : destroy
115
+ result
116
+ rescue Exception => exception
117
+ failed(exception.message)
118
+ end
119
+ end
120
+
121
+ ##
122
+ # Mark the job as completed. Will set completed_at to the current time and
123
+ # optionally add the return value if provided.
124
+ #
125
+ # @param [String] return_value the return value you want to store.
126
+ #
127
+ # @return [true, false] update_attributes the result of the
128
+ # update_attributes call
129
+
130
+ def completed(return_value = nil)
131
+ update_attributes({
132
+ :completed_at => Time.now,
133
+ :return => return_value
134
+ })
135
+ end
136
+
137
+ ##
138
+ # Mark the job as failed. Will set failed_at to the current time and
139
+ # optionally add the exception message if provided.
140
+ #
141
+ # @param [String] exception the exception message you want to store.
142
+ #
143
+ # @return [true, false] update_attributes the result of the
144
+ # update_attributes call
145
+
146
+ def failed(message = nil)
147
+ update_attributes({
148
+ :failed_at => Time.now,
149
+ :exception => message
150
+ })
151
+ end
152
+
153
+ ##
154
+ # Check if the job has been run.
155
+ #
156
+ # @return [true, false] ran
157
+
158
+ def ran?
159
+ completed? || failed?
160
+ end
161
+
162
+ ##
163
+ # Check how long it took for a job to complete or fail
164
+ #
165
+ # @return [Time, Integer] time the time it took
166
+
167
+ def duration
168
+ ran? ? (completed_at || failed_at) - started_at : 0
169
+ end
170
+
171
+ ##
172
+ # Check if completed_at is set
173
+ #
174
+ # @return [true, false] set?
175
+
176
+ def completed_at?
177
+ !completed_at.nil?
178
+ end
179
+
180
+ ##
181
+ # Check if failed_at is set
182
+ #
183
+ # @return [true, false] set?
184
+
185
+ def failed_at?
186
+ !failed_at.nil?
187
+ end
188
+
189
+ ##
190
+ # Get the job arguments as an array
191
+ #
192
+ # @return [array] arguments
193
+
194
+ def args
195
+ arguments.is_a?(Array) ? arguments : YAML.load(arguments)
196
+ end
197
+
198
+ ##
199
+ # Get the job status
200
+ #
201
+ # @return [:pending, :completed, :failed] status
202
+
203
+ def status
204
+ return :completed if completed?
205
+ return :failed if failed?
206
+ :pending
207
+ end
208
+
209
+ alias_method :completed?, :completed_at?
210
+ alias_method :failed?, :failed_at?
211
+ end
212
+ end
@@ -4,29 +4,59 @@ require 'mongo_mapper'
4
4
  module Navvy
5
5
  class Job
6
6
  include MongoMapper::Document
7
+ class << self
8
+ attr_writer :limit
9
+ attr_accessor :keep
10
+ end
7
11
 
8
- key :object, String
9
- key :method, Symbol
10
- key :arguments, Array
11
- key :exception, String
12
- key :run_at, Time
13
- key :failed_at, Time
12
+ key :object, String
13
+ key :method_name, Symbol
14
+ key :arguments, Array
15
+ key :return, String
16
+ key :exception, String
17
+ key :created_at, Time
18
+ key :run_at, Time
19
+ key :started_at, Time
20
+ key :completed_at, Time
21
+ key :failed_at, Time
22
+
23
+ ##
24
+ # Default limit of jobs to be fetched
25
+ #
26
+ # @return [Integer] limit
27
+
28
+ def self.limit
29
+ @limit || 100
30
+ end
31
+
32
+ ##
33
+ # Should the job be kept?
34
+ #
35
+ # @return [true, false] keep
36
+
37
+ def self.keep?
38
+ keep = (@keep || false)
39
+ return keep.from_now >= Time.now if keep.is_a? Fixnum
40
+ keep
41
+ end
14
42
 
15
43
  ##
16
44
  # Add a job to the job queue.
17
45
  #
18
46
  # @param [Object] object the object you want to run a method from
19
- # @param [Symbol, String] method the name of the method you want to run
47
+ # @param [Symbol, String] method_name the name of the method you want to
48
+ # run
20
49
  # @param [*] arguments optional arguments you want to pass to the method
21
50
  #
22
- # @return [true, false]
51
+ # @return [true, false]
23
52
 
24
- def self.enqueue(object, method, *args)
53
+ def self.enqueue(object, method_name, *args)
25
54
  create(
26
- :object => object.name,
27
- :method => method.to_sym,
28
- :run_at => Time.now,
29
- :arguments => args
55
+ :object => object.to_s,
56
+ :method_name => method_name.to_sym,
57
+ :arguments => args,
58
+ :run_at => Time.now,
59
+ :created_at => Time.now
30
60
  )
31
61
  end
32
62
 
@@ -35,25 +65,47 @@ module Navvy
35
65
  # jobs (where :failed_at is not nil) and jobs that should run in the future
36
66
  # (where :run_at is greater than the current time).
37
67
  #
38
- # @param [Integer] limit the limit of jobs to be fetched. Defaults to
68
+ # @param [Integer] limit the limit of jobs to be fetched. Defaults to
39
69
  # Navvy::Job.limit
40
70
  #
41
71
  # @return [array, nil] the next available jobs in an array or nil if no
42
- # jobsn were found.
72
+ # jobs were found.
43
73
 
44
74
  def self.next(limit = self.limit)
45
75
  all(
46
- :failed_at => nil,
47
- :run_at => {'$lte', Time.now},
48
- :limit => limit
76
+ :failed_at => nil,
77
+ :completed_at => nil,
78
+ :run_at => {'$lte', Time.now},
79
+ :limit => limit,
80
+ :order => 'created_at'
49
81
  )
50
82
  end
51
83
 
84
+ ##
85
+ # Clean up jobs that we don't need to keep anymore. If Navvy::Job.keep is
86
+ # false it'll delete every completed job, if it's a timestamp it'll only
87
+ # delete completed jobs that have passed their keeptime.
88
+ #
89
+ # @return [true, false] delete_all the result of the delete_all call
90
+
91
+ def self.cleanup
92
+ if keep.is_a? Fixnum
93
+ delete_all(
94
+ :completed_at => {'$lte' => keep.ago}
95
+ )
96
+ else
97
+ delete_all(
98
+ :completed_at => {'$ne' => nil}
99
+ ) unless keep?
100
+ end
101
+ end
102
+
52
103
  ##
53
104
  # Run the job. Will delete the Navvy::Job record and return its return
54
- # value if it runs successfully. If a job fails, it'll update the
55
- # Navvy::Job record to include the exception message it sent back and set
56
- # the :failed_at date. Failed jobs don't get deleted.
105
+ # value if it runs successfully unless Navvy::Job.keep is set. If a job
106
+ # fails, it'll update the Navvy::Job record to include the exception
107
+ # message it sent back and set the :failed_at date. Failed jobs never get
108
+ # deleted.
57
109
  #
58
110
  # @example
59
111
  # job = Navvy::Job.next # finds the next available job in the queue
@@ -63,15 +115,82 @@ module Navvy
63
115
 
64
116
  def run
65
117
  begin
66
- result = object.constantize.send(method)
67
- destroy
118
+ update_attributes(:started_at => Time.now)
119
+ if args.empty?
120
+ result = object.constantize.send(method_name)
121
+ else
122
+ result = object.constantize.send(method_name, *args)
123
+ end
124
+ Navvy::Job.keep? ? completed : destroy
68
125
  result
69
126
  rescue Exception => exception
70
- update_attributes({
71
- :exception => exception.message,
72
- :failed_at => Time.now
73
- })
127
+ failed(exception.message)
74
128
  end
75
129
  end
130
+
131
+ ##
132
+ # Mark the job as completed. Will set completed_at to the current time and
133
+ # optionally add the return value if provided.
134
+ #
135
+ # @param [String] return_value the return value you want to store.
136
+ #
137
+ # @return [true, false] update_attributes the result of the
138
+ # update_attributes call
139
+
140
+ def completed(return_value = nil)
141
+ update_attributes({
142
+ :completed_at => Time.now,
143
+ :return => return_value
144
+ })
145
+ end
146
+
147
+ ##
148
+ # Mark the job as failed. Will set failed_at to the current time and
149
+ # optionally add the exception message if provided.
150
+ #
151
+ # @param [String] exception the exception message you want to store.
152
+ #
153
+ # @return [true, false] update_attributes the result of the
154
+ # update_attributes call
155
+
156
+ def failed(message = nil)
157
+ update_attributes({
158
+ :failed_at => Time.now,
159
+ :exception => message
160
+ })
161
+ end
162
+
163
+ ##
164
+ # Check if the job has been run.
165
+ #
166
+ # @return [true, false] ran
167
+
168
+ def ran?
169
+ completed? || failed?
170
+ end
171
+
172
+ ##
173
+ # Check how long it took for a job to complete or fail
174
+ #
175
+ # @return [Time, Integer] time the time it took
176
+
177
+ def duration
178
+ ran? ? (completed_at || failed_at) - started_at : 0
179
+ end
180
+
181
+ ##
182
+ # Get the job status
183
+ #
184
+ # @return [:pending, :completed, :failed] status
185
+
186
+ def status
187
+ return :completed if completed?
188
+ return :failed if failed?
189
+ :pending
190
+ end
191
+
192
+ alias_method :completed?, :completed_at?
193
+ alias_method :failed?, :failed_at?
194
+ alias_method :args, :arguments
76
195
  end
77
196
  end