delayed_job_recurring 0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a415c64ebb7f90d1e582264d12150ea51ca9035e
4
+ data.tar.gz: 018743cb2951f1b615796103e7a16b66de915ca5
5
+ SHA512:
6
+ metadata.gz: 3a741233a41bdd26c970abf7e1eb9fc96b8dee93f0990d0925fa6d67ad918bc1537fc52219869156b5aaccec96203b6c7cdeceb3bc0db7ec4817760e0cc56a6a
7
+ data.tar.gz: d5ebc8c0ae9e1d2fc6b0503d432ee72b72e41273e1c268c9343cb7ca9a1122a88370dbba65fe9ede822831a013d649817bde53a1a575a681b35ee8ccc159c084
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Amitree
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # delayed\_job\_recurring
2
+
3
+ Extends delayed\_job to support recurring jobs.
4
+
5
+ ## Usage
6
+
7
+ In your Gemfile:
8
+
9
+ ```ruby
10
+ gem 'delayed_job_recurring'
11
+ ```
12
+
13
+ Then define a task class:
14
+
15
+ ```ruby
16
+ class MyTask
17
+ include Delayed::RecurringJob
18
+ run_every 1.day
19
+ run_at DateTime.parse('2014-03-08 11:00:00 PST')
20
+ timezone 'US/Pacific'
21
+ def perform
22
+ # Do some work here!
23
+ end
24
+ end
25
+ ```
26
+
27
+ And schedule it. In a rails app, you might put this in an initializer:
28
+
29
+ ```ruby
30
+ MyTask.schedule # run every day at 11am Pacific time (accounting for daylight savings)
31
+ ```
32
+
33
+ ## Advanced usage
34
+
35
+ ### Passing options to schedule
36
+
37
+ ```ruby
38
+ MyTask.schedule(run_at: DateTime.parse('2014-03-08 11:00:00 PST'))
39
+ ```
40
+
41
+ ### Running at multiples times each day
42
+
43
+ ```ruby
44
+ MyTask.schedule(run_every: 1.day, run_at: [DateTime.parse('2014-03-08 11:00:00 PST'), DateTime.parse('2014-03-08 18:00:00 PST')]
45
+ ```
46
+
47
+ ## Thanks!
48
+
49
+ Many thanks to @ginjo and @kares for their work! This code was derived from https://gist.github.com/ginjo/3688965.
@@ -0,0 +1,172 @@
1
+ #
2
+ # Delayed::RecurringJob
3
+ #
4
+ # Inspired by https://gist.github.com/ginjo/3688965
5
+ #
6
+ module Delayed
7
+ module RecurringJob
8
+ def self.included(base)
9
+ base.extend(ClassMethods)
10
+ base.class_eval do
11
+ @@logger = Delayed::Worker.logger
12
+ cattr_reader :logger
13
+ end
14
+ end
15
+
16
+ def failure
17
+ schedule!
18
+ end
19
+
20
+ def success
21
+ schedule!
22
+ end
23
+
24
+ # Schedule this "repeating" job
25
+ def schedule! options = {}
26
+ @schedule_options = options.reverse_merge(@schedule_options || {}).reverse_merge(
27
+ run_at: self.class.run_at,
28
+ timezone: self.class.timezone,
29
+ run_interval: serialize_duration(self.class.run_every)
30
+ )
31
+
32
+ enqueue_opts = { priority: 0, run_at: next_run_time }
33
+
34
+ if Gem.loaded_specs['delayed_job'].version.to_s.first.to_i < 3
35
+ Delayed::Job.enqueue self, enqueue_opts[:priority], enqueue_opts[:run_at]
36
+ else
37
+ Delayed::Job.enqueue self, enqueue_opts
38
+ end
39
+ end
40
+
41
+ def next_run_time
42
+ times = @schedule_options[:run_at]
43
+ times = [times] unless times.is_a? Array
44
+ times = times.map{|time| time.in_time_zone @schedule_options[:timezone]} if @schedule_options[:timezone]
45
+
46
+ interval = deserialize_duration(@schedule_options[:run_interval])
47
+
48
+ until next_time = next_future_time(times)
49
+ times.map!{ |time| time + interval }
50
+ end
51
+
52
+ # Update @schedule_options to avoid growing number of calculations each time
53
+ @schedule_options[:run_at] = times
54
+
55
+ next_time
56
+ end
57
+
58
+ private
59
+ # We don't want the run_interval to be serialized as a number of seconds.
60
+ # 1.day is not the same as 86400 (not all days are 86400 seconds long!)
61
+ def serialize_duration(duration)
62
+ case duration
63
+ when ActiveSupport::Duration
64
+ {value: duration.value, parts: duration.parts}
65
+ else
66
+ duration
67
+ end
68
+ end
69
+
70
+ def deserialize_duration(serialized)
71
+ case serialized
72
+ when Hash
73
+ ActiveSupport::Duration.new(serialized[:value], serialized[:parts])
74
+ else
75
+ serialized
76
+ end
77
+ end
78
+
79
+ def next_future_time(times)
80
+ times.select{|time| time > Time.now}.min
81
+ end
82
+
83
+ module ClassMethods
84
+ def run_at(time = nil)
85
+ if time.nil?
86
+ @run_at || run_every.from_now
87
+ else
88
+ @run_at = time
89
+ end
90
+ end
91
+
92
+ def run_every(interval = nil)
93
+ if interval.nil?
94
+ @run_interval || 1.hour
95
+ else
96
+ @run_interval = interval
97
+ end
98
+ end
99
+
100
+ def timezone(zone = nil)
101
+ if zone.nil?
102
+ @tz
103
+ else
104
+ @tz = zone
105
+ end
106
+ end
107
+
108
+ # Show all jobs for this schedule
109
+ def jobs
110
+ ::Delayed::Job.where("(handler LIKE ?) OR (handler LIKE ?)", "--- !ruby/object:#{name} %", "--- !ruby/object:#{name}\n%")
111
+ end
112
+
113
+ # Remove all jobs for this schedule (Stop the schedule)
114
+ def unschedule
115
+ jobs.each{|j| j.destroy}
116
+ end
117
+
118
+ # Main interface to start this schedule (adds it to the jobs table).
119
+ # Pass in a time to run the first job (nil runs the first job at run_interval from now).
120
+ def schedule(options = {})
121
+ schedule!(options) if Delayed::Worker.delay_jobs && !scheduled?
122
+ end
123
+
124
+ def schedule!(options = {})
125
+ new.schedule!(options)
126
+ end
127
+
128
+ def scheduled?
129
+ jobs.count > 0
130
+ end
131
+
132
+ end # ClassMethods
133
+ end # RecurringJob
134
+
135
+ module Task
136
+ # Creates a new class wrapper around a block of code to be scheduled.
137
+ def self.new(name, options, &block)
138
+ task_class = Class.new
139
+ task_class.class_eval do
140
+ include Delayed::RecurringJob
141
+
142
+ def display_name
143
+ self.class.name
144
+ end
145
+
146
+ def perform
147
+ block.call
148
+ end
149
+ end
150
+
151
+ Object.const_set(name, task_class) if name
152
+ task_class.schedule(options)
153
+ return task_class
154
+ end
155
+
156
+ # Schedule a block of code on-the-fly.
157
+ # This is a friendly wrapper for using Task.new without an explicit constant assignment.
158
+ # Delayed::Task.schedule('MyNewTask', run_every: 10.minutes, run_at: 1.minute.from_now){do_some_stuff_here}
159
+ # or
160
+ # Delayed::Task.schedule(run_every: 10.minutes, run_at: 1.minute.from_now){do_some_stuff_here}
161
+ def self.schedule(name_or_options={}, options={}, &block)
162
+ case name_or_options
163
+ when Hash
164
+ name, options = nil, name_or_options
165
+ else
166
+ name = name_or_options
167
+ end
168
+
169
+ self.new name, options, &block
170
+ end
171
+ end # Task
172
+ end # Delayed
@@ -0,0 +1,3 @@
1
+ require 'delayed_job'
2
+ require 'delayed_job_active_record'
3
+ require 'delayed/recurring_job'
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: delayed_job_recurring
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Tony Novak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 3.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec-rails
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 3.0.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 3.0.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: database_cleaner
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: timecop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.7.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.7.1
97
+ - !ruby/object:Gem::Dependency
98
+ name: delayed_job
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '4.0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '4.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: delayed_job_active_record
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '4.0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '4.0'
125
+ description: Extends delayed_job to support recurring jobs, including timezone support
126
+ email: engineering@amitree.com
127
+ executables: []
128
+ extensions: []
129
+ extra_rdoc_files: []
130
+ files:
131
+ - LICENSE
132
+ - README.md
133
+ - lib/delayed/recurring_job.rb
134
+ - lib/delayed_job_recurring.rb
135
+ homepage: https://github.com/amitree/delayed_job_recurring
136
+ licenses:
137
+ - MIT
138
+ metadata: {}
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - "~>"
146
+ - !ruby/object:Gem::Version
147
+ version: '2.0'
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubyforge_project:
155
+ rubygems_version: 2.2.2
156
+ signing_key:
157
+ specification_version: 4
158
+ summary: Recurring jobs for delayed_job
159
+ test_files: []