emipair-delayed_job 2.0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. data/.gitignore +2 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.textile +213 -0
  4. data/Rakefile +33 -0
  5. data/VERSION +1 -0
  6. data/benchmarks.rb +33 -0
  7. data/contrib/delayed_job.monitrc +14 -0
  8. data/contrib/delayed_job_multiple.monitrc +23 -0
  9. data/emipair-delayed_job.gemspec +25 -0
  10. data/generators/delayed_job/delayed_job_generator.rb +22 -0
  11. data/generators/delayed_job/templates/migration.rb +21 -0
  12. data/generators/delayed_job/templates/script +5 -0
  13. data/init.rb +1 -0
  14. data/lib/delayed/backend/active_record.rb +90 -0
  15. data/lib/delayed/backend/base.rb +111 -0
  16. data/lib/delayed/backend/data_mapper.rb +147 -0
  17. data/lib/delayed/backend/mongo_mapper.rb +110 -0
  18. data/lib/delayed/command.rb +109 -0
  19. data/lib/delayed/message_sending.rb +22 -0
  20. data/lib/delayed/performable_method.rb +62 -0
  21. data/lib/delayed/railtie.rb +10 -0
  22. data/lib/delayed/recipes.rb +31 -0
  23. data/lib/delayed/tasks.rb +15 -0
  24. data/lib/delayed/worker.rb +214 -0
  25. data/lib/delayed_job.rb +15 -0
  26. data/lib/passive_support.rb +4 -0
  27. data/lib/passive_support/basic_object.rb +16 -0
  28. data/lib/passive_support/core_ext.rb +8 -0
  29. data/lib/passive_support/core_ext/class.rb +1 -0
  30. data/lib/passive_support/core_ext/class/attribute_accessors.rb +57 -0
  31. data/lib/passive_support/core_ext/date.rb +10 -0
  32. data/lib/passive_support/core_ext/date/behavior.rb +42 -0
  33. data/lib/passive_support/core_ext/date/calculations.rb +241 -0
  34. data/lib/passive_support/core_ext/date/conversions.rb +107 -0
  35. data/lib/passive_support/core_ext/date_time.rb +12 -0
  36. data/lib/passive_support/core_ext/date_time/calculations.rb +126 -0
  37. data/lib/passive_support/core_ext/date_time/conversions.rb +107 -0
  38. data/lib/passive_support/core_ext/enumerable.rb +120 -0
  39. data/lib/passive_support/core_ext/kernel.rb +5 -0
  40. data/lib/passive_support/core_ext/kernel/agnostics.rb +11 -0
  41. data/lib/passive_support/core_ext/kernel/daemonizing.rb +7 -0
  42. data/lib/passive_support/core_ext/kernel/debugger.rb +16 -0
  43. data/lib/passive_support/core_ext/kernel/reporting.rb +59 -0
  44. data/lib/passive_support/core_ext/kernel/requires.rb +24 -0
  45. data/lib/passive_support/core_ext/module.rb +20 -0
  46. data/lib/passive_support/core_ext/module/aliasing.rb +74 -0
  47. data/lib/passive_support/core_ext/module/attr_accessor_with_default.rb +31 -0
  48. data/lib/passive_support/core_ext/module/attr_internal.rb +32 -0
  49. data/lib/passive_support/core_ext/module/delegation.rb +135 -0
  50. data/lib/passive_support/core_ext/module/inclusion.rb +30 -0
  51. data/lib/passive_support/core_ext/module/introspection.rb +90 -0
  52. data/lib/passive_support/core_ext/module/loading.rb +23 -0
  53. data/lib/passive_support/core_ext/module/model_naming.rb +25 -0
  54. data/lib/passive_support/core_ext/module/synchronization.rb +39 -0
  55. data/lib/passive_support/core_ext/numeric.rb +9 -0
  56. data/lib/passive_support/core_ext/numeric/bytes.rb +50 -0
  57. data/lib/passive_support/core_ext/numeric/conversions.rb +19 -0
  58. data/lib/passive_support/core_ext/numeric/time.rb +81 -0
  59. data/lib/passive_support/core_ext/object.rb +6 -0
  60. data/lib/passive_support/core_ext/object/blank.rb +76 -0
  61. data/lib/passive_support/core_ext/object/conversions.rb +15 -0
  62. data/lib/passive_support/core_ext/object/extending.rb +80 -0
  63. data/lib/passive_support/core_ext/object/instance_variables.rb +74 -0
  64. data/lib/passive_support/core_ext/object/misc.rb +90 -0
  65. data/lib/passive_support/core_ext/object/singleton_class.rb +13 -0
  66. data/lib/passive_support/core_ext/string.rb +1 -0
  67. data/lib/passive_support/core_ext/string/constantize.rb +7 -0
  68. data/lib/passive_support/core_ext/time.rb +46 -0
  69. data/lib/passive_support/core_ext/time/behavior.rb +13 -0
  70. data/lib/passive_support/core_ext/time/calculations.rb +313 -0
  71. data/lib/passive_support/core_ext/time/conversions.rb +90 -0
  72. data/lib/passive_support/core_ext/time/zones.rb +86 -0
  73. data/lib/passive_support/duration.rb +100 -0
  74. data/lib/passive_support/ordered_hash.rb +158 -0
  75. data/rails/init.rb +5 -0
  76. data/recipes/delayed_job.rb +1 -0
  77. data/spec/backend/active_record_job_spec.rb +46 -0
  78. data/spec/backend/data_mapper_job_spec.rb +16 -0
  79. data/spec/backend/mongo_mapper_job_spec.rb +94 -0
  80. data/spec/backend/shared_backend_spec.rb +268 -0
  81. data/spec/delayed_method_spec.rb +58 -0
  82. data/spec/performable_method_spec.rb +42 -0
  83. data/spec/sample_jobs.rb +25 -0
  84. data/spec/setup/active_record.rb +33 -0
  85. data/spec/setup/data_mapper.rb +24 -0
  86. data/spec/setup/mongo_mapper.rb +17 -0
  87. data/spec/spec_helper.rb +19 -0
  88. data/spec/story_spec.rb +17 -0
  89. data/spec/worker_spec.rb +225 -0
  90. data/tasks/jobs.rake +1 -0
  91. metadata +323 -0
