clockwork 2.0.2 → 2.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +12 -8
- data/CHANGELOG.md +9 -0
- data/README.md +66 -21
- data/bin/clockwork +1 -1
- data/bin/clockworkd +2 -1
- data/clockwork.gemspec +1 -2
- data/gemfiles/activesupport4.gemfile +2 -2
- data/gemfiles/{activesupport3.gemfile → activesupport5.gemfile} +2 -1
- data/lib/clockwork/database_events/event_collection.rb +8 -1
- data/lib/clockwork/database_events/event_store.rb +5 -1
- data/lib/clockwork/event.rb +15 -3
- data/test/event_test.rb +37 -0
- data/test/samples/signal_test.rb +1 -0
- metadata +5 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7ad11e2b8e79db572d4240dd9ce2065067b22514919d4debc3758065b438ebca
|
4
|
+
data.tar.gz: 5dec51edf5bbd101a002e8c6ede3f11918441d75281b2baff3091bc0995721c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f06770eaf4eb2b473ee165a00ddf7b6326f957ccfbf1eb71ee65a3e4a840ea8a42fe052a52a7032f7b786677bad54e94bc58a7f3961a44bb404c16685dcddc48
|
7
|
+
data.tar.gz: 5a60bddb9ae7b7312e8df9a4b01a022d9072a3a4f2fdb7f5790f03a61cf8b31a0d6fc4a76c3b80abeee63eeb269447922b2c065982497f2d6f7becd33082f78e
|
data/.travis.yml
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
language: ruby
|
2
2
|
sudo: false
|
3
3
|
cache: bundler
|
4
|
+
before_install:
|
5
|
+
- gem update --system
|
6
|
+
# This is required to support ActiveSupport 4
|
7
|
+
# https://docs.travis-ci.com/user/languages/ruby/#bundler-20
|
8
|
+
- gem uninstall -v '>= 2' -i $(rvm gemdir)@global -ax bundler || true
|
9
|
+
- gem install bundler -v '< 2'
|
4
10
|
rvm:
|
5
|
-
-
|
6
|
-
- 2.
|
7
|
-
- 2.
|
8
|
-
-
|
9
|
-
-
|
10
|
-
- jruby-19mode
|
11
|
-
- # rbx-2.2.7
|
11
|
+
- 2.3.8
|
12
|
+
- 2.4.5
|
13
|
+
- 2.5.3
|
14
|
+
- jruby-9.1.17.0
|
15
|
+
- jruby-9.2.6.0
|
12
16
|
gemfile:
|
13
|
-
- gemfiles/activesupport3.gemfile
|
14
17
|
- gemfiles/activesupport4.gemfile
|
18
|
+
- gemfiles/activesupport5.gemfile
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Clockwork - a clock process to replace cron [![Build Status](https://api.travis-ci.org/Rykian/clockwork.png?branch=master)](https://travis-ci.org/Rykian/clockwork)
|
1
|
+
Clockwork - a clock process to replace cron [![Build Status](https://api.travis-ci.org/Rykian/clockwork.png?branch=master)](https://travis-ci.org/Rykian/clockwork)
|
2
2
|
===========================================
|
3
3
|
|
4
4
|
Cron is non-ideal for running scheduled application tasks, especially in an app
|
@@ -18,6 +18,8 @@ Create clock.rb:
|
|
18
18
|
|
19
19
|
```ruby
|
20
20
|
require 'clockwork'
|
21
|
+
require 'active_support/time' # Allow numeric durations (eg: 1.minutes)
|
22
|
+
|
21
23
|
module Clockwork
|
22
24
|
handler do |job|
|
23
25
|
puts "Running #{job}"
|
@@ -58,8 +60,8 @@ Quickstart for Heroku
|
|
58
60
|
|
59
61
|
Clockwork fits well with heroku's cedar stack.
|
60
62
|
|
61
|
-
Consider
|
62
|
-
a new project for
|
63
|
+
Consider using [clockwork-init.sh](https://gist.github.com/tomykaira/1312172) to create
|
64
|
+
a new project for Heroku.
|
63
65
|
|
64
66
|
Use with queueing
|
65
67
|
-----------------
|
@@ -80,6 +82,7 @@ For example, if you're using Beanstalk/Stalker:
|
|
80
82
|
|
81
83
|
```ruby
|
82
84
|
require 'stalker'
|
85
|
+
require 'active_support/time'
|
83
86
|
|
84
87
|
module Clockwork
|
85
88
|
handler { |job| Stalker.enqueue(job) }
|
@@ -142,31 +145,31 @@ module Clockwork
|
|
142
145
|
end
|
143
146
|
```
|
144
147
|
|
145
|
-
This tells clockwork to fetch all `ClockworkDatabaseEvent` instances from the database, and create an internal clockwork event for each one. Each clockwork event will be configured based on the instance's `frequency` and, optionally, its `at`, `
|
148
|
+
This tells clockwork to fetch all `ClockworkDatabaseEvent` instances from the database, and create an internal clockwork event for each one. Each clockwork event will be configured based on the instance's `frequency` and, optionally, its `at`, `if?`, `ignored_attributes`, `name`, and, `tz` methods. The code above also says to reload the events from the database every `1.minute`; we need pick up any changes in the database frequently (choose a sensible reload frequency by changing the `every:` option).
|
146
149
|
|
147
|
-
When one of the events is ready to be run (based on it's `frequency`, and possible `at`, `if
|
150
|
+
When one of the events is ready to be run (based on it's `frequency`, and possible `at`, `if?`, `ignored attributes`, and `tz` methods), clockwork arranges for the block passed to `sync_database_events` to be run. The above example shows how you could use either DelayedJob or Sidekiq to kick off a worker job. This approach is good because the ideal is to use clockwork as a simple scheduler, and avoid making it carry out any long-running tasks.
|
148
151
|
|
149
152
|
### Your Model Classes
|
150
153
|
|
151
154
|
`ActiveRecord` models are a perfect candidate for the model class. Having said that, the only requirements are:
|
152
155
|
|
153
|
-
|
154
|
-
|
155
|
-
2. the instances returned respond to:
|
156
|
+
1. the class responds to `all` returning an array of instances from the database
|
156
157
|
|
157
|
-
|
158
|
+
2. the instances returned respond to:
|
159
|
+
- `id` returning a unique identifier (this is needed to track changes to event settings)
|
160
|
+
- `frequency` returning the how frequently (in seconds) the database event should be run
|
158
161
|
|
159
|
-
|
162
|
+
- `attributes` returning a hash of [attribute name] => [attribute value] values (or really anything that we can use store on registering the event, and then compare again to see if the state has changed later)
|
160
163
|
|
161
|
-
|
164
|
+
- `at` *(optional)* return any acceptable clockwork `:at` string
|
162
165
|
|
163
|
-
|
166
|
+
- `name` *(optional)* returning the name for the event (used to identify it in the Clockwork output)
|
164
167
|
|
165
|
-
|
168
|
+
- `if?`*(optional)* returning either true or false, depending on whether the database event should run at the given time (this method will be passed the time as a parameter, much like the standard clockwork `:if`)
|
166
169
|
|
167
|
-
|
170
|
+
- `ignored_attributes` *(optional)* returning an array of model attributes (as symbols) to ignore when determining whether the database event has been modified since our last run
|
168
171
|
|
169
|
-
|
172
|
+
- `tz` *(optional)* returning the timezone to use (default is the local timezone)
|
170
173
|
|
171
174
|
#### Example Setup
|
172
175
|
|
@@ -245,6 +248,28 @@ class ClockworkDatabaseEvent < ActiveRecord::Base
|
|
245
248
|
end
|
246
249
|
```
|
247
250
|
|
251
|
+
#### Example use of `ignored_attributes`
|
252
|
+
|
253
|
+
Clockwork compares all attributes of the model between runs to determine if the model has changed, and if it has, it runs the event if all other conditions are met.
|
254
|
+
|
255
|
+
However, in certain cases, you may want to store additional attributes in your model that you don't want to affect whether a database event is executed prior to its next interval.
|
256
|
+
|
257
|
+
So for example, you may update an attribute of your model named `last_scheduled_at` on each run to track the last time it was successfully scheduled. You can tell Clockwork to ignore that attribute in its comparison as follows:
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
# app/models/clockwork_database_event.rb
|
261
|
+
class ClockworkDatabaseEvent < ActiveRecord::Base
|
262
|
+
|
263
|
+
...
|
264
|
+
|
265
|
+
def ignored_attributes
|
266
|
+
[ :last_scheduled_at ]
|
267
|
+
end
|
268
|
+
|
269
|
+
...
|
270
|
+
end
|
271
|
+
```
|
272
|
+
|
248
273
|
|
249
274
|
Event Parameters
|
250
275
|
----------
|
@@ -305,7 +330,8 @@ If this is a problem, please use the `:thread` option to prevent the long runnin
|
|
305
330
|
```ruby
|
306
331
|
every(1.day, 'reminders.send', :at => '00:00', :tz => 'UTC')
|
307
332
|
# Runs the job each day at midnight, UTC.
|
308
|
-
# The value for :tz can be anything supported by [TZInfo](
|
333
|
+
# The value for :tz can be anything supported by [TZInfo](https://github.com/tzinfo/tzinfo)
|
334
|
+
# Using the 'tzinfo' gem, run TZInfo::Timezone.all_identifiers to get a list of acceptable identifiers.
|
309
335
|
```
|
310
336
|
|
311
337
|
### :if
|
@@ -341,6 +367,19 @@ Clockwork.every(1.day, 'run.me.in.new.thread', :thread => true)
|
|
341
367
|
|
342
368
|
If a job is long-running or IO-intensive, this option helps keep the clock precise.
|
343
369
|
|
370
|
+
### :skip_first_run
|
371
|
+
|
372
|
+
Normally, a clockwork process that is defined to run in a specified period will run at startup.
|
373
|
+
This is sometimes undesired behaviour, if the action being run relies on other processes booting which may be slower than clock.
|
374
|
+
To avoid this problem, `:skip_first_run` can be used.
|
375
|
+
|
376
|
+
```ruby
|
377
|
+
Clockwork.every(5.minutes, 'myjob', :skip_first_run => true)
|
378
|
+
```
|
379
|
+
|
380
|
+
The above job will not run at initial boot, and instead run every 5 minutes after boot.
|
381
|
+
|
382
|
+
|
344
383
|
Configuration
|
345
384
|
-----------------------
|
346
385
|
|
@@ -357,7 +396,6 @@ Clockwork wakes up once a second and performs its duties. To change the number o
|
|
357
396
|
sleeps, set the `sleep_timeout` configuration option as shown below in the example.
|
358
397
|
|
359
398
|
From 1.1.0, Clockwork does not accept `sleep_timeout` less than 1 seconds.
|
360
|
-
This restriction is introduced to solve more severe bug [#135](https://github.com/tomykaira/clockwork/pull/135).
|
361
399
|
|
362
400
|
### :tz
|
363
401
|
|
@@ -524,10 +562,17 @@ clockworkd -c YOUR_CLOCK.rb start
|
|
524
562
|
|
525
563
|
For more details, you can run `clockworkd -h`.
|
526
564
|
|
565
|
+
Integration Testing
|
566
|
+
-------------------
|
567
|
+
|
568
|
+
You could take a look at:
|
569
|
+
* [clockwork-mocks](https://github.com/dpoetzsch/clockwork-mocks) that helps with running scheduled tasks during integration testing.
|
570
|
+
* [clockwork-test](https://github.com/kevin-j-m/clockwork-test) which ensures that tasks are triggered at the right time
|
571
|
+
|
527
572
|
Issues and Pull requests
|
528
573
|
------------------------
|
529
574
|
|
530
|
-
If you find a bug, please create an issue - [Issues ·
|
575
|
+
If you find a bug, please create an issue - [Issues · Rykian/clockwork](https://github.com/Rykian/clockwork/issues).
|
531
576
|
|
532
577
|
For a bug fix or a feature request, please send a pull-request.
|
533
578
|
Do not forget to add tests to show how your feature works, or what bug is fixed.
|
@@ -543,8 +588,8 @@ Use cases
|
|
543
588
|
|
544
589
|
Feel free to add your idea or experience and send a pull-request.
|
545
590
|
|
546
|
-
-
|
547
|
-
-
|
591
|
+
- Sending errors to Airbrake
|
592
|
+
- Read events from a database
|
548
593
|
|
549
594
|
Meta
|
550
595
|
----
|
@@ -559,4 +604,4 @@ Patches contributed by Mark McGranaghan and Lukáš Konarovský
|
|
559
604
|
|
560
605
|
Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
|
561
606
|
|
562
|
-
|
607
|
+
https://github.com/Rykian/clockwork
|
data/bin/clockwork
CHANGED
data/bin/clockworkd
CHANGED
@@ -4,6 +4,7 @@ STDERR.sync = STDOUT.sync = true
|
|
4
4
|
|
5
5
|
require 'clockwork'
|
6
6
|
require 'optparse'
|
7
|
+
require 'pathname'
|
7
8
|
|
8
9
|
begin
|
9
10
|
require 'daemons'
|
@@ -45,7 +46,7 @@ opts = OptionParser.new do |opts|
|
|
45
46
|
end
|
46
47
|
opts.on('-c', '--clock=FILE',"Clock .rb file. Default is #{@options[:file]}.") do |clock_file|
|
47
48
|
@options[:file] = clock_file
|
48
|
-
@options[:file] = "./#{@options[:file]}" unless @options[:file].
|
49
|
+
@options[:file] = "./#{@options[:file]}" unless Pathname.new(@options[:file]).absolute?
|
49
50
|
@options[:file] = File.expand_path(@options[:file])
|
50
51
|
end
|
51
52
|
opts.on('-d', '--dir=DIR', 'Directory to change to once the process starts') do |dir|
|
data/clockwork.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "clockwork"
|
3
|
-
s.version = "2.0.
|
3
|
+
s.version = "2.0.4"
|
4
4
|
|
5
5
|
s.authors = ["Adam Wiggins", "tomykaira"]
|
6
6
|
s.license = 'MIT'
|
@@ -20,7 +20,6 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.add_dependency(%q<tzinfo>)
|
21
21
|
s.add_dependency(%q<activesupport>)
|
22
22
|
|
23
|
-
s.add_development_dependency "bundler", "~> 1.3"
|
24
23
|
s.add_development_dependency "rake"
|
25
24
|
s.add_development_dependency "daemons"
|
26
25
|
s.add_development_dependency "minitest", "~> 5.8"
|
@@ -14,7 +14,14 @@ module Clockwork
|
|
14
14
|
def has_changed?(model)
|
15
15
|
return true if event.nil?
|
16
16
|
|
17
|
-
|
17
|
+
ignored_attributes = model.ignored_attributes if model.respond_to?(:ignored_attributes)
|
18
|
+
ignored_attributes ||= []
|
19
|
+
|
20
|
+
model_attributes = model.attributes.select do |k, _|
|
21
|
+
not ignored_attributes.include?(k.to_sym)
|
22
|
+
end
|
23
|
+
|
24
|
+
event.model_attributes != model_attributes
|
18
25
|
end
|
19
26
|
|
20
27
|
def unregister
|
@@ -112,15 +112,19 @@ module Clockwork
|
|
112
112
|
options = {
|
113
113
|
:from_database => true,
|
114
114
|
:synchronizer => self,
|
115
|
+
:ignored_attributes => [],
|
115
116
|
}
|
116
117
|
|
117
118
|
options[:at] = at_strings_for(model) if model.respond_to?(:at)
|
118
119
|
options[:if] = ->(time){ model.if?(time) } if model.respond_to?(:if?)
|
119
120
|
options[:tz] = model.tz if model.respond_to?(:tz)
|
121
|
+
options[:ignored_attributes] = model.ignored_attributes if model.respond_to?(:ignored_attributes)
|
120
122
|
|
121
123
|
# store the state of the model at time of registering so we can
|
122
124
|
# easily compare and determine if state has changed later
|
123
|
-
options[:model_attributes] = model.attributes
|
125
|
+
options[:model_attributes] = model.attributes.select do |k, v|
|
126
|
+
not options[:ignored_attributes].include?(k.to_sym)
|
127
|
+
end
|
124
128
|
|
125
129
|
options
|
126
130
|
end
|
data/lib/clockwork/event.rb
CHANGED
@@ -8,11 +8,12 @@ module Clockwork
|
|
8
8
|
@period = period
|
9
9
|
@job = job
|
10
10
|
@at = At.parse(options[:at])
|
11
|
-
@last = nil
|
12
11
|
@block = block
|
13
12
|
@if = options[:if]
|
14
13
|
@thread = options.fetch(:thread, @manager.config[:thread])
|
15
14
|
@timezone = options.fetch(:tz, @manager.config[:tz])
|
15
|
+
@skip_first_run = options[:skip_first_run]
|
16
|
+
@last = @skip_first_run ? convert_timezone(Time.now) : nil
|
16
17
|
end
|
17
18
|
|
18
19
|
def convert_timezone(t)
|
@@ -21,7 +22,10 @@ module Clockwork
|
|
21
22
|
|
22
23
|
def run_now?(t)
|
23
24
|
t = convert_timezone(t)
|
24
|
-
|
25
|
+
return false unless elapsed_ready?(t)
|
26
|
+
return false unless run_at?(t)
|
27
|
+
return false unless run_if?(t)
|
28
|
+
true
|
25
29
|
end
|
26
30
|
|
27
31
|
def thread?
|
@@ -57,10 +61,18 @@ module Clockwork
|
|
57
61
|
@manager.handle_error e
|
58
62
|
end
|
59
63
|
|
60
|
-
def elapsed_ready(t)
|
64
|
+
def elapsed_ready?(t)
|
61
65
|
@last.nil? || (t - @last.to_i).to_i >= @period
|
62
66
|
end
|
63
67
|
|
68
|
+
def run_at?(t)
|
69
|
+
@at.nil? || @at.ready?(t)
|
70
|
+
end
|
71
|
+
|
72
|
+
def run_if?(t)
|
73
|
+
@if.nil? || @if.call(t)
|
74
|
+
end
|
75
|
+
|
64
76
|
def validate_if_option(if_option)
|
65
77
|
if if_option && !if_option.respond_to?(:call)
|
66
78
|
raise ArgumentError.new(':if expects a callable object, but #{if_option} does not respond to call')
|
data/test/event_test.rb
CHANGED
@@ -34,4 +34,41 @@ describe Clockwork::Event do
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
describe '#run_now?' do
|
39
|
+
before do
|
40
|
+
@manager = Class.new
|
41
|
+
@manager.stubs(:config).returns({})
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'event skip_first_run option set to true' do
|
45
|
+
it 'returns false on first attempt' do
|
46
|
+
event = Clockwork::Event.new(@manager, 1, nil, nil, :skip_first_run => true)
|
47
|
+
assert_equal false, event.run_now?(Time.now)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns true on subsequent attempts' do
|
51
|
+
event = Clockwork::Event.new(@manager, 1, nil, nil, :skip_first_run => true)
|
52
|
+
# first run
|
53
|
+
event.run_now?(Time.now)
|
54
|
+
|
55
|
+
# second run
|
56
|
+
assert_equal true, event.run_now?(Time.now + 1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'event skip_first_run option not set' do
|
61
|
+
it 'returns true on first attempt' do
|
62
|
+
event = Clockwork::Event.new(@manager, 1, nil, nil)
|
63
|
+
assert_equal true, event.run_now?(Time.now + 1)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'event skip_first_run option set to false' do
|
68
|
+
it 'returns true on first attempt' do
|
69
|
+
event = Clockwork::Event.new(@manager, 1, nil, nil, :skip_first_run => false)
|
70
|
+
assert_equal true, event.run_now?(Time.now)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
37
74
|
end
|
data/test/samples/signal_test.rb
CHANGED
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: 2.0.
|
4
|
+
version: 2.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Wiggins
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2019-07-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: tzinfo
|
@@ -39,20 +39,6 @@ dependencies:
|
|
39
39
|
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
|
-
- !ruby/object:Gem::Dependency
|
43
|
-
name: bundler
|
44
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
-
requirements:
|
46
|
-
- - "~>"
|
47
|
-
- !ruby/object:Gem::Version
|
48
|
-
version: '1.3'
|
49
|
-
type: :development
|
50
|
-
prerelease: false
|
51
|
-
version_requirements: !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - "~>"
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version: '1.3'
|
56
42
|
- !ruby/object:Gem::Dependency
|
57
43
|
name: rake
|
58
44
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,6 +123,7 @@ extra_rdoc_files:
|
|
137
123
|
files:
|
138
124
|
- ".gitignore"
|
139
125
|
- ".travis.yml"
|
126
|
+
- CHANGELOG.md
|
140
127
|
- Gemfile
|
141
128
|
- LICENSE
|
142
129
|
- README.md
|
@@ -146,8 +133,8 @@ files:
|
|
146
133
|
- clockwork.gemspec
|
147
134
|
- clockworkd.1
|
148
135
|
- example.rb
|
149
|
-
- gemfiles/activesupport3.gemfile
|
150
136
|
- gemfiles/activesupport4.gemfile
|
137
|
+
- gemfiles/activesupport5.gemfile
|
151
138
|
- lib/clockwork.rb
|
152
139
|
- lib/clockwork/at.rb
|
153
140
|
- lib/clockwork/database_events.rb
|
@@ -188,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
188
175
|
version: '0'
|
189
176
|
requirements: []
|
190
177
|
rubyforge_project:
|
191
|
-
rubygems_version: 2.
|
178
|
+
rubygems_version: 2.7.6
|
192
179
|
signing_key:
|
193
180
|
specification_version: 4
|
194
181
|
summary: A scheduler process to replace cron.
|