simple_scheduler 0.1.0
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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +234 -0
- data/Rakefile +13 -0
- data/lib/simple_scheduler.rb +10 -0
- data/lib/simple_scheduler/railtie.rb +8 -0
- data/lib/simple_scheduler/scheduler_job.rb +68 -0
- data/lib/simple_scheduler/task.rb +127 -0
- data/lib/simple_scheduler/version.rb +3 -0
- data/lib/tasks/simple_scheduler_tasks.rake +4 -0
- metadata +155 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8d15cd2b0b48692188a8b470d258e667dae84d08
|
4
|
+
data.tar.gz: 20eca360c888dbf7e482377f8d78053e988906c5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 452dae7f622c805289ade38235a8041d9ed23faeb9bd5a1acfa51577570afd07667dbb70d5d099c7630c9ca4fbe04245d80c53655f14464c50369bdb8747a52c
|
7
|
+
data.tar.gz: f1d49ad79d145d281ce24d4476c752c1d51ae8b37cba98e6e25830c0bc355a2b12e4c3b36a475a25f86a2456e74b0796c237ecae28f0eabd127ebf7d3ec79ac7
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016 Brian Pattison
|
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 PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL 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.
|
data/README.md
ADDED
@@ -0,0 +1,234 @@
|
|
1
|
+
# Simple Scheduler
|
2
|
+
|
3
|
+
[](https://travis-ci.org/simplymadeapps/simple_scheduler)
|
4
|
+
[](https://codeclimate.com/github/simplymadeapps/simple_scheduler)
|
5
|
+
[](http://www.rubydoc.info/github/simplymadeapps/simple_scheduler/)
|
6
|
+
|
7
|
+
Simple Scheduler is a scheduling add-on that is designed to be used with
|
8
|
+
[Sidekiq](http://sidekiq.org) and
|
9
|
+
[Heroku Scheduler](https://elements.heroku.com/addons/scheduler). It
|
10
|
+
gives you the ability to **schedule tasks at any interval** without adding
|
11
|
+
a clock process. Heroku Scheduler only allows you to schedule tasks every 10 minutes,
|
12
|
+
every hour, or every day.
|
13
|
+
|
14
|
+
## Requirements
|
15
|
+
|
16
|
+
You must be using:
|
17
|
+
|
18
|
+
- Rails 4.2+
|
19
|
+
- [Sidekiq](http://sidekiq.org)
|
20
|
+
- [Heroku Scheduler](https://elements.heroku.com/addons/scheduler)
|
21
|
+
|
22
|
+
Both Active Job and Sidekiq::Worker classes can be queued by the scheduler.
|
23
|
+
|
24
|
+
## Installation
|
25
|
+
|
26
|
+
Add this line to your application's Gemfile:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem "simple_scheduler"
|
30
|
+
```
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
|
34
|
+
```bash
|
35
|
+
$ bundle
|
36
|
+
```
|
37
|
+
|
38
|
+
## Getting Started
|
39
|
+
|
40
|
+
Create a configuration file `config/simple_scheduler.yml`:
|
41
|
+
|
42
|
+
```yml
|
43
|
+
# Global configuration options and their defaults. These can also be set on each task.
|
44
|
+
queue_ahead: 360 # Number of minutes to queue jobs into the future
|
45
|
+
tz: nil # The application time zone will be used by default
|
46
|
+
|
47
|
+
# Runs once every 2 minutes
|
48
|
+
simple_task:
|
49
|
+
class: "SomeActiveJob"
|
50
|
+
every: "2.minutes"
|
51
|
+
|
52
|
+
# Runs once every day at 4:00 AM
|
53
|
+
overnight_task:
|
54
|
+
class: "SomeSidekiqWorker"
|
55
|
+
every: "1.day"
|
56
|
+
at: "4:00"
|
57
|
+
|
58
|
+
# Runs once every hour at the half hour
|
59
|
+
half_hour_task:
|
60
|
+
class: "HalfHourTask"
|
61
|
+
every: "30.minutes"
|
62
|
+
at: "*:30"
|
63
|
+
|
64
|
+
# Runs once every week on Saturdays at 12:00 AM
|
65
|
+
weekly_task:
|
66
|
+
class: "WeeklyJob"
|
67
|
+
every: "1.week"
|
68
|
+
at: "Sat 0:00"
|
69
|
+
tz: "America/Chicago"
|
70
|
+
```
|
71
|
+
|
72
|
+
### Task options
|
73
|
+
|
74
|
+
#### :class
|
75
|
+
|
76
|
+
The class name of the ActiveJob or Sidekiq::Worker. Your job or
|
77
|
+
worker class should accept the expected run time as a parameter
|
78
|
+
on the `perform` method.
|
79
|
+
|
80
|
+
#### :every
|
81
|
+
|
82
|
+
How frequently the task should be performed as an ActiveSupport duration definition.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
1.day
|
86
|
+
5.days
|
87
|
+
12.hours
|
88
|
+
20.minutes
|
89
|
+
1.week
|
90
|
+
```
|
91
|
+
|
92
|
+
#### :at (optional)
|
93
|
+
|
94
|
+
This is the starting point for the `every` duration. If not given, the job will
|
95
|
+
run immediately when the configuration file is loaded for the first time and will
|
96
|
+
follow the `every` duration to determine future execution times.
|
97
|
+
|
98
|
+
Valid string formats/examples:
|
99
|
+
|
100
|
+
```
|
101
|
+
18:00
|
102
|
+
3:30
|
103
|
+
**:00
|
104
|
+
*:30
|
105
|
+
Sun 2:00
|
106
|
+
[Sun|Mon|Tue|Wed|Thu|Fri|Sat] 00:00
|
107
|
+
```
|
108
|
+
|
109
|
+
Add the rake task to Heroku Scheduler and set it to run every 10 minutes:
|
110
|
+
|
111
|
+
```
|
112
|
+
rake simple_scheduler
|
113
|
+
```
|
114
|
+
|
115
|
+
The file `config/simple_scheduler.yml` will be used by default, but it may be
|
116
|
+
useful to point to another configuration file in non-production environments.
|
117
|
+
|
118
|
+
```
|
119
|
+
rake simple_scheduler["config/simple_scheduler.staging.yml"]
|
120
|
+
```
|
121
|
+
|
122
|
+
## Writing Your Jobs
|
123
|
+
|
124
|
+
Your Active Job or Sidekiq Worker must accept the task name and time stamp
|
125
|
+
as arguments. This is so they can be queued properly and so your job can
|
126
|
+
use the scheduled time to know when it was supposed to run vs the current time.
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
class ExampleJob < ActiveJob::Base
|
130
|
+
# @param task_name [String] This is the key used in the YAML file to define the task
|
131
|
+
# @param time [Integer] The epoch time for when the job was scheduled to be run
|
132
|
+
def perform(task_name, time)
|
133
|
+
puts task_name
|
134
|
+
puts Time.at(time)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
When writing your jobs, you need to account for any possible server downtime.
|
140
|
+
The most common downtime would be caused by Heroku's required daily restart.
|
141
|
+
|
142
|
+
To ensure that your tasks always run, the jobs are queued in advance and it's
|
143
|
+
possible the jobs may not be executed at the exact time that you configured
|
144
|
+
them to run. If there is extended downtime, your jobs may back up and there
|
145
|
+
is no guarantee of the order they will be executed when your worker process
|
146
|
+
comes back online.
|
147
|
+
|
148
|
+
Because there is no guarantee that the job is run at the exact time given in
|
149
|
+
the configuration, the time the job was expected to run will be passed to
|
150
|
+
the job so you can handle situations where the time it was run doesn't match
|
151
|
+
the time it was expected to run.
|
152
|
+
|
153
|
+
### How It Works
|
154
|
+
|
155
|
+
Once the rake task is added to Heroku Scheduler, the Simple Scheduler library
|
156
|
+
will load the configuration file every 10 minutes, and ensure that each task
|
157
|
+
has jobs scheduled in the future be checking the `Sidekiq::ScheduledSet`.
|
158
|
+
|
159
|
+
A minimum of two jobs is always added to the scheduled set. By default all
|
160
|
+
jobs for the next six hours are queued in advance. This ensures that there is
|
161
|
+
always one job in the queue that can be used to determine the next run time,
|
162
|
+
even if one of the two was executed during the 10 minute scheduler wait time.
|
163
|
+
|
164
|
+
### Server Downtime Example
|
165
|
+
|
166
|
+
If you're using a gem like [clockwork](https://github.com/Rykian/clockwork), there is no way for the clock process to
|
167
|
+
know that the task was never run. If your task is scheduled for `12:00:00`, your
|
168
|
+
clock process could possibly be restarted at `11:59:59` and your dyno might not
|
169
|
+
be available until `12:00:20`.
|
170
|
+
|
171
|
+
Simple Scheduler would have already enqueued the task hours before the task should actually
|
172
|
+
run, so you still have to worry about the worker dyno restarting, but when the worker
|
173
|
+
dyno becomes available, the enqueued task will be there and will be executed immediately.
|
174
|
+
|
175
|
+
### Daily Digest Email Example
|
176
|
+
|
177
|
+
Here's an example of a daily digest email that needs to go out at 8:00 AM for
|
178
|
+
users in their local time zone. We need to run this every 15 minutes to handle
|
179
|
+
all time zone offsets.
|
180
|
+
|
181
|
+
config/simple_scheduler.yml:
|
182
|
+
|
183
|
+
```yml
|
184
|
+
# Runs every hour starting at the top of the hour + every 15 minutes
|
185
|
+
daily_digest_task:
|
186
|
+
class: "DailyDigestEmailJob"
|
187
|
+
every: "15.minutes"
|
188
|
+
at: "*:00"
|
189
|
+
```
|
190
|
+
|
191
|
+
app/jobs/daily_digest_email_job.rb:
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
class DailyDigestEmailJob < ApplicationJob
|
195
|
+
queue_as :default
|
196
|
+
|
197
|
+
# Called by Simple Scheduler and is given the scheduled time so decisions can be made
|
198
|
+
# based on when the job was scheduled to be run rather than when it was actually run.
|
199
|
+
# @param task_name [String] This is the key used in the YAML file to define the task
|
200
|
+
# @param time [Integer] The epoch time for when the job was scheduled to be run
|
201
|
+
def perform(task_name, time)
|
202
|
+
# Don't do this! This will be way too slow!
|
203
|
+
User.find_each do |user|
|
204
|
+
if user.digest_time == Time.at(time)
|
205
|
+
DigestMailer.daily(user).deliver_later
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
```
|
211
|
+
|
212
|
+
app/models/user.rb:
|
213
|
+
|
214
|
+
```ruby
|
215
|
+
class User < ApplicationRecord
|
216
|
+
# Returns the time the user's daily digest should be
|
217
|
+
# delivered today based on the user's time zone.
|
218
|
+
# @return [Time]
|
219
|
+
def digest_time
|
220
|
+
"8:00 AM".in_time_zone(self.time_zone)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
```
|
224
|
+
|
225
|
+
## Contributing
|
226
|
+
|
227
|
+
1. Fork it
|
228
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
229
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
230
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
231
|
+
5. Create new Pull Request
|
232
|
+
|
233
|
+
## License
|
234
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
begin
|
2
|
+
require "bundler/setup"
|
3
|
+
rescue LoadError
|
4
|
+
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
5
|
+
end
|
6
|
+
|
7
|
+
begin
|
8
|
+
require "rspec/core/rake_task"
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
10
|
+
task default: :spec
|
11
|
+
rescue LoadError
|
12
|
+
puts "Could not load Rspec Rake task"
|
13
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require "active_job"
|
2
|
+
require "sidekiq/api"
|
3
|
+
require_relative "./simple_scheduler/railtie"
|
4
|
+
require_relative "./simple_scheduler/scheduler_job"
|
5
|
+
require_relative "./simple_scheduler/task"
|
6
|
+
require_relative "./simple_scheduler/version"
|
7
|
+
|
8
|
+
# Module for scheduling jobs at specific times using Sidekiq.
|
9
|
+
module SimpleScheduler
|
10
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module SimpleScheduler
|
2
|
+
# Active Job class that queues jobs defined in the config file.
|
3
|
+
class SchedulerJob < ActiveJob::Base
|
4
|
+
# Load the global scheduler config from the YAML file.
|
5
|
+
# @param config_path [String]
|
6
|
+
def load_config(config_path)
|
7
|
+
@config = YAML.load_file(config_path)
|
8
|
+
@queue_ahead = @config["queue_ahead"] || Task::DEFAULT_QUEUE_AHEAD_MINUTES
|
9
|
+
@time_zone = @config["tz"] ? ActiveSupport::TimeZone.new(@config["tz"]) : Time.zone
|
10
|
+
@config.delete("queue_ahead")
|
11
|
+
@config.delete("tz")
|
12
|
+
end
|
13
|
+
|
14
|
+
# Accepts a file path to read the scheduler configuration.
|
15
|
+
# @param config_path [String]
|
16
|
+
def perform(config_path = nil)
|
17
|
+
config_path ||= "config/simple_scheduler.yml"
|
18
|
+
load_config(config_path)
|
19
|
+
queue_future_jobs
|
20
|
+
end
|
21
|
+
|
22
|
+
# Queue each of the future jobs into Sidekiq from the defined tasks.
|
23
|
+
def queue_future_jobs
|
24
|
+
tasks.each do |task|
|
25
|
+
new_run_times = task.future_run_times - task.existing_run_times
|
26
|
+
next if new_run_times.empty?
|
27
|
+
|
28
|
+
if task.job_class.included_modules.include?(Sidekiq::Worker)
|
29
|
+
queue_future_sidekiq_workers(task, new_run_times)
|
30
|
+
else
|
31
|
+
queue_future_active_jobs(task, new_run_times)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# The array of tasks loaded from the config YAML.
|
37
|
+
# @return [Array<SimpleScheduler::SchedulerJob]
|
38
|
+
def tasks
|
39
|
+
@config.map do |task_name, options|
|
40
|
+
task_params = options.symbolize_keys
|
41
|
+
task_params[:queue_ahead] ||= @queue_ahead
|
42
|
+
task_params[:name] = task_name
|
43
|
+
task_params[:tz] ||= @time_zone
|
44
|
+
Task.new(task_params)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Queues jobs in the future using Active Job based on the task options.
|
51
|
+
# @param task [SimpleScheduler::Task]
|
52
|
+
# @param run_times [Array<Time>]
|
53
|
+
def queue_future_active_jobs(task, run_times)
|
54
|
+
run_times.each do |time|
|
55
|
+
task.job_class.set(wait_until: time).perform_later(task.name, time.to_i)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Queues jobs in the future using Sidekiq based on the task options.
|
60
|
+
# @param task [SimpleScheduler::Task]
|
61
|
+
# @param run_times [Array<Time>]
|
62
|
+
def queue_future_sidekiq_workers(task, run_times)
|
63
|
+
run_times.each do |time|
|
64
|
+
task.job_class.perform_at(time, task.name, time.to_i)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module SimpleScheduler
|
2
|
+
# Class for parsing each task in the scheduler config YAML file and returning
|
3
|
+
# the values needed to schedule the task in the future.
|
4
|
+
class Task
|
5
|
+
attr_accessor :at, :frequency, :job_class, :job_class_name, :name, :queue_ahead, :time_zone
|
6
|
+
|
7
|
+
AT_PATTERN = /(Sun|Mon|Tue|Wed|Thu|Fri|Sat)?\s?(?:\*{1,2}|(\d{1,2})):(\d{1,2})/
|
8
|
+
DAYS = %w(Sun Mon Tue Wed Thu Fri Sat).freeze
|
9
|
+
DEFAULT_QUEUE_AHEAD_MINUTES = 360
|
10
|
+
|
11
|
+
# Initializes a task by parsing the params so the task can be queued in the future.
|
12
|
+
# @param params [Hash]
|
13
|
+
# @option params [String] :class The class of the Active Job or Sidekiq Worker
|
14
|
+
# @option params [String] :every How frequently the job will be performed
|
15
|
+
# @option params [String] :at The starting time for the interval
|
16
|
+
# @option params [Integer] :queue_ahead The number of minutes that jobs should be queued in the future
|
17
|
+
# @option params [String] :task_name The name of the task as defined in the YAML config
|
18
|
+
# @option params [ActiveSupport::TimeZone] :tz The time zone to use when parsing the `at` option
|
19
|
+
def initialize(params)
|
20
|
+
validate_params!(params)
|
21
|
+
@at = params[:at]
|
22
|
+
@frequency = parse_frequency(params[:every])
|
23
|
+
@job_class_name = params[:class]
|
24
|
+
@job_class = @job_class_name.constantize
|
25
|
+
@queue_ahead = params[:queue_ahead] || DEFAULT_QUEUE_AHEAD_MINUTES
|
26
|
+
@name = params[:name] || @job_class_name
|
27
|
+
@time_zone = params[:tz] || Time.zone
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns an array of existing jobs matching the job class of the task.
|
31
|
+
# @return [Array<Sidekiq::SortedEntry>]
|
32
|
+
def existing_jobs
|
33
|
+
@existing_jobs ||= SimpleScheduler::Task.scheduled_set.select do |job|
|
34
|
+
job.display_class == @job_class_name && job.display_args[0] == @name
|
35
|
+
end.to_a
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns an array of existing future run times that have already been scheduled.
|
39
|
+
# @return [Array<Time>]
|
40
|
+
def existing_run_times
|
41
|
+
@existing_run_times ||= existing_jobs.map(&:at)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the very first time a job should be run for the scheduled task.
|
45
|
+
# @return [Time]
|
46
|
+
def first_run_time
|
47
|
+
first_run_time = first_run_day
|
48
|
+
change_hour = first_run_hour
|
49
|
+
change_hour += 1 if at_match[2].nil? && first_run_hour == now.hour && first_run_min < now.min
|
50
|
+
first_run_time = first_run_time.change(hour: change_hour, min: first_run_min)
|
51
|
+
first_run_time += at_match[1] ? 1.week : 1.day if now > first_run_time
|
52
|
+
first_run_time
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns an array Time objects for future run times based on
|
56
|
+
# the current time and the given minutes to look ahead.
|
57
|
+
# @return [Array<Time>]
|
58
|
+
def future_run_times
|
59
|
+
future_run_times = existing_run_times.dup
|
60
|
+
last_run_time = future_run_times.last || first_run_time - frequency
|
61
|
+
last_run_time = last_run_time.in_time_zone(@time_zone)
|
62
|
+
|
63
|
+
while future_run_times.length < 2 || ((last_run_time - now) / 1.minute) < @queue_ahead
|
64
|
+
last_run_time = frequency.from_now(last_run_time)
|
65
|
+
last_run_time = last_run_time.change(hour: first_run_hour, min: first_run_min) if at_match[2]
|
66
|
+
future_run_times << last_run_time
|
67
|
+
end
|
68
|
+
|
69
|
+
future_run_times
|
70
|
+
end
|
71
|
+
|
72
|
+
# Loads the scheduled jobs from Sidekiq once to avoid loading from
|
73
|
+
# Redis for each task when looking up existing scheduled jobs.
|
74
|
+
# @return [Sidekiq::ScheduledSet]
|
75
|
+
def self.scheduled_set
|
76
|
+
@scheduled_set ||= Sidekiq::ScheduledSet.new
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def at_match
|
82
|
+
@at_match ||= AT_PATTERN.match(@at) || []
|
83
|
+
end
|
84
|
+
|
85
|
+
def first_run_day
|
86
|
+
return @first_run_day if @first_run_day
|
87
|
+
|
88
|
+
@first_run_day = now.beginning_of_day
|
89
|
+
|
90
|
+
# If no day of the week is given, return today
|
91
|
+
return @first_run_day unless first_run_wday
|
92
|
+
|
93
|
+
# Shift to the correct day of the week if given
|
94
|
+
add_days = first_run_wday - first_run_day.wday
|
95
|
+
add_days += 7 if first_run_day.wday > first_run_wday
|
96
|
+
@first_run_day += add_days.days
|
97
|
+
end
|
98
|
+
|
99
|
+
def first_run_hour
|
100
|
+
@first_run_hour ||= (at_match[2] || now.hour).to_i
|
101
|
+
end
|
102
|
+
|
103
|
+
def first_run_min
|
104
|
+
@first_run_min ||= (at_match[3] || now.min).to_i
|
105
|
+
end
|
106
|
+
|
107
|
+
def first_run_wday
|
108
|
+
@first_run_wday ||= DAYS.index(at_match[1])
|
109
|
+
end
|
110
|
+
|
111
|
+
def now
|
112
|
+
@now ||= @time_zone.now.beginning_of_minute
|
113
|
+
end
|
114
|
+
|
115
|
+
def parse_frequency(every_string)
|
116
|
+
split_duration = every_string.split(".")
|
117
|
+
frequency = split_duration[0].to_i
|
118
|
+
frequency_units = split_duration[1]
|
119
|
+
frequency.send(frequency_units)
|
120
|
+
end
|
121
|
+
|
122
|
+
def validate_params!(params)
|
123
|
+
raise ArgumentError, "Missing param `class` specifying the class of the job to run." unless params.key?(:class)
|
124
|
+
raise ArgumentError, "Missing param `every` specifying how often the job should run." unless params.key?(:every)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
metadata
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: simple_scheduler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brian Pattison
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-12-07 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: '4.2'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.2'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: sidekiq
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.2'
|
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: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
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
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: simplecov
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov-rcov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: timecop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: |2
|
112
|
+
Simple Scheduler adds the ability to enhance Heroku Scheduler by using Sidekiq to queue
|
113
|
+
jobs in the future. This allows for defining specific run times (Ex: Every Sunday at 4 AM)
|
114
|
+
and running tasks more often than Heroku Scheduler's 10 minute limit.
|
115
|
+
email:
|
116
|
+
- brian@brianpattison.com
|
117
|
+
executables: []
|
118
|
+
extensions: []
|
119
|
+
extra_rdoc_files: []
|
120
|
+
files:
|
121
|
+
- lib/simple_scheduler/railtie.rb
|
122
|
+
- lib/simple_scheduler/scheduler_job.rb
|
123
|
+
- lib/simple_scheduler/task.rb
|
124
|
+
- lib/simple_scheduler/version.rb
|
125
|
+
- lib/simple_scheduler.rb
|
126
|
+
- lib/tasks/simple_scheduler_tasks.rake
|
127
|
+
- MIT-LICENSE
|
128
|
+
- Rakefile
|
129
|
+
- README.md
|
130
|
+
homepage: https://github.com/simplymadeapps/simple_scheduler
|
131
|
+
licenses:
|
132
|
+
- MIT
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - '>='
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 2.0.14.1
|
151
|
+
signing_key:
|
152
|
+
specification_version: 4
|
153
|
+
summary: An enhancement for Heroku Scheduler + Sidekiq for scheduling jobs at specific
|
154
|
+
times.
|
155
|
+
test_files: []
|