emipair-delayed_job 2.0.3.1

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.
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