zhong 0.1.9 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +30 -0
- data/lib/zhong/job.rb +24 -12
- data/lib/zhong/scheduler.rb +13 -12
- data/lib/zhong/version.rb +1 -1
- data/test/test_library.rb +20 -0
- data/test/test_web.rb +14 -0
- metadata +2 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 902b025d6f1afe4edfa0f2cb6f6639bc0ff558ff
|
4
|
+
data.tar.gz: 939fedf9667d0dc26f7fb1adcddedd5cbe76deba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16ce76fbea779cffa736f3c48f99bca3fa0ac0ba27e46261f20c41c037143317cb406715e8e866da4495dcf33a20a11830a37b6bd1b41ac9d64c39568c7e1896
|
7
|
+
data.tar.gz: 85abd697abfe5fa1ffd7513ae8e146f34c3360234f2685f9f89aec5224b259d086e25097614836ba90ebe8bc2347b8ea324051d87ea5e6813acc845267de96bf
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -53,12 +53,42 @@ Zhong.schedule do
|
|
53
53
|
puts "#{job} ran?: #{ran}"
|
54
54
|
end
|
55
55
|
|
56
|
+
on(:before_disable) do |job|
|
57
|
+
puts "#{job} is going to be disabled"
|
58
|
+
end
|
59
|
+
|
60
|
+
on(:after_disable) do |job|
|
61
|
+
puts "#{job} disabled"
|
62
|
+
end
|
63
|
+
|
64
|
+
on(:before_enable) do |job|
|
65
|
+
puts "#{job} is going to be enabled"
|
66
|
+
end
|
67
|
+
|
68
|
+
on(:after_enable) do |job|
|
69
|
+
puts "#{job} enabled"
|
70
|
+
end
|
71
|
+
|
56
72
|
error_handler do |e, job|
|
57
73
|
puts "dang, #{job} messed up: #{e}"
|
58
74
|
end
|
59
75
|
end
|
60
76
|
```
|
61
77
|
|
78
|
+
## Web UI
|
79
|
+
|
80
|
+
Zhong comes with a web application that can display jobs, their last run and
|
81
|
+
enable/disable them.
|
82
|
+
|
83
|
+
### Rails
|
84
|
+
|
85
|
+
Add the following to your config/routes.rb:
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
require 'zhong/web'
|
89
|
+
mount Zhong::Web, at: "zhong"
|
90
|
+
```
|
91
|
+
|
62
92
|
## History
|
63
93
|
|
64
94
|
View the [changelog](https://github.com/nickelser/zhong/blob/master/CHANGELOG.md).
|
data/lib/zhong/job.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
module Zhong
|
2
2
|
class Job
|
3
|
-
|
3
|
+
extend Forwardable
|
4
|
+
def_delegators Zhong, :redis, :tz, :logger, :heartbeat_key
|
4
5
|
|
5
|
-
|
6
|
+
attr_reader :name, :category, :last_ran, :at, :every, :id
|
7
|
+
|
8
|
+
def initialize(job_name, config = {}, callbacks = {}, &block)
|
6
9
|
@name = job_name
|
7
10
|
@category = config[:category]
|
8
11
|
@logger = config[:logger]
|
9
12
|
@config = config
|
13
|
+
@callbacks = callbacks
|
10
14
|
|
11
15
|
@at = config[:at] ? At.parse(config[:at], grace: config.fetch(:grace, 15.minutes)) : nil
|
12
16
|
@every = config[:every] ? Every.parse(config[:every]) : nil
|
@@ -15,8 +19,6 @@ module Zhong
|
|
15
19
|
|
16
20
|
@block = block
|
17
21
|
|
18
|
-
@redis = config[:redis]
|
19
|
-
@tz = config[:tz]
|
20
22
|
@if = config[:if]
|
21
23
|
@long_running_timeout = config[:long_running_timeout]
|
22
24
|
@running = false
|
@@ -88,20 +90,24 @@ module Zhong
|
|
88
90
|
end
|
89
91
|
|
90
92
|
def refresh_last_ran
|
91
|
-
last_ran_val =
|
93
|
+
last_ran_val = redis.get(last_ran_key)
|
92
94
|
@last_ran = last_ran_val ? Time.at(last_ran_val.to_i) : nil
|
93
95
|
end
|
94
96
|
|
95
97
|
def disable
|
96
|
-
|
98
|
+
fire_callbacks(:before_disable, self)
|
99
|
+
redis.set(disabled_key, "true")
|
100
|
+
fire_callbacks(:after_disable, self)
|
97
101
|
end
|
98
102
|
|
99
103
|
def enable
|
100
|
-
|
104
|
+
fire_callbacks(:before_enable, self)
|
105
|
+
redis.del(disabled_key)
|
106
|
+
fire_callbacks(:after_enable, self)
|
101
107
|
end
|
102
108
|
|
103
109
|
def disabled?
|
104
|
-
|
110
|
+
!redis.get(disabled_key).nil?
|
105
111
|
end
|
106
112
|
|
107
113
|
def to_s
|
@@ -115,7 +121,7 @@ module Zhong
|
|
115
121
|
end
|
116
122
|
|
117
123
|
def clear
|
118
|
-
|
124
|
+
redis.del(last_ran_key)
|
119
125
|
end
|
120
126
|
|
121
127
|
def last_ran_key
|
@@ -136,10 +142,16 @@ module Zhong
|
|
136
142
|
|
137
143
|
private
|
138
144
|
|
145
|
+
def fire_callbacks(event, *args)
|
146
|
+
@callbacks[event].to_a.map do |callback|
|
147
|
+
callback.call(*args)
|
148
|
+
end.compact.all? # do not skip on nils
|
149
|
+
end
|
150
|
+
|
139
151
|
# if the @at value is changed across runs, the last_run becomes invalid
|
140
152
|
# so clear it
|
141
153
|
def clear_last_ran_if_at_changed
|
142
|
-
previous_at_msgpack =
|
154
|
+
previous_at_msgpack = redis.get(desired_at_key)
|
143
155
|
|
144
156
|
if previous_at_msgpack
|
145
157
|
previous_at = At.deserialize(previous_at_msgpack)
|
@@ -150,7 +162,7 @@ module Zhong
|
|
150
162
|
end
|
151
163
|
end
|
152
164
|
|
153
|
-
|
165
|
+
redis.set(desired_at_key, @at.serialize)
|
154
166
|
end
|
155
167
|
|
156
168
|
def run_every?(time)
|
@@ -167,7 +179,7 @@ module Zhong
|
|
167
179
|
|
168
180
|
def ran!(time)
|
169
181
|
@last_ran = time
|
170
|
-
|
182
|
+
redis.set(last_ran_key, @last_ran.to_i)
|
171
183
|
end
|
172
184
|
|
173
185
|
def redis_lock
|
data/lib/zhong/scheduler.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
module Zhong
|
2
2
|
class Scheduler
|
3
|
-
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def_delegators Zhong, :redis, :tz, :logger, :heartbeat_key
|
6
|
+
attr_reader :jobs
|
4
7
|
|
5
8
|
DEFAULT_CONFIG = {
|
6
9
|
timeout: 0.5,
|
7
10
|
grace: 15.minutes,
|
8
|
-
long_running_timeout: 5.minutes
|
9
|
-
tz: nil,
|
10
|
-
heartbeat_key: "zhong:heartbeat",
|
11
|
+
long_running_timeout: 5.minutes
|
11
12
|
}.freeze
|
12
13
|
|
13
14
|
def initialize(config = {})
|
@@ -15,9 +16,6 @@ module Zhong
|
|
15
16
|
@callbacks = {}
|
16
17
|
@config = DEFAULT_CONFIG.merge(config)
|
17
18
|
|
18
|
-
@logger = @config[:logger]
|
19
|
-
@redis = @config[:redis]
|
20
|
-
@tz = @config[:tz]
|
21
19
|
@category = nil
|
22
20
|
@error_handler = nil
|
23
21
|
@running = false
|
@@ -44,7 +42,7 @@ module Zhong
|
|
44
42
|
def every(period, name, opts = {}, &block)
|
45
43
|
raise "must specify a period for #{name} (#{caller.first})" unless period
|
46
44
|
|
47
|
-
job = Job.new(name, opts.merge(@config).merge(every: period, category: @category), &block)
|
45
|
+
job = Job.new(name, opts.merge(@config).merge(every: period, category: @category), @callbacks, &block)
|
48
46
|
|
49
47
|
raise "duplicate job #{job}" if jobs.key?(job.id)
|
50
48
|
|
@@ -57,7 +55,10 @@ module Zhong
|
|
57
55
|
end
|
58
56
|
|
59
57
|
def on(event, &block)
|
60
|
-
|
58
|
+
unless [:before_tick, :after_tick, :before_run, :after_run, :before_disable,
|
59
|
+
:after_disable, :before_enable, :after_enable].include?(event.to_sym)
|
60
|
+
raise "unknown callback #{event}"
|
61
|
+
end
|
61
62
|
(@callbacks[event.to_sym] ||= []) << block
|
62
63
|
end
|
63
64
|
|
@@ -112,9 +113,9 @@ module Zhong
|
|
112
113
|
end
|
113
114
|
|
114
115
|
def redis_time
|
115
|
-
s, ms =
|
116
|
+
s, ms = redis.time # returns [seconds since epoch, microseconds]
|
116
117
|
now = Time.at(s + ms / (10**6))
|
117
|
-
|
118
|
+
tz ? now.in_time_zone(tz) : now
|
118
119
|
end
|
119
120
|
|
120
121
|
private
|
@@ -144,7 +145,7 @@ module Zhong
|
|
144
145
|
end
|
145
146
|
|
146
147
|
def heartbeat(time)
|
147
|
-
|
148
|
+
redis.hset(heartbeat_key, heartbeat_field, time.to_i)
|
148
149
|
end
|
149
150
|
|
150
151
|
def heartbeat_field
|
data/lib/zhong/version.rb
CHANGED
data/test/test_library.rb
CHANGED
@@ -24,7 +24,27 @@ class TestLibrary < Minitest::Test
|
|
24
24
|
assert_equal true, Zhong.any_running?
|
25
25
|
assert_in_delta Zhong.redis_time.to_f, Time.now.to_f, 1
|
26
26
|
assert_in_delta Zhong.redis_time.to_f, Zhong.latest_heartbeat.to_f, 1
|
27
|
+
refute_empty Zhong.all_heartbeats
|
27
28
|
Zhong.stop
|
28
29
|
t.join
|
29
30
|
end
|
31
|
+
|
32
|
+
def test_redis_change
|
33
|
+
Zhong.schedule { nil }
|
34
|
+
t = Thread.new { Zhong.start }
|
35
|
+
sleep(1)
|
36
|
+
assert_equal true, Zhong.any_running?
|
37
|
+
test_redis = Zhong.redis
|
38
|
+
Zhong.stop
|
39
|
+
t.join
|
40
|
+
Zhong.redis = Redis.new(url: "redis://localhost/15")
|
41
|
+
refute Zhong.any_running?(5.seconds)
|
42
|
+
t = Thread.new { Zhong.start }
|
43
|
+
sleep(1)
|
44
|
+
assert_equal true, Zhong.any_running?
|
45
|
+
assert_in_delta Zhong.redis_time.to_f, Time.now.to_f, 1
|
46
|
+
Zhong.stop
|
47
|
+
Zhong.redis = test_redis
|
48
|
+
t.join
|
49
|
+
end
|
30
50
|
end
|
data/test/test_web.rb
CHANGED
@@ -32,8 +32,13 @@ class TestWeb < Minitest::Test
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def test_disable_job
|
35
|
+
test_before_disable = 0
|
36
|
+
test_after_disable = 0
|
37
|
+
|
35
38
|
Zhong.schedule do
|
36
39
|
every(30.seconds, "test_disable_web_job") { nil }
|
40
|
+
on(:before_disable) { test_before_disable += 1 }
|
41
|
+
on(:after_disable) { test_after_disable += 1 }
|
37
42
|
end
|
38
43
|
|
39
44
|
job = Zhong.scheduler.find_by_name("test_disable_web_job")
|
@@ -48,11 +53,18 @@ class TestWeb < Minitest::Test
|
|
48
53
|
assert_contains "every 30 seconds", last_response.body
|
49
54
|
assert_contains 'name="enable"', last_response.body
|
50
55
|
assert_equal true, job.disabled?
|
56
|
+
assert_equal 1, test_before_disable
|
57
|
+
assert_equal 1, test_after_disable
|
51
58
|
end
|
52
59
|
|
53
60
|
def test_enable_job
|
61
|
+
test_before_enable = 0
|
62
|
+
test_after_enable = 0
|
63
|
+
|
54
64
|
Zhong.schedule do
|
55
65
|
every(12.hours, "test_enable_web_job") { nil }
|
66
|
+
on(:before_enable) { test_before_enable += 1 }
|
67
|
+
on(:after_enable) { test_after_enable += 1 }
|
56
68
|
end
|
57
69
|
|
58
70
|
job = Zhong.scheduler.find_by_name("test_enable_web_job")
|
@@ -67,6 +79,8 @@ class TestWeb < Minitest::Test
|
|
67
79
|
assert_contains "every 12 hours", last_response.body
|
68
80
|
assert_contains 'name="disable"', last_response.body
|
69
81
|
assert_equal false, job.disabled?
|
82
|
+
assert_equal 1, test_before_enable
|
83
|
+
assert_equal 1, test_after_enable
|
70
84
|
end
|
71
85
|
|
72
86
|
def test_heartbeat
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zhong
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Elser
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: suo
|
@@ -288,4 +288,3 @@ test_files:
|
|
288
288
|
- test/test_library.rb
|
289
289
|
- test/test_scheduler.rb
|
290
290
|
- test/test_web.rb
|
291
|
-
has_rdoc:
|