say_when 1.0.0 → 2.0.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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +5 -0
- data/Guardfile +50 -0
- data/README.md +135 -2
- data/Rakefile +1 -0
- data/lib/say_when.rb +33 -18
- data/lib/say_when/configuration.rb +16 -0
- data/lib/say_when/cron_expression.rb +19 -21
- data/lib/say_when/poller/base_poller.rb +108 -0
- data/lib/say_when/poller/celluloid_poller.rb +30 -0
- data/lib/say_when/poller/concurrent_poller.rb +31 -0
- data/lib/say_when/poller/simple_poller.rb +37 -0
- data/lib/say_when/processor/active_job_strategy.rb +35 -0
- data/lib/say_when/processor/simple_strategy.rb +13 -0
- data/lib/say_when/processor/test_strategy.rb +21 -0
- data/lib/say_when/scheduler.rb +67 -101
- data/lib/say_when/storage/active_record_strategy.rb +204 -0
- data/lib/say_when/storage/base_job.rb +96 -0
- data/lib/say_when/storage/memory_strategy.rb +140 -0
- data/lib/say_when/tasks.rb +15 -3
- data/lib/say_when/triggers/base.rb +3 -3
- data/lib/say_when/triggers/cron_strategy.rb +2 -3
- data/lib/say_when/triggers/instance_strategy.rb +3 -4
- data/lib/say_when/triggers/once_strategy.rb +3 -4
- data/lib/say_when/utils.rb +16 -0
- data/lib/say_when/version.rb +1 -1
- data/say_when.gemspec +10 -5
- data/test/minitest_helper.rb +45 -15
- data/test/say_when/configuration_test.rb +14 -0
- data/test/say_when/cron_expression_test.rb +140 -0
- data/test/say_when/poller/base_poller_test.rb +42 -0
- data/test/say_when/poller/celluloid_poller_test.rb +17 -0
- data/test/say_when/poller/concurrent_poller_test.rb +19 -0
- data/test/say_when/poller/simple_poller_test.rb +27 -0
- data/test/say_when/processor/active_job_strategy_test.rb +31 -0
- data/test/say_when/processor/simple_strategy_test.rb +15 -0
- data/test/say_when/scheduler_test.rb +41 -57
- data/test/say_when/storage/active_record_strategy_test.rb +134 -0
- data/test/say_when/storage/memory_strategy_test.rb +96 -0
- data/test/say_when/triggers/cron_strategy_test.rb +11 -0
- data/test/say_when/triggers/instance_strategy_test.rb +13 -0
- data/test/say_when/triggers/once_strategy_test.rb +2 -2
- data/test/say_when_test.rb +20 -0
- metadata +110 -36
- data/lib/say_when/base_job.rb +0 -96
- data/lib/say_when/processor/active_messaging.rb +0 -21
- data/lib/say_when/processor/base.rb +0 -19
- data/lib/say_when/processor/shoryuken.rb +0 -14
- data/lib/say_when/processor/simple.rb +0 -17
- data/lib/say_when/storage/active_record/acts.rb +0 -92
- data/lib/say_when/storage/active_record/job.rb +0 -100
- data/lib/say_when/storage/active_record/job_execution.rb +0 -14
- data/lib/say_when/storage/memory/base.rb +0 -36
- data/lib/say_when/storage/memory/job.rb +0 -53
- data/test/say_when/cron_expression_spec.rb +0 -74
- data/test/say_when/processor/active_messaging_test.rb +0 -41
- data/test/say_when/storage/active_record/job_test.rb +0 -90
- data/test/say_when/storage/memory/job_test.rb +0 -32
- data/test/say_when/storage/memory/trigger_test.rb +0 -54
- data/test/support/models.rb +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92b39148a95c13f3b9fc5d55d56ef49d3566576f
|
4
|
+
data.tar.gz: d30b25ecd756ae2eb187a70c7511578fb0ddfc74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62ac59b8bd6d443cde57281aa883e3aaedba830737c4c9ea296914fd1c032628c9d316b5b9f920895bcc226694ab9e1568eb03dee8a759533cca671cb6afac3f
|
7
|
+
data.tar.gz: 0a30ee197ca109d14c57377f1c55c48d9e8f3b337a7a062dab0c93adf70e508e942e825420cbde5eb8b6fee2ec61a7a174ebb58be93081a55d8644dc5ec13156
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
## Uncomment and set this to only include directories you want to watch
|
5
|
+
# directories %w(app lib config test spec features)
|
6
|
+
|
7
|
+
## Uncomment to clear the screen before every task
|
8
|
+
# clearing :on
|
9
|
+
|
10
|
+
## Guard internally checks for changes in the Guardfile and exits.
|
11
|
+
## If you want Guard to automatically start up again, run guard in a
|
12
|
+
## shell loop, e.g.:
|
13
|
+
##
|
14
|
+
## $ while bundle exec guard; do echo "Restarting Guard..."; done
|
15
|
+
##
|
16
|
+
## Note: if you are using the `directories` clause above and you are not
|
17
|
+
## watching the project directory ('.'), then you will want to move
|
18
|
+
## the Guardfile to a watched dir and symlink it back, e.g.
|
19
|
+
#
|
20
|
+
# $ mkdir config
|
21
|
+
# $ mv Guardfile config/
|
22
|
+
# $ ln -s config/Guardfile .
|
23
|
+
#
|
24
|
+
# and, you'll have to watch "config/Guardfile" instead of "Guardfile"
|
25
|
+
|
26
|
+
guard :minitest do
|
27
|
+
# with Minitest::Unit
|
28
|
+
watch(%r{^test/(.*)\/?(.*)_test\.rb$})
|
29
|
+
watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb" }
|
30
|
+
watch(%r{^test/minitest_helper\.rb$}) { 'test' }
|
31
|
+
|
32
|
+
# with Minitest::Spec
|
33
|
+
# watch(%r{^spec/(.*)_spec\.rb$})
|
34
|
+
# watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
35
|
+
# watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
|
36
|
+
|
37
|
+
# Rails 4
|
38
|
+
# watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
|
39
|
+
# watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' }
|
40
|
+
# watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{m[1]}_test.rb" }
|
41
|
+
# watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" }
|
42
|
+
# watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[1]}_test.rb" }
|
43
|
+
# watch(%r{^test/.+_test\.rb$})
|
44
|
+
# watch(%r{^test/test_helper\.rb$}) { 'test' }
|
45
|
+
|
46
|
+
# Rails < 4
|
47
|
+
# watch(%r{^app/controllers/(.*)\.rb$}) { |m| "test/functional/#{m[1]}_test.rb" }
|
48
|
+
# watch(%r{^app/helpers/(.*)\.rb$}) { |m| "test/helpers/#{m[1]}_test.rb" }
|
49
|
+
# watch(%r{^app/models/(.*)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
|
50
|
+
end
|
data/README.md
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
# SayWhen
|
2
2
|
|
3
|
-
|
3
|
+
SayWhen is a job scheduling library for use in any project, but with a few extra hooks for rails projects.
|
4
|
+
It was roughly inspired by the [Quartz scheduler](http://quartz-scheduler.org/).
|
5
|
+
|
6
|
+
You add it to a ruby program (optionally configure it) and then schedule jobs using a few different strategies, with cron-like expressions the most powerful.
|
7
|
+
|
8
|
+
When scheduling, you specify a trigger which controls when execution will occur, such as a cron trigger, or an execute only once trigger, and a job, which is the actual work to perform.
|
9
|
+
|
10
|
+
The cron triggers are based on the [extended cron capabilities](http://wiki.opensymphony.com/display/QRTZ1/CronTriggers+Tutorial).
|
11
|
+
|
12
|
+
Jobs can be stored different ways, either in memory (e.g. loaded on start from a ruby file), or saved to a database.
|
13
|
+
|
14
|
+
The scheduler can execute the jobs in different ways, either by loading and running them itself synchronously, or by delegating the processing to ActiveJob.
|
15
|
+
|
16
|
+
SayWhen can be run either in its own process, or can run as a supervised actor in a Celluloid process (e.g. sidekiq or shoryuken).
|
4
17
|
|
5
18
|
## Installation
|
6
19
|
|
@@ -18,9 +31,129 @@ Or install it yourself as:
|
|
18
31
|
|
19
32
|
$ gem install say_when
|
20
33
|
|
34
|
+
## Configuration
|
35
|
+
|
36
|
+
To use, first configure how jobs are stored and processed.
|
37
|
+
Be default, they are in memory, and processed synchronously, but that can be configured to behave differently.
|
38
|
+
|
39
|
+
The currently available storage options are:
|
40
|
+
- Memory (default) - usually jobs are initialized in code on load
|
41
|
+
- ActiveRecord - stores the scheduled jobs and can log execution information to database tables
|
42
|
+
|
43
|
+
The processor options are:
|
44
|
+
- Simple - the scheduler process executes the job itself synchronously
|
45
|
+
- ActiveJob - delegates the work to an ActiveJob async call
|
46
|
+
- Test - stubs out processing, useful for testing only
|
47
|
+
|
48
|
+
You also have some options for running SayWhen:
|
49
|
+
- SimplePoller - just a simple looping process, can be started with rake
|
50
|
+
- CelluloidPoller - defines a Celluloid Actor appropriate for adding to a running Celluloid process, such as from Shoryuken
|
51
|
+
|
52
|
+
Finally, there are options for triggers that determine when jobs run:
|
53
|
+
- Cron expression
|
54
|
+
- Once
|
55
|
+
- Instance
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
# config/intializers/say_when.rb
|
59
|
+
|
60
|
+
require 'say_when'
|
61
|
+
|
62
|
+
# you can specify a the logger
|
63
|
+
SayWhen.logger = Rails.logger
|
64
|
+
|
65
|
+
# configure the scheduler for how to store and process scheduled jobs
|
66
|
+
# it will default to a :memory strategy and :simple processor
|
67
|
+
SayWhen.configure do |options|
|
68
|
+
options[:storage_strategy] = :active_record
|
69
|
+
options[:processor_strategy] = :simple
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
21
73
|
## Usage
|
22
74
|
|
23
|
-
|
75
|
+
### Basics
|
76
|
+
There is a very verbose way to create a scheduled job, setting the options for the trigger and job explicitly, and then some helper methods that make this easier for common cases.
|
77
|
+
|
78
|
+
Here is an example of the verbose way of scheduling a job, which is a good place to start:
|
79
|
+
```ruby
|
80
|
+
|
81
|
+
job = SayWhen::Scheduler.schedule(
|
82
|
+
trigger_strategy: 'once',
|
83
|
+
trigger_options: { at: 10.second.since },
|
84
|
+
job_class: SomeTask,
|
85
|
+
job_method: 'execute',
|
86
|
+
data: { id: 123 }
|
87
|
+
)
|
88
|
+
```
|
89
|
+
|
90
|
+
There are also convenience methods on the `Scheduler`:
|
91
|
+
```ruby
|
92
|
+
|
93
|
+
```
|
94
|
+
## ActiveRecord integration
|
95
|
+
|
96
|
+
Besides storing jobs in ActiveRecord, you can also associate jobs with other models.
|
97
|
+
|
98
|
+
There is an `acts_as_scheduled` method you can call in an ActiveRecord class for this purpose.
|
99
|
+
It both makes it easier to schedule a job, and to see manage the list of related jobs.
|
100
|
+
|
101
|
+
For example, you might create a job to send a reminder a week after a user is created, and relate this new job to that user.
|
102
|
+
By associating it with the `ActiveRecord` object, you can more easily manage this reminder, such as canceling it if they close their account.
|
103
|
+
|
104
|
+
When using `ActiveRecord` integration in Rails, there is a generator for the migration to create the tables for saving scheduled jobs:
|
105
|
+
```
|
106
|
+
bundle exec rails generate say_when:migration
|
107
|
+
```
|
108
|
+
|
109
|
+
The resulting migration assumes the scheduled jobs will use a integer based id column, please update the default migration if this is not the case in your system:
|
110
|
+
```ruby
|
111
|
+
# change this to string or other type as needed
|
112
|
+
t.integer :scheduled_id
|
113
|
+
```
|
114
|
+
|
115
|
+
## Pollers
|
116
|
+
|
117
|
+
The `SimplePoller` does what you would expect; when you run it, it starts up a loop of checking for jobs to run, sleeping, and then checking again.
|
118
|
+
|
119
|
+
It can be executed from the rake task:
|
120
|
+
```
|
121
|
+
bundle exec rake say_when:start
|
122
|
+
```
|
123
|
+
|
124
|
+
But that isn't doing much except loading the environment then this:
|
125
|
+
```ruby
|
126
|
+
require 'say_when'
|
127
|
+
require 'say_when/poller/simple_poller'
|
128
|
+
|
129
|
+
SayWhen::Poller::SimplePoller.start
|
130
|
+
```
|
131
|
+
|
132
|
+
For my own purposes, I use things like `daemontools` and `god` to daemonize, so this has been enough for me, but it would not be hard to write a command line script for it.
|
133
|
+
|
134
|
+
Most of the time I am also running a job processor, either `shoryuken` or `sidekiq`, and I would prefer to piggyback on that same process instead of starting up another. Since both of those use Celluloid (or did, Sidekiq no longer does), I also created a Celluloid actor class that can be added to the celluloid based job process via hooks in their startup.
|
135
|
+
|
136
|
+
For Shoryuken, add this to your initializer (probably `config/initializers/shoryuken.rb`):
|
137
|
+
```ruby
|
138
|
+
require 'say_when/poller/celluloid_poller'
|
139
|
+
|
140
|
+
Shoryuken.on_start do
|
141
|
+
# check for new jobs to run every 5 seconds
|
142
|
+
SayWhen::Poller::CelluloidPoller.supervise_as :say_when, 5
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
For Sidekiq, there is a slightly different syntax, but basically the same idea
|
147
|
+
(via https://github.com/mperham/sidekiq/wiki/Deployment#events):
|
148
|
+
```ruby
|
149
|
+
require 'say_when/poller/celluloid_poller'
|
150
|
+
|
151
|
+
Sidekiq.configure_server do |config|
|
152
|
+
config.on(:startup) do
|
153
|
+
SayWhen::Poller::CelluloidPoller.supervise_as :say_when, 5
|
154
|
+
end
|
155
|
+
end
|
156
|
+
```
|
24
157
|
|
25
158
|
## Contributing
|
26
159
|
|
data/Rakefile
CHANGED
data/lib/say_when.rb
CHANGED
@@ -1,32 +1,47 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require 'active_support'
|
3
|
+
require 'active_support/all'
|
4
4
|
|
5
|
-
require
|
6
|
-
require 'say_when/
|
5
|
+
require 'say_when/version'
|
6
|
+
require 'say_when/configuration'
|
7
|
+
require 'say_when/utils'
|
7
8
|
require 'say_when/cron_expression'
|
8
|
-
require 'say_when/processor/base'
|
9
|
-
require 'say_when/processor/simple'
|
10
9
|
require 'say_when/scheduler'
|
11
10
|
|
12
|
-
require 'say_when/processor/active_messaging' if defined?(::ActiveMessaging)
|
13
|
-
require 'say_when/processor/shoryuken' if defined?(::Shoryuken)
|
14
|
-
require 'say_when/storage/active_record/job' if defined?(::ActiveRecord)
|
15
11
|
require 'say_when/railtie' if defined?(Rails)
|
16
12
|
|
17
13
|
module SayWhen
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
if defined?(Rails.logger) && Rails.logger
|
25
|
-
@@logger = Rails.logger
|
14
|
+
class << self
|
15
|
+
def logger
|
16
|
+
@logger ||= if defined?(Rails)
|
17
|
+
Rails.logger
|
18
|
+
else
|
19
|
+
Logger.new(STDOUT)
|
26
20
|
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def logger=(l)
|
24
|
+
@logger = l
|
25
|
+
end
|
26
|
+
|
27
|
+
def options
|
28
|
+
@options ||= SayWhen::Configuration.default_options
|
29
|
+
end
|
30
|
+
|
31
|
+
def configure(opts = {})
|
32
|
+
@lock ||= Mutex.new
|
33
|
+
options.merge(opts)
|
34
|
+
yield options if block_given?
|
35
|
+
end
|
36
|
+
|
37
|
+
def scheduler
|
38
|
+
return @scheduler if defined?(@scheduler) && @scheduler
|
39
|
+
@lock.synchronize { @scheduler = SayWhen::Scheduler.new }
|
40
|
+
@scheduler
|
41
|
+
end
|
27
42
|
|
28
|
-
|
43
|
+
def schedule(job)
|
44
|
+
scheduler.schedule(job)
|
29
45
|
end
|
30
|
-
@@logger
|
31
46
|
end
|
32
47
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
module SayWhen
|
5
|
+
class Configuration
|
6
|
+
def self.default_options
|
7
|
+
{}.tap do |defaults|
|
8
|
+
defaults[:processor_strategy] = :simple
|
9
|
+
defaults[:storage_strategy] = :memory
|
10
|
+
defaults[:tick_length] = (ENV['SAY_WHEN_TICK_LENGTH'] || '5').to_i
|
11
|
+
defaults[:queue] = ENV['SAY_WHEN_QUEUE'] || 'default'
|
12
|
+
defaults[:reset_acquired_length] = (ENV['SAY_WHEN_RESET_ACQUIRED_LENGTH'] || '3600').to_i
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -3,14 +3,13 @@
|
|
3
3
|
require 'date'
|
4
4
|
|
5
5
|
module SayWhen
|
6
|
-
|
7
6
|
# Based on the extended cron capabilties
|
8
|
-
# http://
|
7
|
+
# http://www.quartz-scheduler.org/documentation/quartz-2.2.x/tutorials/tutorial-lesson-06.html
|
9
8
|
class CronExpression
|
10
9
|
attr_reader :expression
|
11
10
|
attr_accessor :time_zone, :seconds, :minutes, :hours, :days_of_month, :months, :days_of_week, :years
|
12
11
|
|
13
|
-
def initialize(expression, time_zone=nil)
|
12
|
+
def initialize(expression = {}, time_zone = nil)
|
14
13
|
if expression.is_a?(Hash)
|
15
14
|
opts = expression
|
16
15
|
|
@@ -28,19 +27,14 @@ module SayWhen
|
|
28
27
|
"#{opts[:seconds]} #{opts[:minutes]} #{opts[:hours]} #{opts[:days_of_month]} #{opts[:months]} #{opts[:days_of_week]} #{opts[:years]}"
|
29
28
|
end
|
30
29
|
|
31
|
-
|
32
|
-
opts[:time_zone]
|
33
|
-
else
|
34
|
-
Time.zone.nil? ? "UTC" : Time.zone.name
|
35
|
-
end
|
36
|
-
|
30
|
+
@time_zone = opts[:time_zone]
|
37
31
|
else
|
38
32
|
@expression = expression
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
33
|
+
end
|
34
|
+
|
35
|
+
@time_zone ||= time_zone
|
36
|
+
if @time_zone.blank?
|
37
|
+
@time_zone = Time.zone.try(:name) || "UTC"
|
44
38
|
end
|
45
39
|
|
46
40
|
parse
|
@@ -122,7 +116,6 @@ module SayWhen
|
|
122
116
|
[before, false]
|
123
117
|
end
|
124
118
|
|
125
|
-
|
126
119
|
end
|
127
120
|
|
128
121
|
class CronValue
|
@@ -154,19 +147,24 @@ module SayWhen
|
|
154
147
|
values = []
|
155
148
|
case val
|
156
149
|
#check for a '/' for increments
|
157
|
-
when /(
|
150
|
+
when /(.+)\/(\d+)/
|
151
|
+
(( $1 == "*") ? min : $1.to_i).step(max, $2.to_i) { |x| values << x }
|
158
152
|
|
159
153
|
#check for ',' for list of values
|
160
|
-
when /(\d+)(,\d+)+/
|
154
|
+
when /(\d+)(,\d+)+/
|
155
|
+
values = val.split(',').map{ |v| v.to_i }.sort
|
161
156
|
|
162
157
|
#check for '-' for range of values
|
163
|
-
when /(\d+)-(\d+)/
|
158
|
+
when /(\d+)-(\d+)/
|
159
|
+
values = (($1.to_i)..($2.to_i)).to_a
|
164
160
|
|
165
161
|
#check for '*' for all values between min and max
|
166
|
-
when /^(\*)$/
|
162
|
+
when /^(\*)$/
|
163
|
+
values = (min..max).to_a
|
167
164
|
|
168
165
|
#lastly, should just be a number
|
169
|
-
when /^(\d+)$/
|
166
|
+
when /^(\d+)$/
|
167
|
+
values << $1.to_i
|
170
168
|
|
171
169
|
#if nothing else, leave values as []
|
172
170
|
else values = []
|
@@ -535,7 +533,7 @@ module SayWhen
|
|
535
533
|
|
536
534
|
class YearsCronValue < CronValue
|
537
535
|
def initialize(exp)
|
538
|
-
super(:year, 1970,
|
536
|
+
super(:year, 1970, (Date.today.year + 100), exp)
|
539
537
|
end
|
540
538
|
|
541
539
|
def next(date)
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'say_when/utils'
|
4
|
+
|
5
|
+
module SayWhen
|
6
|
+
module Poller
|
7
|
+
module BasePoller
|
8
|
+
def self.included(mod)
|
9
|
+
mod.include(SayWhen::Utils)
|
10
|
+
attr_accessor :reset_next_at
|
11
|
+
end
|
12
|
+
|
13
|
+
def stop
|
14
|
+
end
|
15
|
+
|
16
|
+
def start
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset_acquired
|
20
|
+
time_now = Time.now
|
21
|
+
self.reset_next_at ||= time_now
|
22
|
+
|
23
|
+
if reset_acquired_length > 0 && reset_next_at <= time_now
|
24
|
+
self.reset_next_at = time_now + reset_acquired_length
|
25
|
+
logger.debug "SayWhen:: reset acquired at #{time_now}, try again at #{reset_next_at}"
|
26
|
+
storage.reset_acquired(reset_acquired_length)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def job_error(msg, job, ex)
|
31
|
+
job_msg = job && " job:'#{job.inspect}'"
|
32
|
+
logger.error "#{self.class.name} #{msg}#{job_msg}: #{ex.message}\n\t#{ex.backtrace.join("\t\n")}"
|
33
|
+
release(job)
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_jobs
|
37
|
+
reset_acquired
|
38
|
+
time_now = Time.now
|
39
|
+
while job = acquire(time_now)
|
40
|
+
process(job, time_now)
|
41
|
+
time_now = Time.now
|
42
|
+
end
|
43
|
+
rescue StandardError => ex
|
44
|
+
job_error("Error!", job, ex)
|
45
|
+
tick(error_tick_length)
|
46
|
+
rescue Interrupt => ex
|
47
|
+
job_error("Interrupt!", job, ex)
|
48
|
+
raise ex
|
49
|
+
rescue Exception => ex
|
50
|
+
job_error("Exception!", job, ex)
|
51
|
+
raise ex
|
52
|
+
end
|
53
|
+
|
54
|
+
def acquire(time_now)
|
55
|
+
logger.debug "SayWhen:: Looking for job that should be ready to fire before #{time_now}"
|
56
|
+
if job = self.storage.acquire_next(time_now)
|
57
|
+
logger.debug "SayWhen:: got a job: #{job.inspect}"
|
58
|
+
else
|
59
|
+
logger.debug "SayWhen:: no jobs to acquire"
|
60
|
+
end
|
61
|
+
job
|
62
|
+
end
|
63
|
+
|
64
|
+
def process(job, time_now)
|
65
|
+
# delegate processing the trigger to the processor
|
66
|
+
processor.process(job)
|
67
|
+
logger.debug "SayWhen:: job processed: #{job.inspect}"
|
68
|
+
|
69
|
+
# this should update next fire at, and put back in list of scheduled jobs
|
70
|
+
storage.fired(job, time_now)
|
71
|
+
logger.debug "SayWhen:: job fired: #{job.inspect}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def release(job)
|
75
|
+
logger.info "SayWhen::Scheduler release: #{job.inspect}"
|
76
|
+
job.release if job
|
77
|
+
end
|
78
|
+
|
79
|
+
def tick(t = tick_length)
|
80
|
+
sleep(t.to_f)
|
81
|
+
end
|
82
|
+
|
83
|
+
def tick_length
|
84
|
+
@tick_length ||= SayWhen.options[:tick_length].to_f
|
85
|
+
end
|
86
|
+
|
87
|
+
def error_tick_length
|
88
|
+
@error_tick_length ||= SayWhen.options[:error_tick_length].to_f || tick_length
|
89
|
+
end
|
90
|
+
|
91
|
+
def reset_acquired_length
|
92
|
+
@reset_acquired_length ||= SayWhen.options[:reset_acquired_length].to_f
|
93
|
+
end
|
94
|
+
|
95
|
+
def processor
|
96
|
+
@processor ||= load_strategy(:processor, SayWhen.options[:processor_strategy])
|
97
|
+
end
|
98
|
+
|
99
|
+
def storage
|
100
|
+
@storage ||= load_strategy(:storage, SayWhen.options[:storage_strategy])
|
101
|
+
end
|
102
|
+
|
103
|
+
def logger
|
104
|
+
SayWhen.logger
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|