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.
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
@@ -1,21 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'activemessaging/message_sender'
4
-
5
- module SayWhen
6
- module Processor
7
- class ActiveMessaging < SayWhen::Processor::Base
8
-
9
- include ::ActiveMessaging::MessageSender
10
-
11
- def initialize(scheduler)
12
- super(scheduler)
13
- end
14
-
15
- # send the job to the other end, then in the a13g processor, call the execute method
16
- def process(job)
17
- publish(:say_when, { job_id: job.id }.to_yaml )
18
- end
19
- end if defined?(::ActiveMessaging)
20
- end
21
- end
@@ -1,19 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module SayWhen
4
- module Processor
5
-
6
- class Base
7
- attr_accessor :scheduler
8
-
9
- def initialize(scheduler)
10
- @scheduler = scheduler
11
- end
12
-
13
- def process(job)
14
- raise NotImplementedError.new('You need to implement process(job)')
15
- end
16
- end
17
-
18
- end
19
- end
@@ -1,14 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module SayWhen
4
- module Processor
5
- class Shoryuken < SayWhen::Processor::Base
6
- def initialize(scheduler)
7
- super(scheduler)
8
- end
9
-
10
- def process(job)
11
- end
12
- end
13
- end
14
- end
@@ -1,17 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module SayWhen
4
- module Processor
5
-
6
- class Simple < SayWhen::Processor::Base
7
- def initialize(scheduler)
8
- super(scheduler)
9
- end
10
-
11
- def process(job)
12
- job.execute
13
- end
14
- end
15
-
16
- end
17
- end
@@ -1,92 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module SayWhen #:nodoc:
4
- module Storage #:nodoc:
5
- module ActiveRecord #:nodoc:
6
- module Acts #:nodoc:
7
-
8
- extend ActiveSupport::Concern
9
-
10
- module ClassMethods
11
- def acts_as_scheduled
12
- include SayWhen::Storage::ActiveRecord::Acts::InstanceMethods
13
-
14
- has_many :scheduled_jobs, as: :scheduled, class_name: 'SayWhen::Storage::ActiveRecord::Job', dependent: :destroy
15
- end
16
- end
17
-
18
- module InstanceMethods
19
-
20
- def schedule_instance(next_at_method, job={})
21
- options = job_options(job)
22
- options[:trigger_strategy] = 'instance'
23
- options[:trigger_options] = { next_at_method: next_at_method }
24
- Scheduler.schedule(options)
25
- end
26
-
27
- def schedule_cron(expression, time_zone, job={})
28
- options = job_options(job)
29
- options[:trigger_strategy] = 'cron'
30
- options[:trigger_options] = { expression: expression, time_zone: time_zone }
31
- Scheduler.schedule(options)
32
- end
33
-
34
- def schedule_once(time, job={})
35
- options = job_options(job)
36
- options[:trigger_strategy] = 'once'
37
- options[:trigger_options] = { at: time}
38
- Scheduler.schedule(options)
39
- end
40
-
41
- def schedule_in(after, job={})
42
- options = job_options(job)
43
- options[:trigger_strategy] = 'once'
44
- options[:trigger_options] = { at: (Time.now + after)}
45
- Scheduler.schedule(options)
46
- end
47
-
48
- # helpers
49
-
50
- def job_options(job)
51
- {
52
- scheduled: self,
53
- job_class: extract_job_class(job),
54
- job_method: extract_job_method(job),
55
- data: extract_data(job)
56
- }
57
- end
58
-
59
- def extract_job_class(job)
60
- if job.is_a?(Hash)
61
- job[:class]
62
- elsif job.is_a?(Class)
63
- job.name
64
- elsif job.is_a?(String)
65
- job
66
- else
67
- raise "Could not identify job class from: #{job}"
68
- end
69
- end
70
-
71
- def extract_job_method(job)
72
- if job.is_a?(Hash)
73
- job[:method]
74
- else
75
- 'execute'
76
- end
77
- end
78
-
79
- def extract_data(job)
80
- if job.is_a?(Hash)
81
- job[:data]
82
- else
83
- nil
84
- end
85
- end
86
- end # InstanceMethods
87
- end
88
- end
89
- end
90
- end
91
-
92
- ActiveRecord::Base.send(:include, SayWhen::Storage::ActiveRecord::Acts) unless ActiveRecord::Base.include?(SayWhen::Storage::ActiveRecord::Acts)
@@ -1,100 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'active_record'
4
- require 'say_when/base_job'
5
- require 'say_when/storage/active_record/job_execution'
6
- require 'say_when/storage/active_record/acts'
7
-
8
- module SayWhen
9
- module Storage
10
- module ActiveRecord
11
-
12
- class Job < ::ActiveRecord::Base
13
-
14
- include SayWhen::BaseJob
15
-
16
- self.table_name = 'say_when_jobs'
17
-
18
- serialize :trigger_options
19
- serialize :data
20
-
21
- belongs_to :scheduled, polymorphic: true
22
- has_many :job_executions, class_name: 'SayWhen::Storage::ActiveRecord::JobExecution'
23
-
24
- before_create :set_defaults
25
-
26
- def self.acquire_next(no_later_than)
27
- next_job = nil
28
-
29
- hide_logging do
30
- SayWhen::Storage::ActiveRecord::Job.transaction do
31
- # select and lock the next job that needs executin' (status waiting, and after no_later_than)
32
- next_job = where('status = ? and next_fire_at < ?', STATE_WAITING, no_later_than.in_time_zone('UTC')).
33
- order('next_fire_at ASC').
34
- lock(true).first
35
-
36
- # set status to acquired to take it out of rotation
37
- next_job.update_attribute(:status, STATE_ACQUIRED) unless next_job.nil?
38
- end
39
- end
40
- next_job
41
- end
42
-
43
- def set_defaults
44
- # puts "SayWhen::Storage::ActiveRecord::Job - set_defaults start"
45
- self.status = STATE_WAITING
46
- self.next_fire_at = self.trigger.next_fire_at
47
- # puts "SayWhen::Storage::ActiveRecord::Job - set_defaults, next_fire_at: #{self.next_fire_at}"
48
- end
49
-
50
- def fired(fired_at=Time.now)
51
- Job.transaction {
52
- super
53
- self.save!
54
- }
55
- end
56
-
57
- def release
58
- Job.transaction {
59
- super
60
- self.save!
61
- }
62
- end
63
-
64
- # default impl with some error handling and result recording
65
- def execute
66
- result = nil
67
- execution = SayWhen::Storage::ActiveRecord::JobExecution.create(:job=>self, :status=>'executing', :start_at=>Time.now)
68
-
69
- begin
70
- result = self.execute_job(data)
71
- execution.result = result
72
- execution.status = 'complete'
73
- rescue Object=>ex
74
- execution.result = "#{ex.class.name}: #{ex.message}\n\t#{ex.backtrace.join("\n\t")}"
75
- execution.status = 'error'
76
- end
77
-
78
- execution.end_at = Time.now
79
- execution.save!
80
- result
81
- end
82
-
83
- protected
84
-
85
- def self.hide_logging
86
- old_logger = nil
87
- begin
88
- old_logger = ::ActiveRecord::Base.logger
89
- ::ActiveRecord::Base.logger = nil
90
-
91
- yield
92
-
93
- ensure
94
- ::ActiveRecord::Base.logger = old_logger
95
- end
96
- end
97
- end
98
- end
99
- end
100
- end
@@ -1,14 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'active_record'
4
-
5
- module SayWhen
6
- module Storage
7
- module ActiveRecord
8
- class JobExecution < ::ActiveRecord::Base
9
- self.table_name = "say_when_job_executions"
10
- belongs_to :job, class_name: 'SayWhen::Storage::ActiveRecord::Job'
11
- end
12
- end
13
- end
14
- end
@@ -1,36 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module SayWhen
4
- module Storage
5
- module Memory
6
-
7
- module Base
8
-
9
- attr_accessor :props
10
-
11
- def has_properties(*args)
12
- @props ||= []
13
- args.each do |a|
14
- unless @props.member?(a.to_s)
15
- @props << a.to_s
16
- class_eval { attr_accessor(a.to_sym) }
17
- end
18
- end
19
- end
20
-
21
- def self.included(base)
22
- base.extend self
23
- end
24
-
25
- def initialize(args={})
26
- args.each do |k,v|
27
- if self.class.props.member?(k.to_s)
28
- self.send("#{k}=", v)
29
- end
30
- end
31
- end
32
- end
33
-
34
- end
35
- end
36
- end
@@ -1,53 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'say_when/storage/memory/base'
4
-
5
- module SayWhen
6
- module Storage
7
- module Memory
8
-
9
- # define a trigger class
10
- class Job
11
-
12
- include SayWhen::Storage::Memory::Base
13
- include SayWhen::BaseJob
14
-
15
- has_properties :group, :name, :status, :start_at, :end_at
16
- has_properties :trigger_strategy, :trigger_options, :last_fire_at, :next_fire_at
17
- has_properties :job_class, :job_method, :data
18
- has_properties :scheduled
19
-
20
- cattr_accessor :jobs
21
- @@jobs = SortedSet.new
22
- @@acquire_lock = Mutex.new
23
-
24
- def self.acquire_next(no_later_than)
25
- @@acquire_lock.synchronize {
26
-
27
- next_job = jobs.detect(nil) do |j|
28
- (j.status == STATE_WAITING) && (j.next_fire_at.to_i <= no_later_than.to_i)
29
- end
30
-
31
- next_job.status = STATE_ACQUIRED if next_job
32
- next_job
33
- }
34
- end
35
-
36
- def self.create(job)
37
- job = new(job) if job.is_a?(Hash)
38
- self.jobs << job
39
- end
40
-
41
- def initialize(options={})
42
- super
43
- self.status = STATE_WAITING unless self.status
44
- self.next_fire_at = trigger.next_fire_at
45
- end
46
-
47
- def <=>(job)
48
- self.next_fire_at.to_i <=> job.next_fire_at.to_i
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,74 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require File.dirname(__FILE__) + '/../spec_helper'
4
-
5
- describe SayWhen::CronExpression do
6
-
7
- it 'should set the time_zone' do
8
- @ce = SayWhen::CronExpression.new("0 0 12 ? * 1#1 *", 'Pacific Time (US & Canada)')
9
- @ce.time_zone.must_equal 'Pacific Time (US & Canada)'
10
- end
11
-
12
- describe 'get first sunday in the month with "1#1' do
13
-
14
- before do
15
- @ce = SayWhen::CronExpression.new("0 0 12 ? * 1#1 *", 'Pacific Time (US & Canada)')
16
- end
17
-
18
- it 'finds first sunday in the same month' do
19
- @ce.next_fire_at(Time.utc(2008,1,1)).must_equal Time.parse('2008-01-06 12:00:00 -0800')
20
- end
21
-
22
-
23
- it 'finds first sunday in the next month' do
24
- @ce.next_fire_at(Time.utc(2008,1,7)).must_equal Time.parse('2008-02-03 12:00:00 -0800')
25
- end
26
-
27
- it 'finds last sunday in the same month' do
28
- @ce.last_fire_at(Time.utc(2008,1,10)).must_equal Time.parse('2008-01-06 12:00:00 -0800')
29
- end
30
-
31
- it 'finds sundays in the prior months and years' do
32
- @ce.last_fire_at(Time.utc(2008,1,5)).must_equal Time.parse('2007-12-02 12:00:00 -0800')
33
- @ce.last_fire_at(Time.parse('2007-12-02 12:00:00 -0800') - 1.second).must_equal Time.parse('2007-11-04 12:00:00 -0800')
34
- @ce.last_fire_at(Time.parse('2007-11-04 12:00:00 -0800') - 1.second).must_equal Time.parse('2007-10-07 12:00:00 -0700')
35
- @ce.next_fire_at(Time.parse('2007-10-07 12:00:00 -0700') + 1.second).must_equal Time.parse('2007-11-04 12:00:00 -0800')
36
- end
37
- end
38
-
39
- describe 'get last sunday in the month with "1L"' do
40
- before do
41
- @ce = SayWhen::CronExpression.new("0 0 12 ? * 1L *", 'Pacific Time (US & Canada)')
42
- end
43
-
44
- it 'gets next final sunday for same month' do
45
- @ce.next_fire_at(Time.utc(2008,1,1)).must_equal Time.parse('2008-01-27 12:00:00 -0800')
46
- end
47
-
48
-
49
- it 'gets next final sunday for next month' do
50
- @ce.next_fire_at(Time.utc(2008,1,28)).must_equal Time.parse('2008-02-24 12:00:00 -0800')
51
- end
52
-
53
- it 'gets last final sunday for same month' do
54
- @ce.last_fire_at(Time.utc(2008,1,28)).must_equal Time.parse('2008-01-27 12:00:00 -0800')
55
- end
56
-
57
- it 'gets last sunday for prior month and year' do
58
- @ce.last_fire_at(Time.utc(2008,1,1)).must_equal Time.parse('2007-12-30 12:00:00 -0800')
59
- end
60
-
61
-
62
- it 'gets last sunday for prior month and year' do
63
- nfa = @ce.last_fire_at(Time.utc(2007,12,1))
64
- nfa.must_equal Time.parse('2007-11-25 12:00:00 -0800')
65
-
66
- nfa = @ce.last_fire_at(nfa - 1.second)
67
- nfa.must_equal Time.parse('2007-10-28 12:00:00 -0700')
68
-
69
- nfa = @ce.next_fire_at(nfa + 1.second)
70
- nfa.must_equal Time.parse('2007-11-25 12:00:00 -0800')
71
- end
72
-
73
- end
74
- end