say_when 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +5 -0
  4. data/Guardfile +50 -0
  5. data/README.md +135 -2
  6. data/Rakefile +1 -0
  7. data/lib/say_when.rb +33 -18
  8. data/lib/say_when/configuration.rb +16 -0
  9. data/lib/say_when/cron_expression.rb +19 -21
  10. data/lib/say_when/poller/base_poller.rb +108 -0
  11. data/lib/say_when/poller/celluloid_poller.rb +30 -0
  12. data/lib/say_when/poller/concurrent_poller.rb +31 -0
  13. data/lib/say_when/poller/simple_poller.rb +37 -0
  14. data/lib/say_when/processor/active_job_strategy.rb +35 -0
  15. data/lib/say_when/processor/simple_strategy.rb +13 -0
  16. data/lib/say_when/processor/test_strategy.rb +21 -0
  17. data/lib/say_when/scheduler.rb +67 -101
  18. data/lib/say_when/storage/active_record_strategy.rb +204 -0
  19. data/lib/say_when/storage/base_job.rb +96 -0
  20. data/lib/say_when/storage/memory_strategy.rb +140 -0
  21. data/lib/say_when/tasks.rb +15 -3
  22. data/lib/say_when/triggers/base.rb +3 -3
  23. data/lib/say_when/triggers/cron_strategy.rb +2 -3
  24. data/lib/say_when/triggers/instance_strategy.rb +3 -4
  25. data/lib/say_when/triggers/once_strategy.rb +3 -4
  26. data/lib/say_when/utils.rb +16 -0
  27. data/lib/say_when/version.rb +1 -1
  28. data/say_when.gemspec +10 -5
  29. data/test/minitest_helper.rb +45 -15
  30. data/test/say_when/configuration_test.rb +14 -0
  31. data/test/say_when/cron_expression_test.rb +140 -0
  32. data/test/say_when/poller/base_poller_test.rb +42 -0
  33. data/test/say_when/poller/celluloid_poller_test.rb +17 -0
  34. data/test/say_when/poller/concurrent_poller_test.rb +19 -0
  35. data/test/say_when/poller/simple_poller_test.rb +27 -0
  36. data/test/say_when/processor/active_job_strategy_test.rb +31 -0
  37. data/test/say_when/processor/simple_strategy_test.rb +15 -0
  38. data/test/say_when/scheduler_test.rb +41 -57
  39. data/test/say_when/storage/active_record_strategy_test.rb +134 -0
  40. data/test/say_when/storage/memory_strategy_test.rb +96 -0
  41. data/test/say_when/triggers/cron_strategy_test.rb +11 -0
  42. data/test/say_when/triggers/instance_strategy_test.rb +13 -0
  43. data/test/say_when/triggers/once_strategy_test.rb +2 -2
  44. data/test/say_when_test.rb +20 -0
  45. metadata +110 -36
  46. data/lib/say_when/base_job.rb +0 -96
  47. data/lib/say_when/processor/active_messaging.rb +0 -21
  48. data/lib/say_when/processor/base.rb +0 -19
  49. data/lib/say_when/processor/shoryuken.rb +0 -14
  50. data/lib/say_when/processor/simple.rb +0 -17
  51. data/lib/say_when/storage/active_record/acts.rb +0 -92
  52. data/lib/say_when/storage/active_record/job.rb +0 -100
  53. data/lib/say_when/storage/active_record/job_execution.rb +0 -14
  54. data/lib/say_when/storage/memory/base.rb +0 -36
  55. data/lib/say_when/storage/memory/job.rb +0 -53
  56. data/test/say_when/cron_expression_spec.rb +0 -74
  57. data/test/say_when/processor/active_messaging_test.rb +0 -41
  58. data/test/say_when/storage/active_record/job_test.rb +0 -90
  59. data/test/say_when/storage/memory/job_test.rb +0 -32
  60. data/test/say_when/storage/memory/trigger_test.rb +0 -54
  61. data/test/support/models.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5f6591f3f5206b517960a08efa500fbf3334ff56
4
- data.tar.gz: 4daa2a598bf163817da9b7987938cc7216ed1f58
3
+ metadata.gz: 92b39148a95c13f3b9fc5d55d56ef49d3566576f
4
+ data.tar.gz: d30b25ecd756ae2eb187a70c7511578fb0ddfc74
5
5
  SHA512:
6
- metadata.gz: 236e345d76fb90f946e90a6d7ed8581bf670823b36965da9110e680f39847656c309e00ecc77d931e113a27648bc29bc37f72223f9a32c94f45a240c64fbff47
7
- data.tar.gz: 3eb871a517e9e1c7246d7b52957b423c56fc58d47127f1523d2383a6f605314901684ad2a21c70bc3bd620964f6346403d3bf9e10ec70a79a9192a6cb55fda13
6
+ metadata.gz: 62ac59b8bd6d443cde57281aa883e3aaedba830737c4c9ea296914fd1c032628c9d316b5b9f920895bcc226694ab9e1568eb03dee8a759533cca671cb6afac3f
7
+ data.tar.gz: 0a30ee197ca109d14c57377f1c55c48d9e8f3b337a7a062dab0c93adf70e508e942e825420cbde5eb8b6fee2ec61a7a174ebb58be93081a55d8644dc5ec13156
data/.gitignore CHANGED
@@ -14,3 +14,4 @@
14
14
  *.DS_Store
15
15
  .ruby-version
16
16
  test/db/test.db
17
+ notes.md
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - "2.2.4"
5
+ - "2.3.1"
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
- TODO: Write a gem description
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
- TODO: Write usage instructions here
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
@@ -1,5 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
+ require 'say_when/tasks'
3
4
 
4
5
  Rake::TestTask.new(:test) do |t|
5
6
  t.libs << 'test'
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 "say_when/version"
6
- require 'say_when/base_job'
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
- def SayWhen.logger=(logger)
19
- @@logger = logger
20
- end
21
-
22
- def SayWhen.logger
23
- if !defined?(@@logger) || !@@logger
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
- @@logger = Logger.new(STDOUT) unless defined?(@@logger)
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://wiki.opensymphony.com/display/QRTZ1/CronTriggers+Tutorial
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
- self.time_zone = if opts.has_key?(:time_zone) && !opts[:time_zone].blank?
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
- self.time_zone = if time_zone.blank?
40
- Time.zone.nil? ? "UTC" : Time.zone.name
41
- else
42
- time_zone
43
- end
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 /(\w+)\/(\d+)/ then (( $1 == "*") ? min : $1.to_i).step(max, $2.to_i) { |x| values << x }
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+)+/ then values = val.split(',').map{ |v| v.to_i }.sort
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+)/ then values = (($1.to_i)..($2.to_i)).to_a
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 /^(\*)$/ then values = (min..max).to_a
162
+ when /^(\*)$/
163
+ values = (min..max).to_a
167
164
 
168
165
  #lastly, should just be a number
169
- when /^(\d+)$/ then values << $1.to_i
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, 2099, exp)
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