drewda_delayed_job_active_record 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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