clockwork 1.2.1 → 1.3.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/clockwork.gemspec +1 -1
- data/lib/clockwork/database_events.rb +4 -4
- data/lib/clockwork/database_events/event.rb +12 -17
- data/lib/clockwork/database_events/event_collection.rb +59 -0
- data/lib/clockwork/database_events/event_store.rb +129 -0
- data/lib/clockwork/database_events/manager.rb +1 -1
- data/lib/clockwork/database_events/synchronizer.rb +23 -0
- data/test/database_events/event_store_test.rb +24 -0
- data/test/database_events/{sync_performer_test.rb → synchronizer_test.rb} +2 -2
- data/test/database_events/test_helpers.rb +1 -1
- metadata +8 -5
- data/lib/clockwork/database_events/registry.rb +0 -39
- data/lib/clockwork/database_events/sync_performer.rb +0 -94
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fbf625509799141df225a803d47a38486923c26
|
4
|
+
data.tar.gz: 9e4f834dd23c3fa8d73b65a22643566be36dbef8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5875887345a81b5d7c8202b6f6d4d5fc7a2b1fa968611a7ebb914d949e951fa25b63585e7b60d0e64e115866a2ee04172b08268037c2d95f0142cf78d43cd377
|
7
|
+
data.tar.gz: ac4cdfbb58324b6faa106556c3339df3aefae1c8701b860ecab04402660475eb399e659caf516a486c54e68e6b43e98b9f84b1647e848063d22c2d84d8207347
|
data/clockwork.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require_relative 'database_events/event'
|
2
|
-
require_relative 'database_events/
|
3
|
-
require_relative 'database_events/
|
2
|
+
require_relative 'database_events/synchronizer'
|
3
|
+
require_relative 'database_events/event_store'
|
4
4
|
require_relative 'database_events/manager'
|
5
5
|
|
6
6
|
# TERMINOLOGY
|
@@ -15,7 +15,7 @@ module Clockwork
|
|
15
15
|
|
16
16
|
module Methods
|
17
17
|
def sync_database_events(options={}, &block)
|
18
|
-
DatabaseEvents::
|
18
|
+
DatabaseEvents::Synchronizer.setup(options, &block)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -23,4 +23,4 @@ module Clockwork
|
|
23
23
|
|
24
24
|
module DatabaseEvents
|
25
25
|
end
|
26
|
-
end
|
26
|
+
end
|
@@ -4,35 +4,30 @@ module Clockwork
|
|
4
4
|
|
5
5
|
class Event < Clockwork::Event
|
6
6
|
|
7
|
-
attr_accessor :
|
7
|
+
attr_accessor :event_store, :at
|
8
8
|
|
9
|
-
def initialize(manager, period, job, block,
|
9
|
+
def initialize(manager, period, job, block, event_store, options={})
|
10
10
|
super(manager, period, job, block, options)
|
11
|
-
@
|
12
|
-
@
|
11
|
+
@event_store = event_store
|
12
|
+
@event_store.register(self, job)
|
13
13
|
end
|
14
14
|
|
15
15
|
def name
|
16
|
-
(
|
16
|
+
(job_has_name? && job.name) ? job.name : "#{job.class}:#{job.id}"
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
name
|
21
|
-
end
|
22
|
-
|
23
|
-
def name_or_frequency_has_changed?(model)
|
24
|
-
name_has_changed?(model) || frequency_has_changed?(model)
|
19
|
+
def job_has_name?
|
20
|
+
job.respond_to?(:name)
|
25
21
|
end
|
26
22
|
|
27
|
-
|
28
|
-
|
29
|
-
job.respond_to?(:name) && job.name != model.name
|
23
|
+
def to_s
|
24
|
+
name
|
30
25
|
end
|
31
26
|
|
32
|
-
def
|
33
|
-
@period
|
27
|
+
def frequency
|
28
|
+
@period
|
34
29
|
end
|
35
30
|
end
|
36
31
|
|
37
32
|
end
|
38
|
-
end
|
33
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Clockwork
|
2
|
+
module DatabaseEvents
|
3
|
+
class EventCollection
|
4
|
+
|
5
|
+
def initialize(manager=Clockwork.manager)
|
6
|
+
@events = []
|
7
|
+
@manager = manager
|
8
|
+
end
|
9
|
+
|
10
|
+
def add(event)
|
11
|
+
@events << event
|
12
|
+
end
|
13
|
+
|
14
|
+
def has_changed?(model)
|
15
|
+
return true if event.nil?
|
16
|
+
|
17
|
+
(has_name? && name != model.name) ||
|
18
|
+
frequency != model.frequency ||
|
19
|
+
ats != model_ats(model)
|
20
|
+
end
|
21
|
+
|
22
|
+
def unregister
|
23
|
+
events.each{|e| manager.unregister(e) }
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :events, :manager
|
29
|
+
|
30
|
+
def event
|
31
|
+
events.first
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_name?
|
35
|
+
event.job_has_name?
|
36
|
+
end
|
37
|
+
|
38
|
+
def name
|
39
|
+
event.name
|
40
|
+
end
|
41
|
+
|
42
|
+
def frequency
|
43
|
+
event.frequency
|
44
|
+
end
|
45
|
+
|
46
|
+
def ats
|
47
|
+
events.collect(&:at).compact
|
48
|
+
end
|
49
|
+
|
50
|
+
def model_ats(model)
|
51
|
+
at_strings_for(model).collect{|at| At.parse(at) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def at_strings_for(model)
|
55
|
+
model.at.to_s.split(',').map(&:strip)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# How EventStore and Clockwork manager events are kept in sync...
|
2
|
+
#
|
3
|
+
# The normal Clockwork::Manager is responsible for keeping track of
|
4
|
+
# Clockwork events, and ensuring they are scheduled at the correct time.
|
5
|
+
# It has an @events array for this purpose.
|
6
|
+
|
7
|
+
# For keeping track of Database-backed events though, we need to keep
|
8
|
+
# track of more information about the events, e.g. the block which should
|
9
|
+
# be triggered when they are run, which model the event comes from, the
|
10
|
+
# model ID it relates to etc. Therefore, we devised a separate mechanism
|
11
|
+
# for keeping track of these database-backed events: the per-model EventStore.
|
12
|
+
|
13
|
+
# Having two classes responsible for keeping track of events though leads to
|
14
|
+
# a slight quirk, in that these two have to be kept in sync. The way this is
|
15
|
+
# done is by letting the EventStore largely defer to the Clockwork Manager.
|
16
|
+
|
17
|
+
# 1. When the EventStore wishes to recreate events:
|
18
|
+
# - it asks the Clockwork.manager to do this for it
|
19
|
+
# - by calling Clockwork.manager.every
|
20
|
+
|
21
|
+
# 2. When the DatabaseEvents::Manager creates events (via its #register)
|
22
|
+
# - it creates a new DatabaseEvents::Event
|
23
|
+
# - DatabaseEvents::Event#initialize registers it with the EventStore
|
24
|
+
module Clockwork
|
25
|
+
|
26
|
+
module DatabaseEvents
|
27
|
+
|
28
|
+
class EventStore
|
29
|
+
|
30
|
+
def initialize(block_to_perform_on_event_trigger)
|
31
|
+
@related_events = {}
|
32
|
+
@block_to_perform_on_event_trigger = block_to_perform_on_event_trigger
|
33
|
+
end
|
34
|
+
|
35
|
+
# DatabaseEvents::Manager#register creates a new DatabaseEvents::Event, whose
|
36
|
+
# #initialize method registers the new database event with the EventStore by
|
37
|
+
# calling this method.
|
38
|
+
def register(event, model)
|
39
|
+
related_events_for(model.id).add(event)
|
40
|
+
end
|
41
|
+
|
42
|
+
def update(current_model_objects)
|
43
|
+
unregister_all_except(current_model_objects)
|
44
|
+
update_registered_models(current_model_objects)
|
45
|
+
register_new_models(current_model_objects)
|
46
|
+
end
|
47
|
+
|
48
|
+
def unregister_all_except(model_objects)
|
49
|
+
ids = model_objects.collect(&:id)
|
50
|
+
(@related_events.keys - ids).each{|id| unregister(id) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def update_registered_models(model_objects)
|
54
|
+
registered_models(model_objects).each do |model|
|
55
|
+
if has_changed?(model)
|
56
|
+
unregister(model.id)
|
57
|
+
register_with_manager(model)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def register_new_models(model_objects)
|
63
|
+
unregistered_models(model_objects).each do |new_model_object|
|
64
|
+
register_with_manager(new_model_object)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
attr_reader :related_events
|
71
|
+
|
72
|
+
def registered?(model)
|
73
|
+
related_events_for(model.id) != nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def has_changed?(model)
|
77
|
+
related_events_for(model.id).has_changed?(model)
|
78
|
+
end
|
79
|
+
|
80
|
+
def related_events_for(id)
|
81
|
+
related_events[id] ||= EventCollection.new
|
82
|
+
end
|
83
|
+
|
84
|
+
def registered_models(model_objects)
|
85
|
+
model_objects.select{|m| registered?(m) }
|
86
|
+
end
|
87
|
+
|
88
|
+
def unregistered_models(model_objects)
|
89
|
+
model_objects.select{|m| !registered?(m) }
|
90
|
+
end
|
91
|
+
|
92
|
+
def unregister(id)
|
93
|
+
related_events_for(id).unregister
|
94
|
+
related_events.delete(id)
|
95
|
+
end
|
96
|
+
|
97
|
+
# When re-creating events, the Clockwork.manager must be used to
|
98
|
+
# create them, as it is ultimately responsible for ensuring that
|
99
|
+
# the events actually get run when they should. We call its #every
|
100
|
+
# method, which will result in DatabaseEvent::Manager#register being
|
101
|
+
# called, which creates a new DatabaseEvent::Event, which will be
|
102
|
+
# registered with the EventStore on #initialize.
|
103
|
+
def register_with_manager(model)
|
104
|
+
Clockwork.manager.
|
105
|
+
every(model.frequency, model, options(model),
|
106
|
+
&@block_to_perform_on_event_trigger)
|
107
|
+
end
|
108
|
+
|
109
|
+
def options(model)
|
110
|
+
options = {
|
111
|
+
:from_database => true,
|
112
|
+
:synchronizer => self,
|
113
|
+
:at => at_strings_for(model)
|
114
|
+
}
|
115
|
+
|
116
|
+
options[:tz] = model.tz if model.respond_to?(:tz)
|
117
|
+
|
118
|
+
options
|
119
|
+
end
|
120
|
+
|
121
|
+
def at_strings_for(model)
|
122
|
+
return nil if model.at.to_s.empty?
|
123
|
+
|
124
|
+
model.at.split(',').map(&:strip)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
@@ -10,7 +10,7 @@ module Clockwork
|
|
10
10
|
|
11
11
|
def register(period, job, block, options)
|
12
12
|
@events << if options[:from_database]
|
13
|
-
Clockwork::DatabaseEvents::Event.new(self, period, job, (block || handler), options.fetch(:
|
13
|
+
Clockwork::DatabaseEvents::Event.new(self, period, job, (block || handler), options.fetch(:synchronizer), options)
|
14
14
|
else
|
15
15
|
Clockwork::Event.new(self, period, job, block || handler, options)
|
16
16
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../database_events'
|
2
|
+
|
3
|
+
module Clockwork
|
4
|
+
|
5
|
+
module DatabaseEvents
|
6
|
+
|
7
|
+
class Synchronizer
|
8
|
+
|
9
|
+
def self.setup(options={}, &block_to_perform_on_event_trigger)
|
10
|
+
model_class = options.fetch(:model) { raise KeyError, ":model must be set to the model class" }
|
11
|
+
every = options.fetch(:every) { raise KeyError, ":every must be set to the database sync frequency" }
|
12
|
+
|
13
|
+
event_store = EventStore.new(block_to_perform_on_event_trigger)
|
14
|
+
|
15
|
+
# create event that syncs clockwork events with events coming from database-backed model
|
16
|
+
Clockwork.manager.every every, "sync_database_events_for_model_#{model_class}" do
|
17
|
+
event_store.update(model_class.all)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "minitest/autorun"
|
2
|
+
require 'clockwork/database_events/event_store'
|
3
|
+
require 'clockwork/database_events/event_collection'
|
4
|
+
|
5
|
+
describe Clockwork::DatabaseEvents::EventStore do
|
6
|
+
|
7
|
+
described_class = Clockwork::DatabaseEvents::EventStore
|
8
|
+
EventCollection = Clockwork::DatabaseEvents::EventCollection
|
9
|
+
|
10
|
+
describe '#register' do
|
11
|
+
it 'adds the event to the event group' do
|
12
|
+
event_group = EventCollection.new
|
13
|
+
EventCollection.stubs(:new).returns(event_group)
|
14
|
+
|
15
|
+
event = OpenStruct.new
|
16
|
+
model = OpenStruct.new id: 1
|
17
|
+
subject = described_class.new(Proc.new {})
|
18
|
+
|
19
|
+
event_group.expects(:add).with(event)
|
20
|
+
|
21
|
+
subject.register(event, model)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -7,7 +7,7 @@ require_relative '../../lib/clockwork'
|
|
7
7
|
require_relative '../../lib/clockwork/database_events'
|
8
8
|
require_relative 'test_helpers'
|
9
9
|
|
10
|
-
describe Clockwork::DatabaseEvents::
|
10
|
+
describe Clockwork::DatabaseEvents::Synchronizer do
|
11
11
|
before do
|
12
12
|
@now = Time.now
|
13
13
|
DatabaseEventModel.delete_all
|
@@ -25,7 +25,7 @@ describe Clockwork::DatabaseEvents::SyncPerformer do
|
|
25
25
|
|
26
26
|
describe "setup" do
|
27
27
|
before do
|
28
|
-
@subject = Clockwork::DatabaseEvents::
|
28
|
+
@subject = Clockwork::DatabaseEvents::Synchronizer
|
29
29
|
end
|
30
30
|
|
31
31
|
describe "arguments" do
|
@@ -5,7 +5,7 @@ def setup_sync(options={})
|
|
5
5
|
frequency = options.fetch(:every) { raise KeyError, ":every must be set to the database sync frequency" }
|
6
6
|
events_run = options.fetch(:events_run) { raise KeyError, ":events_run must be provided"}
|
7
7
|
|
8
|
-
Clockwork::DatabaseEvents::
|
8
|
+
Clockwork::DatabaseEvents::Synchronizer.setup model: model_class, every: frequency do |model|
|
9
9
|
name = model.respond_to?(:name) ? model.name : model.to_s
|
10
10
|
events_run << name
|
11
11
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clockwork
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Wiggins
|
@@ -138,15 +138,17 @@ files:
|
|
138
138
|
- lib/clockwork/at.rb
|
139
139
|
- lib/clockwork/database_events.rb
|
140
140
|
- lib/clockwork/database_events/event.rb
|
141
|
+
- lib/clockwork/database_events/event_collection.rb
|
142
|
+
- lib/clockwork/database_events/event_store.rb
|
141
143
|
- lib/clockwork/database_events/manager.rb
|
142
|
-
- lib/clockwork/database_events/
|
143
|
-
- lib/clockwork/database_events/sync_performer.rb
|
144
|
+
- lib/clockwork/database_events/synchronizer.rb
|
144
145
|
- lib/clockwork/event.rb
|
145
146
|
- lib/clockwork/manager.rb
|
146
147
|
- test/at_test.rb
|
147
148
|
- test/clockwork_test.rb
|
149
|
+
- test/database_events/event_store_test.rb
|
148
150
|
- test/database_events/support/active_record_fake.rb
|
149
|
-
- test/database_events/
|
151
|
+
- test/database_events/synchronizer_test.rb
|
150
152
|
- test/database_events/test_helpers.rb
|
151
153
|
- test/event_test.rb
|
152
154
|
- test/manager_test.rb
|
@@ -177,8 +179,9 @@ summary: A scheduler process to replace cron.
|
|
177
179
|
test_files:
|
178
180
|
- test/at_test.rb
|
179
181
|
- test/clockwork_test.rb
|
182
|
+
- test/database_events/event_store_test.rb
|
180
183
|
- test/database_events/support/active_record_fake.rb
|
181
|
-
- test/database_events/
|
184
|
+
- test/database_events/synchronizer_test.rb
|
182
185
|
- test/database_events/test_helpers.rb
|
183
186
|
- test/event_test.rb
|
184
187
|
- test/manager_test.rb
|
@@ -1,39 +0,0 @@
|
|
1
|
-
module Clockwork
|
2
|
-
|
3
|
-
module DatabaseEvents
|
4
|
-
|
5
|
-
class Registry
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@events = Hash.new []
|
9
|
-
end
|
10
|
-
|
11
|
-
def register(event, model)
|
12
|
-
@events[model.id] = @events[model.id] + [event]
|
13
|
-
end
|
14
|
-
|
15
|
-
def unregister(model)
|
16
|
-
unregister_by_id(model.id)
|
17
|
-
end
|
18
|
-
|
19
|
-
def unregister_by_id(id)
|
20
|
-
@events[id].each{|e| Clockwork.manager.unregister(e) }
|
21
|
-
@events.delete(id)
|
22
|
-
end
|
23
|
-
|
24
|
-
def unregister_all_except(ids)
|
25
|
-
(@events.keys - ids).each{|id| unregister_by_id(id) }
|
26
|
-
end
|
27
|
-
|
28
|
-
# all events of same id will have same frequency/name, just different ats
|
29
|
-
def event_for(model)
|
30
|
-
events_for(model).first
|
31
|
-
end
|
32
|
-
|
33
|
-
def events_for(model)
|
34
|
-
@events[model.id]
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|
@@ -1,94 +0,0 @@
|
|
1
|
-
require_relative '../database_events'
|
2
|
-
|
3
|
-
module Clockwork
|
4
|
-
|
5
|
-
module DatabaseEvents
|
6
|
-
|
7
|
-
class SyncPerformer
|
8
|
-
|
9
|
-
PERFORMERS = []
|
10
|
-
|
11
|
-
def self.setup(options={}, &block)
|
12
|
-
model_class = options.fetch(:model) { raise KeyError, ":model must be set to the model class" }
|
13
|
-
every = options.fetch(:every) { raise KeyError, ":every must be set to the database sync frequency" }
|
14
|
-
|
15
|
-
sync_performer = self.new(model_class, &block)
|
16
|
-
|
17
|
-
# create event that syncs clockwork events with events coming from database-backed model
|
18
|
-
Clockwork.manager.every every, "sync_database_events_for_model_#{model_class}" do
|
19
|
-
sync_performer.sync
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def initialize(model_class, &proc)
|
24
|
-
@model_class = model_class
|
25
|
-
@block = proc
|
26
|
-
@database_event_registry = Registry.new
|
27
|
-
|
28
|
-
PERFORMERS << self
|
29
|
-
end
|
30
|
-
|
31
|
-
# delegates to Registry
|
32
|
-
def register(event, model)
|
33
|
-
@database_event_registry.register(event, model)
|
34
|
-
end
|
35
|
-
|
36
|
-
# Ensure clockwork events reflect events from database-backed model
|
37
|
-
# Adds any new events, modifies updated ones, and delets removed ones
|
38
|
-
def sync
|
39
|
-
model_ids_that_exist = []
|
40
|
-
|
41
|
-
@model_class.all.each do |model|
|
42
|
-
model_ids_that_exist << model.id
|
43
|
-
if are_different?(@database_event_registry.event_for(model), model)
|
44
|
-
create_or_recreate_event(model)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
@database_event_registry.unregister_all_except(model_ids_that_exist)
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
def are_different?(event, model)
|
52
|
-
return true if event.nil?
|
53
|
-
event.name_or_frequency_has_changed?(model) || ats_have_changed?(model)
|
54
|
-
end
|
55
|
-
|
56
|
-
def ats_have_changed?(model)
|
57
|
-
model_ats = ats_array_for_event(model)
|
58
|
-
event_ats = ats_array_from_model(model)
|
59
|
-
|
60
|
-
model_ats != event_ats
|
61
|
-
end
|
62
|
-
|
63
|
-
def ats_array_for_event(model)
|
64
|
-
@database_event_registry.events_for(model).collect{|event| event.at }.compact
|
65
|
-
end
|
66
|
-
|
67
|
-
def ats_array_from_model(model)
|
68
|
-
(at_strings_for(model) || []).collect{|at| At.parse(at) }
|
69
|
-
end
|
70
|
-
|
71
|
-
def at_strings_for(model)
|
72
|
-
model.at.to_s.empty? ? nil : model.at.split(',').map(&:strip)
|
73
|
-
end
|
74
|
-
|
75
|
-
def create_or_recreate_event(model)
|
76
|
-
if @database_event_registry.event_for(model)
|
77
|
-
@database_event_registry.unregister(model)
|
78
|
-
end
|
79
|
-
|
80
|
-
options = {
|
81
|
-
:from_database => true,
|
82
|
-
:sync_performer => self,
|
83
|
-
:at => at_strings_for(model)
|
84
|
-
}
|
85
|
-
|
86
|
-
options[:tz] = model.tz if model.respond_to?(:tz)
|
87
|
-
|
88
|
-
# we pass actual model instance as the job, rather than just name
|
89
|
-
Clockwork.manager.every model.frequency, model, options, &@block
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
end
|
94
|
-
end
|