delayer 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +0 -2
- data/lib/delayer.rb +3 -6
- data/lib/delayer/delayed_procedure.rb +82 -0
- data/lib/delayer/extend.rb +32 -4
- data/lib/delayer/version.rb +1 -1
- data/test/test_delayer.rb +98 -0
- metadata +4 -4
- data/.circleci/config.yml +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2630bd1444004b5d79a42afc7e6c0a456eaa615c2d98ff7403f36a035951c9d
|
4
|
+
data.tar.gz: 97a1594619a9352dee6298fb4a13d0684b8e75b2cd2e0fa973dac3ae8641ee11
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e96977c14039b5dcae415c6525a9e8839b13de2922b495a402f3678046be8f0e29cc9ad59cf122210afa5fbc67a1422a90c773c839ecd062e380821f70ef36cc
|
7
|
+
data.tar.gz: d625b3e90a7288a3dac9e8d020e99e1ab7f707bcdbee9298ff3674c4f25ab56a4d45c91a9348d6bac1df632bfd50b39734e57ab92a670a3c44eccfd1bd52b936
|
data/README.md
CHANGED
data/lib/delayer.rb
CHANGED
@@ -3,6 +3,7 @@ require "delayer/version"
|
|
3
3
|
require "delayer/error"
|
4
4
|
require "delayer/extend"
|
5
5
|
require "delayer/procedure"
|
6
|
+
require "delayer/delayed_procedure"
|
6
7
|
require "monitor"
|
7
8
|
|
8
9
|
module Delayer
|
@@ -32,12 +33,8 @@ module Delayer
|
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
35
|
-
def method_missing(*args,
|
36
|
-
|
37
|
-
(@default ||= generate_class).__send__(*args, &proc)
|
38
|
-
else
|
39
|
-
(@default ||= generate_class).__send__(*args, **kwrest, &proc)
|
40
|
-
end
|
36
|
+
def method_missing(*args, &proc)
|
37
|
+
(@default ||= generate_class).__send__(*args, &proc)
|
41
38
|
end
|
42
39
|
end
|
43
40
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Delayer
|
4
|
+
class DelayedProcedure
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
attr_reader :state, :delayer, :reserve_at, :right
|
8
|
+
|
9
|
+
def initialize(delayer, delay:, &proc)
|
10
|
+
@delayer = delayer
|
11
|
+
@proc = proc
|
12
|
+
@reserve_at = Process.clock_gettime(Process::CLOCK_MONOTONIC) + delay
|
13
|
+
@left = @right = @cancel = nil
|
14
|
+
@size = 1
|
15
|
+
@delayer.class.reserve(self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def size
|
19
|
+
@size
|
20
|
+
end
|
21
|
+
|
22
|
+
def add(other)
|
23
|
+
return self unless other
|
24
|
+
if self >= other
|
25
|
+
other.add(self)
|
26
|
+
else
|
27
|
+
@left, *children, @right = [@left, @right, other].compact.sort
|
28
|
+
child = children.first
|
29
|
+
if child
|
30
|
+
if @right.right
|
31
|
+
@left = @left.add(child)
|
32
|
+
else
|
33
|
+
@right = @right.add(child)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
@size += 1
|
37
|
+
self
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def next
|
42
|
+
@left&.add(@right).tap do
|
43
|
+
@left = @right = nil
|
44
|
+
freeze
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def register
|
49
|
+
if !canceled?
|
50
|
+
@procedure = Procedure.new(@delayer, &@proc)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def <=>(other)
|
55
|
+
@reserve_at <=> other.reserve_at
|
56
|
+
end
|
57
|
+
|
58
|
+
# Cancel this job
|
59
|
+
# ==== Exception
|
60
|
+
# Delayer::TooLate :: if already called run()
|
61
|
+
# ==== Return
|
62
|
+
# self
|
63
|
+
def cancel
|
64
|
+
if @procedure
|
65
|
+
@procedure.cancel
|
66
|
+
else
|
67
|
+
@cancel = true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Return true if canceled this task
|
72
|
+
# ==== Return
|
73
|
+
# true if canceled this task
|
74
|
+
def canceled?
|
75
|
+
if @procedure
|
76
|
+
@procedure.canceled?
|
77
|
+
else
|
78
|
+
@cancel
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/delayer/extend.rb
CHANGED
@@ -19,10 +19,14 @@ module Delayer
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
def initialize(priority = self.class.instance_eval { @default_priority }, *_args, &proc)
|
22
|
+
def initialize(priority = self.class.instance_eval { @default_priority }, *_args, delay: 0, &proc)
|
23
23
|
self.class.validate_priority priority
|
24
24
|
@priority = priority
|
25
|
-
|
25
|
+
if delay == 0
|
26
|
+
@procedure = Procedure.new(self, &proc)
|
27
|
+
else
|
28
|
+
@procedure = DelayedProcedure.new(self, delay: delay, &proc)
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
32
|
# Cancel this job
|
@@ -57,11 +61,16 @@ module Delayer
|
|
57
61
|
# ==== Return
|
58
62
|
# self
|
59
63
|
def run(current_expire = @expire)
|
64
|
+
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
65
|
+
while @reserve&.reserve_at&.<=(start_time)
|
66
|
+
@reserve.register
|
67
|
+
@reserve = @reserve.next
|
68
|
+
end
|
60
69
|
if current_expire == 0
|
61
70
|
run_once until empty?
|
62
71
|
else
|
63
|
-
@end_time =
|
64
|
-
run_once while !empty? && (@end_time >=
|
72
|
+
@end_time = start_time + @expire
|
73
|
+
run_once while !empty? && (@end_time >= Process.clock_gettime(Process::CLOCK_MONOTONIC))
|
65
74
|
@end_time = nil
|
66
75
|
end
|
67
76
|
if @remain_hook
|
@@ -146,6 +155,25 @@ module Delayer
|
|
146
155
|
self
|
147
156
|
end
|
148
157
|
|
158
|
+
# Register reserved job.
|
159
|
+
# It does not execute immediately.
|
160
|
+
# it calls register() in _procedure.reserve_at_.
|
161
|
+
# ==== Args
|
162
|
+
# [procedure] job(Delayer::DelayedProcedure)
|
163
|
+
# ==== Return
|
164
|
+
# self
|
165
|
+
def reserve(procedure)
|
166
|
+
priority = procedure.delayer.priority
|
167
|
+
lock.synchronize do
|
168
|
+
if @reserve
|
169
|
+
@reserve = @reserve.add(procedure)
|
170
|
+
else
|
171
|
+
@reserve = procedure
|
172
|
+
end
|
173
|
+
end
|
174
|
+
self
|
175
|
+
end
|
176
|
+
|
149
177
|
def register_remain_hook(&proc)
|
150
178
|
@remain_hook = proc
|
151
179
|
end
|
data/lib/delayer/version.rb
CHANGED
data/test/test_delayer.rb
CHANGED
@@ -353,4 +353,102 @@ class TestDelayer < Test::Unit::TestCase
|
|
353
353
|
end
|
354
354
|
end
|
355
355
|
|
356
|
+
def test_timer
|
357
|
+
delayer = Delayer.generate_class expire: 0.01
|
358
|
+
a = []
|
359
|
+
delayer.new(delay: 0.01) { a << 0 }
|
360
|
+
delayer.new { a << 1 }
|
361
|
+
|
362
|
+
delayer.run
|
363
|
+
|
364
|
+
delayer.new { a << 2 }
|
365
|
+
sleep 0.1
|
366
|
+
|
367
|
+
delayer.run
|
368
|
+
|
369
|
+
delayer.new { a << 3 }
|
370
|
+
|
371
|
+
delayer.run
|
372
|
+
|
373
|
+
assert_equal([1, 2, 0, 3], a)
|
374
|
+
end
|
375
|
+
|
376
|
+
def test_plural_timer
|
377
|
+
delayer = Delayer.generate_class expire: 0.01
|
378
|
+
a = []
|
379
|
+
delayer.new(delay: 0.01) { a << 0 }
|
380
|
+
delayer.new(delay: 0.11) { a << 1 }
|
381
|
+
delayer.new { a << 2 }
|
382
|
+
|
383
|
+
delayer.run
|
384
|
+
|
385
|
+
delayer.new { a << 3 }
|
386
|
+
sleep 0.1
|
387
|
+
|
388
|
+
delayer.run
|
389
|
+
sleep 0.1
|
390
|
+
|
391
|
+
delayer.new { a << 4 }
|
392
|
+
|
393
|
+
delayer.run
|
394
|
+
|
395
|
+
assert_equal([2, 3, 0, 4, 1], a)
|
396
|
+
end
|
397
|
+
|
398
|
+
def test_many_timer
|
399
|
+
delayer = Delayer.generate_class expire: 0.01
|
400
|
+
a = []
|
401
|
+
(0..10).to_a.shuffle.each do |i|
|
402
|
+
delayer.new(delay: i / 100.0) { a << i }
|
403
|
+
end
|
404
|
+
|
405
|
+
sleep 0.1
|
406
|
+
|
407
|
+
delayer.run
|
408
|
+
|
409
|
+
assert_equal((0..10).to_a, a)
|
410
|
+
end
|
411
|
+
|
412
|
+
def test_cancel_timer
|
413
|
+
delayer = Delayer.generate_class
|
414
|
+
a = 0
|
415
|
+
delayer.new(delay: 0.01) { a += 1 }
|
416
|
+
d = delayer.new(delay: 0.01) { a += 2 }
|
417
|
+
delayer.new(delay: 0.01) { a += 4 }
|
418
|
+
|
419
|
+
assert_equal(0, a)
|
420
|
+
d.cancel
|
421
|
+
sleep 0.1
|
422
|
+
delayer.run
|
423
|
+
assert_equal(5, a)
|
424
|
+
end
|
425
|
+
|
426
|
+
def test_cancel_timer_after_expire
|
427
|
+
delayer = Delayer.generate_class
|
428
|
+
a = 0
|
429
|
+
delayer.new(delay: 0.01) { a += 1 }
|
430
|
+
d = delayer.new(delay: 0.01) { a += 2 }
|
431
|
+
delayer.new{ d.cancel }
|
432
|
+
delayer.new(delay: 0.01) { a += 4 }
|
433
|
+
|
434
|
+
assert_equal(0, a)
|
435
|
+
sleep 0.1
|
436
|
+
delayer.run
|
437
|
+
assert_equal(5, a)
|
438
|
+
end
|
439
|
+
|
440
|
+
def test_reserve_new_timer_after_cancel
|
441
|
+
delayer = Delayer.generate_class
|
442
|
+
a = 0
|
443
|
+
delayer.new(delay: 0.01) { a += 1 }
|
444
|
+
d = delayer.new(delay: 0.02) { a += 2 }
|
445
|
+
d.cancel
|
446
|
+
delayer.new(delay: 0.03) { a += 4 }
|
447
|
+
|
448
|
+
assert_equal(0, a)
|
449
|
+
sleep 0.1
|
450
|
+
delayer.run
|
451
|
+
assert_equal(5, a)
|
452
|
+
end
|
453
|
+
|
356
454
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delayer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Toshiaki Asai
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -65,7 +65,6 @@ executables: []
|
|
65
65
|
extensions: []
|
66
66
|
extra_rdoc_files: []
|
67
67
|
files:
|
68
|
-
- ".circleci/config.yml"
|
69
68
|
- ".gitignore"
|
70
69
|
- Gemfile
|
71
70
|
- LICENSE.txt
|
@@ -73,6 +72,7 @@ files:
|
|
73
72
|
- Rakefile
|
74
73
|
- delayer.gemspec
|
75
74
|
- lib/delayer.rb
|
75
|
+
- lib/delayer/delayed_procedure.rb
|
76
76
|
- lib/delayer/error.rb
|
77
77
|
- lib/delayer/extend.rb
|
78
78
|
- lib/delayer/procedure.rb
|
@@ -98,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
98
|
- !ruby/object:Gem::Version
|
99
99
|
version: '0'
|
100
100
|
requirements: []
|
101
|
-
rubygems_version: 3.
|
101
|
+
rubygems_version: 3.0.6
|
102
102
|
signing_key:
|
103
103
|
specification_version: 4
|
104
104
|
summary: Delay the processing
|
data/.circleci/config.yml
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
version: '2.1'
|
2
|
-
|
3
|
-
executors:
|
4
|
-
ruby:
|
5
|
-
parameters:
|
6
|
-
tag:
|
7
|
-
type: string
|
8
|
-
docker:
|
9
|
-
- image: circleci/ruby:<< parameters.tag >>
|
10
|
-
|
11
|
-
jobs:
|
12
|
-
build:
|
13
|
-
parameters:
|
14
|
-
ruby-version:
|
15
|
-
type: string
|
16
|
-
executor:
|
17
|
-
name: ruby
|
18
|
-
tag: << parameters.ruby-version >>
|
19
|
-
steps:
|
20
|
-
- checkout
|
21
|
-
- run:
|
22
|
-
name: Which bundler?
|
23
|
-
command: bundle -v
|
24
|
-
- run:
|
25
|
-
command: bundle install --path vendor/bundle
|
26
|
-
- run:
|
27
|
-
name: test
|
28
|
-
command: bundle exec rake test
|
29
|
-
workflows:
|
30
|
-
build:
|
31
|
-
jobs:
|
32
|
-
- build:
|
33
|
-
name: 'ruby-2.5'
|
34
|
-
ruby-version: '2.5.7'
|
35
|
-
- build:
|
36
|
-
name: 'ruby-2.6'
|
37
|
-
ruby-version: '2.6.5'
|
38
|
-
- build:
|
39
|
-
name: 'ruby-2.7'
|
40
|
-
ruby-version: '2.7.0'
|