drewda_delayed_job_active_record 0.3.2

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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2005 Tobias Luetke
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOa AND
17
+ NONINFRINGEMENT. IN NO EVENT SaALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,44 @@
1
+ # DelayedJob ActiveRecord Backend [![Build Status](http://travis-ci.org/collectiveidea/delayed_job_active_record.png)](https://travis-ci.org/collectiveidea/delayed_job_active_record) [![Dependency Status](https://gemnasium.com/collectiveidea/delayed_job_active_record.png)](https://gemnasium.com/collectiveidea/delayed_job_active_record)
2
+
3
+ The only difference in drewda_delayed_job_active_record is in `lib/delayed/backend/active_record.rb`:
4
+
5
+ attr_accessible ... :unique_id, :is_locked
6
+
7
+ This makes it compatible with drewda_delayed_job.
8
+
9
+ ## Installation
10
+
11
+ Add the gem to your Gemfile:
12
+
13
+ gem 'delayed_job_active_record'
14
+
15
+ Run `bundle install`.
16
+
17
+ If you're using Rails, run the generator to create the migration for the delayed_job table.
18
+
19
+ rails g delayed_job:active_record
20
+ rake db:migrate
21
+
22
+ ## Upgrading from 2.x to 3.0.0
23
+
24
+ If you're upgrading from Delayed Job 2.x, run the upgrade generator to create a migration to add a column to your delayed_jobs table.
25
+
26
+ rails g delayed_job:upgrade
27
+ rake db:migrate
28
+
29
+ That's it. Use [delayed_job as normal](http://github.com/collectiveidea/delayed_job).
30
+
31
+ ## How to contribute
32
+
33
+ If you find what looks like a bug:
34
+
35
+ * Search the [mailing list](http://groups.google.com/group/delayed_job) to see if anyone else had the same issue.
36
+ * Check the [GitHub issue tracker](http://github.com/collectiveidea/delayed_job_active_record/issues/) to see if anyone else has reported issue.
37
+ * If you don't see anything, create an issue with information on how to reproduce it.
38
+
39
+ If you want to contribute an enhancement or a fix:
40
+
41
+ * Fork the project on github.
42
+ * Make your changes with tests.
43
+ * Commit the changes without making changes to the Rakefile or any other files that aren't related to your enhancement or fix
44
+ * Send a pull request.
@@ -0,0 +1,104 @@
1
+ require 'active_record'
2
+ require 'active_record/version'
3
+ module Delayed
4
+ module Backend
5
+ module ActiveRecord
6
+ # A job object that is persisted to the database.
7
+ # Contains the work object as a YAML field.
8
+ class Job < ::ActiveRecord::Base
9
+ include Delayed::Backend::Base
10
+
11
+ attr_accessible :priority, :run_at, :queue, :payload_object,
12
+ :failed_at, :locked_at, :locked_by, :unique_id, :is_locked
13
+
14
+ before_save :set_default_run_at
15
+
16
+ def self.rails3?
17
+ ::ActiveRecord::VERSION::MAJOR == 3
18
+ end
19
+
20
+ if rails3?
21
+ self.table_name = 'delayed_jobs'
22
+ def self.ready_to_run(worker_name, max_run_time)
23
+ where('(run_at <= ? AND (locked_at IS NULL OR locked_at < ?) OR locked_by = ?) AND failed_at IS NULL', db_time_now, db_time_now - max_run_time, worker_name)
24
+ end
25
+ def self.by_priority
26
+ order('priority ASC, run_at ASC')
27
+ end
28
+ else
29
+ set_table_name :delayed_jobs
30
+ named_scope :ready_to_run, lambda {|worker_name, max_run_time|
31
+ { :conditions => ['(run_at <= ? AND (locked_at IS NULL OR locked_at < ?) OR locked_by = ?) AND failed_at IS NULL', db_time_now, db_time_now - max_run_time, worker_name] }
32
+ }
33
+ named_scope :by_priority, :order => 'priority ASC, run_at ASC'
34
+ end
35
+
36
+ def self.before_fork
37
+ ::ActiveRecord::Base.clear_all_connections!
38
+ end
39
+
40
+ def self.after_fork
41
+ ::ActiveRecord::Base.establish_connection
42
+ end
43
+
44
+ # When a worker is exiting, make sure we don't have any locked jobs.
45
+ def self.clear_locks!(worker_name)
46
+ update_all("locked_by = null, locked_at = null", ["locked_by = ?", worker_name])
47
+ end
48
+
49
+ # Find a few candidate jobs to run (in case some immediately get locked by others).
50
+ def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)
51
+ scope = self.ready_to_run(worker_name, max_run_time)
52
+ scope = scope.scoped(:conditions => ['priority >= ?', Worker.min_priority]) if Worker.min_priority
53
+ scope = scope.scoped(:conditions => ['priority <= ?', Worker.max_priority]) if Worker.max_priority
54
+ scope = scope.scoped(:conditions => ["queue IN (?)", Worker.queues]) if Worker.queues.any?
55
+
56
+ ::ActiveRecord::Base.silence do
57
+ scope.by_priority.all(:limit => limit)
58
+ end
59
+ end
60
+
61
+ # Lock this job for this worker.
62
+ # Returns true if we have the lock, false otherwise.
63
+ def lock_exclusively!(max_run_time, worker)
64
+ now = self.class.db_time_now
65
+ affected_rows = if locked_by != worker
66
+ # We don't own this job so we will update the locked_by name and the locked_at
67
+ self.class.update_all(["locked_at = ?, locked_by = ?", now, worker], ["id = ? and (locked_at is null or locked_at < ?) and (run_at <= ?)", id, (now - max_run_time.to_i), now])
68
+ else
69
+ # We already own this job, this may happen if the job queue crashes.
70
+ # Simply resume and update the locked_at
71
+ self.class.update_all(["locked_at = ?", now], ["id = ? and locked_by = ?", id, worker])
72
+ end
73
+ if affected_rows == 1
74
+ self.locked_at = now
75
+ self.locked_by = worker
76
+ self.locked_at_will_change!
77
+ self.locked_by_will_change!
78
+ return true
79
+ else
80
+ return false
81
+ end
82
+ end
83
+
84
+ # Get the current time (GMT or local depending on DB)
85
+ # Note: This does not ping the DB to get the time, so all your clients
86
+ # must have syncronized clocks.
87
+ def self.db_time_now
88
+ if Time.zone
89
+ Time.zone.now
90
+ elsif ::ActiveRecord::Base.default_timezone == :utc
91
+ Time.now.utc
92
+ else
93
+ Time.now
94
+ end
95
+ end
96
+
97
+ def reload(*args)
98
+ reset
99
+ super
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,5 @@
1
+ require 'active_record'
2
+ require 'delayed_job'
3
+ require 'delayed/backend/active_record'
4
+
5
+ Delayed::Worker.backend = :active_record
@@ -0,0 +1,17 @@
1
+ require 'generators/delayed_job/delayed_job_generator'
2
+ require 'rails/generators/migration'
3
+ require 'rails/generators/active_record/migration'
4
+
5
+ # Extend the DelayedJobGenerator so that it creates an AR migration
6
+ module DelayedJob
7
+ class ActiveRecordGenerator < ::DelayedJobGenerator
8
+ include Rails::Generators::Migration
9
+ extend ActiveRecord::Generators::Migration
10
+
11
+ self.source_paths << File.join(File.dirname(__FILE__), 'templates')
12
+
13
+ def create_migration_file
14
+ migration_template 'migration.rb', 'db/migrate/create_delayed_jobs.rb'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ class CreateDelayedJobs < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :delayed_jobs, :force => true do |table|
4
+ table.integer :priority, :default => 0 # Allows some jobs to jump to the front of the queue
5
+ table.integer :attempts, :default => 0 # Provides for retries, but still fail eventually.
6
+ table.text :handler # YAML-encoded string of the object that will do work
7
+ table.text :last_error # reason for last failure (See Note below)
8
+ table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
9
+ table.datetime :locked_at # Set when a client is working on this object
10
+ table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
11
+ table.string :locked_by # Who is working on this object (if locked)
12
+ table.string :queue # The name of the queue this job is in
13
+ table.timestamps
14
+ end
15
+
16
+ add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
17
+ end
18
+
19
+ def self.down
20
+ drop_table :delayed_jobs
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ class AddQueueToDelayedJobs < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :delayed_jobs, :queue, :string
4
+ end
5
+
6
+ def self.down
7
+ remove_column :delayed_jobs, :queue
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ require 'generators/delayed_job/delayed_job_generator'
2
+ require 'rails/generators/migration'
3
+ require 'rails/generators/active_record/migration'
4
+
5
+ # Extend the DelayedJobGenerator so that it creates an AR migration
6
+ module DelayedJob
7
+ class UpgradeGenerator < ::DelayedJobGenerator
8
+ include Rails::Generators::Migration
9
+ extend ActiveRecord::Generators::Migration
10
+
11
+ self.source_paths << File.join(File.dirname(__FILE__), 'templates')
12
+
13
+ def create_migration_file
14
+ migration_template 'upgrade_migration.rb', 'db/migrate/add_queue_to_delayed_jobs.rb'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ sqlite:
2
+ adapter: sqlite3
3
+ database: ":memory:"
4
+
5
+ mysql:
6
+ adapter: mysql
7
+ database: delayed_job
8
+ uesrname: root
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+ require 'delayed/backend/active_record'
3
+
4
+ describe Delayed::Backend::ActiveRecord::Job do
5
+ after do
6
+ Time.zone = nil
7
+ end
8
+
9
+ it_should_behave_like 'a delayed_job backend'
10
+
11
+ context "db_time_now" do
12
+ it "should return time in current time zone if set" do
13
+ Time.zone = 'Eastern Time (US & Canada)'
14
+ %w(EST EDT).should include(Delayed::Job.db_time_now.zone)
15
+ end
16
+
17
+ it "should return UTC time if that is the AR default" do
18
+ Time.zone = nil
19
+ ActiveRecord::Base.default_timezone = :utc
20
+ Delayed::Backend::ActiveRecord::Job.db_time_now.zone.should == 'UTC'
21
+ end
22
+
23
+ it "should return local time if that is the AR default" do
24
+ Time.zone = 'Central Time (US & Canada)'
25
+ ActiveRecord::Base.default_timezone = :local
26
+ %w(CST CDT).should include(Delayed::Backend::ActiveRecord::Job.db_time_now.zone)
27
+ end
28
+ end
29
+
30
+ describe "after_fork" do
31
+ it "should call reconnect on the connection" do
32
+ ActiveRecord::Base.should_receive(:establish_connection)
33
+ Delayed::Backend::ActiveRecord::Job.after_fork
34
+ end
35
+ end
36
+
37
+ describe "enqueue" do
38
+ it "should allow enqueue hook to modify job at DB level" do
39
+ later = described_class.db_time_now + 20.minutes
40
+ job = Delayed::Backend::ActiveRecord::Job.enqueue :payload_object => EnqueueJobMod.new
41
+ Delayed::Backend::ActiveRecord::Job.find(job.id).run_at.should be_within(1).of(later)
42
+ end
43
+ end
44
+
45
+ context "ActiveRecord::Base.send(:attr_accessible, nil)" do
46
+ before do
47
+ Delayed::Backend::ActiveRecord::Job.send(:attr_accessible, nil)
48
+ end
49
+
50
+ after do
51
+ Delayed::Backend::ActiveRecord::Job.send(:attr_accessible, *Delayed::Backend::ActiveRecord::Job.new.attributes.keys)
52
+ end
53
+
54
+ it "should still be accessible" do
55
+ job = Delayed::Backend::ActiveRecord::Job.enqueue :payload_object => EnqueueJobMod.new
56
+ Delayed::Backend::ActiveRecord::Job.find(job.id).handler.should_not be_blank
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveRecord do
4
+ it 'should load classes with non-default primary key' do
5
+ lambda {
6
+ YAML.load(Story.create.to_yaml)
7
+ }.should_not raise_error
8
+ end
9
+
10
+ it 'should load classes even if not in default scope' do
11
+ lambda {
12
+ YAML.load(Story.create(:scoped => false).to_yaml)
13
+ }.should_not raise_error
14
+ end
15
+ end
@@ -0,0 +1,52 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ require 'rspec'
6
+ require 'logger'
7
+
8
+ require 'delayed_job_active_record'
9
+ require 'delayed/backend/shared_spec'
10
+
11
+ Delayed::Worker.logger = Logger.new('/tmp/dj.log')
12
+ ENV['RAILS_ENV'] = 'test'
13
+
14
+ config = YAML.load(File.read('spec/database.yml'))
15
+ ActiveRecord::Base.establish_connection config['sqlite']
16
+ ActiveRecord::Base.logger = Delayed::Worker.logger
17
+ ActiveRecord::Migration.verbose = false
18
+
19
+ ActiveRecord::Schema.define do
20
+ create_table :delayed_jobs, :force => true do |table|
21
+ table.integer :priority, :default => 0
22
+ table.integer :attempts, :default => 0
23
+ table.text :handler
24
+ table.text :last_error
25
+ table.datetime :run_at
26
+ table.datetime :locked_at
27
+ table.datetime :failed_at
28
+ table.string :locked_by
29
+ table.string :queue
30
+ table.timestamps
31
+ end
32
+
33
+ add_index :delayed_jobs, [:priority, :run_at], :name => 'delayed_jobs_priority'
34
+
35
+ create_table :stories, :primary_key => :story_id, :force => true do |table|
36
+ table.string :text
37
+ table.boolean :scoped, :default => true
38
+ end
39
+ end
40
+
41
+ # Purely useful for test cases...
42
+ class Story < ActiveRecord::Base
43
+ set_primary_key :story_id
44
+ def tell; text; end
45
+ def whatever(n, _); tell*n; end
46
+ default_scope where(:scoped => true)
47
+
48
+ handle_asynchronously :whatever
49
+ end
50
+
51
+ # Add this directory so the ActiveSupport autoloading works
52
+ ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: drewda_delayed_job_active_record
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Brian Ryckbost
9
+ - Matt Griffin
10
+ - Erik Michaels-Ober
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2012-05-29 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activerecord
18
+ requirement: !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ! '>='
22
+ - !ruby/object:Gem::Version
23
+ version: 2.1.0
24
+ - - <
25
+ - !ruby/object:Gem::Version
26
+ version: '4'
27
+ type: :runtime
28
+ prerelease: false
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: 2.1.0
35
+ - - <
36
+ - !ruby/object:Gem::Version
37
+ version: '4'
38
+ - !ruby/object:Gem::Dependency
39
+ name: drewda_delayed_job
40
+ requirement: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.0'
46
+ type: :runtime
47
+ prerelease: false
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '3.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rake
56
+ requirement: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec
72
+ requirement: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ - !ruby/object:Gem::Dependency
87
+ name: sqlite3
88
+ requirement: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ type: :development
95
+ prerelease: false
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ description: ActiveRecord backend for DelayedJob, originally authored by Tobias Luetke
103
+ email:
104
+ - bryckbost@gmail.com
105
+ - matt@griffinonline.org
106
+ - sferik@gmail.com
107
+ executables: []
108
+ extensions: []
109
+ extra_rdoc_files:
110
+ - README.md
111
+ files:
112
+ - lib/delayed/backend/active_record.rb
113
+ - lib/delayed_job_active_record.rb
114
+ - lib/generators/delayed_job/active_record_generator.rb
115
+ - lib/generators/delayed_job/templates/migration.rb
116
+ - lib/generators/delayed_job/templates/upgrade_migration.rb
117
+ - lib/generators/delayed_job/upgrade_generator.rb
118
+ - spec/database.yml
119
+ - spec/delayed/backend/active_record_spec.rb
120
+ - spec/delayed/serialization/active_record_spec.rb
121
+ - spec/spec_helper.rb
122
+ - LICENSE
123
+ - README.md
124
+ homepage: http://github.com/collectiveidea/delayed_job_active_record
125
+ licenses: []
126
+ post_install_message:
127
+ rdoc_options:
128
+ - --main
129
+ - README.md
130
+ - --inline-source
131
+ - --line-numbers
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ none: false
136
+ requirements:
137
+ - - ! '>='
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ requirements: []
147
+ rubyforge_project:
148
+ rubygems_version: 1.8.24
149
+ signing_key:
150
+ specification_version: 3
151
+ summary: ActiveRecord backend for DelayedJob
152
+ test_files:
153
+ - spec/database.yml
154
+ - spec/delayed/backend/active_record_spec.rb
155
+ - spec/delayed/serialization/active_record_spec.rb
156
+ - spec/spec_helper.rb