@@ -0,0 +1,86 @@
1
+ module PassiveSupport #:nodoc:
2
+ module CoreExtensions #:nodoc:
3
+ module Time #:nodoc:
4
+ module Zones
5
+ def self.included(base) #:nodoc:
6
+ base.extend(ClassMethods) if base == ::Time # i.e., don't include class methods in DateTime
7
+ end
8
+
9
+ module ClassMethods
10
+ attr_accessor :zone_default
11
+
12
+ # Returns the TimeZone for the current request, if this has been set (via Time.zone=).
13
+ # If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>.
14
+ def zone
15
+ Thread.current[:time_zone] || zone_default
16
+ end
17
+
18
+ # Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread.
19
+ #
20
+ # This method accepts any of the following:
21
+ #
22
+ # * A Rails TimeZone object.
23
+ # * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", <tt>-5.hours</tt>).
24
+ # * A TZInfo::Timezone object.
25
+ # * An identifier for a TZInfo::Timezone object (e.g., "America/New_York").
26
+ #
27
+ # Here's an example of how you might set <tt>Time.zone</tt> on a per request basis -- <tt>current_user.time_zone</tt>
28
+ # just needs to return a string identifying the user's preferred TimeZone:
29
+ #
30
+ # class ApplicationController < ActionController::Base
31
+ # before_filter :set_time_zone
32
+ #
33
+ # def set_time_zone
34
+ # Time.zone = current_user.time_zone
35
+ # end
36
+ # end
37
+ def zone=(time_zone)
38
+ Thread.current[:time_zone] = get_zone(time_zone)
39
+ end
40
+
41
+ # Allows override of <tt>Time.zone</tt> locally inside supplied block; resets <tt>Time.zone</tt> to existing value when done.
42
+ def use_zone(time_zone)
43
+ old_zone, ::Time.zone = ::Time.zone, get_zone(time_zone)
44
+ yield
45
+ ensure
46
+ ::Time.zone = old_zone
47
+ end
48
+
49
+ # Returns <tt>Time.zone.now</tt> when <tt>config.time_zone</tt> is set, otherwise just returns <tt>Time.now</tt>.
50
+ def current
51
+ ::Time.zone_default ? ::Time.zone.now : ::Time.now
52
+ end
53
+
54
+ private
55
+ def get_zone(time_zone)
56
+ return time_zone if time_zone.nil? || time_zone.is_a?(TimeZone)
57
+ # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone)
58
+ unless time_zone.respond_to?(:period_for_local)
59
+ time_zone = TimeZone[time_zone] || TZInfo::Timezone.get(time_zone) rescue nil
60
+ end
61
+ # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone
62
+ if time_zone
63
+ time_zone.is_a?(TimeZone) ? time_zone : TimeZone.create(time_zone.name, nil, time_zone)
64
+ end
65
+ end
66
+ end
67
+
68
+ # Returns the simultaneous time in <tt>Time.zone</tt>.
69
+ #
70
+ # Time.zone = 'Hawaii' # => 'Hawaii'
71
+ # Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
72
+ #
73
+ # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
74
+ # instead of the operating system's time zone.
75
+ #
76
+ # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
77
+ # and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
78
+ #
79
+ # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
80
+ def in_time_zone(zone = ::Time.zone)
81
+ PassiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone))
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,100 @@
1
+ require 'passive_support/basic_object'
2
+
3
+ module PassiveSupport
4
+ # Provides accurate date and time measurements using Date#advance and
5
+ # Time#advance, respectively. It mainly supports the methods on Numeric,
6
+ # such as in this example:
7
+ #
8
+ # 1.month.ago # equivalent to Time.now.advance(:months => -1)
9
+ class Duration < BasicObject
10
+ attr_accessor :value, :parts
11
+
12
+ def initialize(value, parts) #:nodoc:
13
+ @value, @parts = value, parts
14
+ end
15
+
16
+ # Adds another Duration or a Numeric to this Duration. Numeric values
17
+ # are treated as seconds.
18
+ def +(other)
19
+ if Duration === other
20
+ Duration.new(value + other.value, @parts + other.parts)
21
+ else
22
+ Duration.new(value + other, @parts + [[:seconds, other]])
23
+ end
24
+ end
25
+
26
+ # Subtracts another Duration or a Numeric from this Duration. Numeric
27
+ # values are treated as seconds.
28
+ def -(other)
29
+ self + (-other)
30
+ end
31
+
32
+ def -@ #:nodoc:
33
+ Duration.new(-value, parts.map { |type,number| [type, -number] })
34
+ end
35
+
36
+ def is_a?(klass) #:nodoc:
37
+ klass == Duration || super
38
+ end
39
+
40
+ # Returns true if <tt>other</tt> is also a Duration instance with the
41
+ # same <tt>value</tt>, or if <tt>other == value</tt>.
42
+ def ==(other)
43
+ if Duration === other
44
+ other.value == value
45
+ else
46
+ other == value
47
+ end
48
+ end
49
+
50
+ def self.===(other) #:nodoc:
51
+ other.is_a?(Duration) rescue super
52
+ end
53
+
54
+ # Calculates a new Time or Date that is as far in the future
55
+ # as this Duration represents.
56
+ def since(time = ::Time.current)
57
+ sum(1, time)
58
+ end
59
+ alias :from_now :since
60
+
61
+ # Calculates a new Time or Date that is as far in the past
62
+ # as this Duration represents.
63
+ def ago(time = ::Time.current)
64
+ sum(-1, time)
65
+ end
66
+ alias :until :ago
67
+
68
+ def inspect #:nodoc:
69
+ consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h }
70
+ parts = [:years, :months, :days, :minutes, :seconds].map do |length|
71
+ n = consolidated[length]
72
+ "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
73
+ end.compact
74
+ parts = ["0 seconds"] if parts.empty?
75
+ parts.to_sentence(:locale => :en)
76
+ end
77
+
78
+ protected
79
+
80
+ def sum(sign, time = ::Time.current) #:nodoc:
81
+ parts.inject(time) do |t,(type,number)|
82
+ if t.acts_like?(:time) || t.acts_like?(:date)
83
+ if type == :seconds
84
+ t.since(sign * number)
85
+ else
86
+ t.advance(type => sign * number)
87
+ end
88
+ else
89
+ raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
90
+ end
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def method_missing(method, *args, &block) #:nodoc:
97
+ value.send(method, *args)
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,158 @@
1
+ require 'yaml'
2
+
3
+ YAML.add_builtin_type("omap") do |type, val|
4
+ PassiveSupport::OrderedHash[val.map(&:to_a).map(&:first)]
5
+ end
6
+
7
+ # OrderedHash is namespaced to prevent conflicts with other implementations
8
+ module PassiveSupport
9
+ class OrderedHash < ::Hash #:nodoc:
10
+ def to_yaml_type
11
+ "!tag:yaml.org,2002:omap"
12
+ end
13
+
14
+ def to_yaml(opts = {})
15
+ YAML.quick_emit(self, opts) do |out|
16
+ out.seq(taguri, to_yaml_style) do |seq|
17
+ each do |k, v|
18
+ seq.add(k => v)
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ # Hash is ordered in Ruby 1.9!
25
+ if RUBY_VERSION < '1.9'
26
+ def initialize(*args, &block)
27
+ super
28
+ @keys = []
29
+ end
30
+
31
+ def self.[](*args)
32
+ ordered_hash = new
33
+
34
+ if (args.length == 1 && args.first.is_a?(Array))
35
+ args.first.each do |key_value_pair|
36
+ next unless (key_value_pair.is_a?(Array))
37
+ ordered_hash[key_value_pair[0]] = key_value_pair[1]
38
+ end
39
+
40
+ return ordered_hash
41
+ end
42
+
43
+ unless (args.size % 2 == 0)
44
+ raise ArgumentError.new("odd number of arguments for Hash")
45
+ end
46
+
47
+ args.each_with_index do |val, ind|
48
+ next if (ind % 2 != 0)
49
+ ordered_hash[val] = args[ind + 1]
50
+ end
51
+
52
+ ordered_hash
53
+ end
54
+
55
+ def initialize_copy(other)
56
+ super
57
+ # make a deep copy of keys
58
+ @keys = other.keys
59
+ end
60
+
61
+ def []=(key, value)
62
+ @keys << key if !has_key?(key)
63
+ super
64
+ end
65
+
66
+ def delete(key)
67
+ if has_key? key
68
+ index = @keys.index(key)
69
+ @keys.delete_at index
70
+ end
71
+ super
72
+ end
73
+
74
+ def delete_if
75
+ super
76
+ sync_keys!
77
+ self
78
+ end
79
+
80
+ def reject!
81
+ super
82
+ sync_keys!
83
+ self
84
+ end
85
+
86
+ def reject(&block)
87
+ dup.reject!(&block)
88
+ end
89
+
90
+ def keys
91
+ @keys.dup
92
+ end
93
+
94
+ def values
95
+ @keys.collect { |key| self[key] }
96
+ end
97
+
98
+ def to_hash
99
+ self
100
+ end
101
+
102
+ def to_a
103
+ @keys.map { |key| [ key, self[key] ] }
104
+ end
105
+
106
+ def each_key
107
+ @keys.each { |key| yield key }
108
+ end
109
+
110
+ def each_value
111
+ @keys.each { |key| yield self[key]}
112
+ end
113
+
114
+ def each
115
+ @keys.each {|key| yield [key, self[key]]}
116
+ end
117
+
118
+ alias_method :each_pair, :each
119
+
120
+ def clear
121
+ super
122
+ @keys.clear
123
+ self
124
+ end
125
+
126
+ def shift
127
+ k = @keys.first
128
+ v = delete(k)
129
+ [k, v]
130
+ end
131
+
132
+ def merge!(other_hash)
133
+ other_hash.each {|k,v| self[k] = v }
134
+ self
135
+ end
136
+
137
+ def merge(other_hash)
138
+ dup.merge!(other_hash)
139
+ end
140
+
141
+ # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
142
+ def replace(other)
143
+ super
144
+ @keys = other.keys
145
+ self
146
+ end
147
+
148
+ def inspect
149
+ "#<OrderedHash #{super}>"
150
+ end
151
+
152
+ private
153
+ def sync_keys!
154
+ @keys.delete_if {|k| !has_key?(k)}
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,5 @@
1
+ require 'delayed_job'
2
+
3
+ config.after_initialize do
4
+ Delayed::Worker.guess_backend
5
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'delayed', 'recipes'))
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'backend/shared_backend_spec'
3
+ require 'delayed/backend/active_record'
4
+
5
+ describe Delayed::Backend::ActiveRecord::Job do
6
+ before(:all) do
7
+ @backend = Delayed::Backend::ActiveRecord::Job
8
+ end
9
+
10
+ before(:each) do
11
+ Delayed::Backend::ActiveRecord::Job.delete_all
12
+ SimpleJob.runs = 0
13
+ end
14
+
15
+ after do
16
+ Time.zone = nil
17
+ end
18
+
19
+ it_should_behave_like 'a backend'
20
+
21
+ context "db_time_now" do
22
+ it "should return time in current time zone if set" do
23
+ Time.zone = 'Eastern Time (US & Canada)'
24
+ %w(EST EDT).should include(Delayed::Job.db_time_now.zone)
25
+ end
26
+
27
+ it "should return UTC time if that is the AR default" do
28
+ Time.zone = nil
29
+ ActiveRecord::Base.default_timezone = :utc
30
+ Delayed::Backend::ActiveRecord::Job.db_time_now.zone.should == 'UTC'
31
+ end
32
+
33
+ it "should return local time if that is the AR default" do
34
+ Time.zone = 'Central Time (US & Canada)'
35
+ ActiveRecord::Base.default_timezone = :local
36
+ %w(CST CDT).should include(Delayed::Backend::ActiveRecord::Job.db_time_now.zone)
37
+ end
38
+ end
39
+
40
+ describe "after_fork" do
41
+ it "should call reconnect on the connection" do
42
+ ActiveRecord::Base.connection.should_receive(:reconnect!)
43
+ Delayed::Backend::ActiveRecord::Job.after_fork
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+ require 'backend/shared_backend_spec'
3
+ require 'delayed/backend/data_mapper'
4
+
5
+ describe Delayed::Backend::DataMapper::Job do
6
+ before(:all) do
7
+ @backend = Delayed::Backend::DataMapper::Job
8
+ end
9
+
10
+ before(:each) do
11
+ # reset database before each example is run
12
+ DataMapper.auto_migrate!
13
+ end
14
+
15
+ it_should_behave_like 'a backend'
16
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+ require 'backend/shared_backend_spec'
3
+ require 'delayed/backend/mongo_mapper'
4
+
5
+ describe Delayed::Backend::MongoMapper::Job do
6
+ before(:all) do
7
+ @backend = Delayed::Backend::MongoMapper::Job
8
+ end
9
+
10
+ before(:each) do
11
+ MongoMapper.database.collections.each(&:remove)
12
+ end
13
+
14
+ it_should_behave_like 'a backend'
15
+
16
+ describe "indexes" do
17
+ it "should have combo index on priority and run_at" do
18
+ @backend.collection.index_information.detect { |index| index[0] == 'priority_1_run_at_1' }.should_not be_nil
19
+ end
20
+
21
+ it "should have index on locked_by" do
22
+ @backend.collection.index_information.detect { |index| index[0] == 'locked_by_1' }.should_not be_nil
23
+ end
24
+ end
25
+
26
+ describe "delayed method" do
27
+ class MongoStoryReader
28
+ def read(story)
29
+ "Epilog: #{story.tell}"
30
+ end
31
+ end
32
+
33
+ class MongoStory
34
+ include ::MongoMapper::Document
35
+ key :text, String
36
+
37
+ def tell
38
+ text
39
+ end
40
+ end
41
+
42
+ it "should ignore not found errors because they are permanent" do
43
+ story = MongoStory.create :text => 'Once upon a time...'
44
+ job = story.send_later(:tell)
45
+ story.destroy
46
+ lambda { job.invoke_job }.should_not raise_error
47
+ end
48
+
49
+ it "should store the object as string" do
50
+ story = MongoStory.create :text => 'Once upon a time...'
51
+ job = story.send_later(:tell)
52
+
53
+ job.payload_object.class.should == Delayed::PerformableMethod
54
+ job.payload_object.object.should == "LOAD;MongoStory;#{story.id}"
55
+ job.payload_object.method.should == :tell
56
+ job.payload_object.args.should == []
57
+ job.payload_object.perform.should == 'Once upon a time...'
58
+ end
59
+
60
+ it "should store arguments as string" do
61
+ story = MongoStory.create :text => 'Once upon a time...'
62
+ job = MongoStoryReader.new.send_later(:read, story)
63
+ job.payload_object.class.should == Delayed::PerformableMethod
64
+ job.payload_object.method.should == :read
65
+ job.payload_object.args.should == ["LOAD;MongoStory;#{story.id}"]
66
+ job.payload_object.perform.should == 'Epilog: Once upon a time...'
67
+ end
68
+ end
69
+
70
+ describe "before_fork" do
71
+ after do
72
+ MongoMapper.connection.connect_to_master
73
+ end
74
+
75
+ it "should disconnect" do
76
+ lambda do
77
+ Delayed::Backend::MongoMapper::Job.before_fork
78
+ end.should change { !!MongoMapper.connection.connected? }.from(true).to(false)
79
+ end
80
+ end
81
+
82
+ describe "after_fork" do
83
+ before do
84
+ MongoMapper.connection.close
85
+ end
86
+
87
+ it "should call reconnect" do
88
+ lambda do
89
+ Delayed::Backend::MongoMapper::Job.after_fork
90
+ end.should change { !!MongoMapper.connection.connected? }.from(false).to(true)
91
+ end
92
+ end
93
+
94
+ end