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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f45ebc21cd1a3e765702c2bc30e1a6244fbc0ab5b6568dd7152ea4f3c7bdc993
4
- data.tar.gz: 70386ae1784b5bcc7ce417e02879dd12723a61f3b8e6100dd281093d50787d47
3
+ metadata.gz: b2630bd1444004b5d79a42afc7e6c0a456eaa615c2d98ff7403f36a035951c9d
4
+ data.tar.gz: 97a1594619a9352dee6298fb4a13d0684b8e75b2cd2e0fa973dac3ae8641ee11
5
5
  SHA512:
6
- metadata.gz: f451adec9a6f7eb037c15ce348d3dd897e0f68bb8c496c00435cd4d09c9757794c725eb1964b9694bc355ecc85f3e0ae2899a94e06a83b2a54cd03819ae130f7
7
- data.tar.gz: 0a430ec5704743c416a1d5cf900378062ec5bbe22dcac0458e69c0b9cf28fb0eed2c903849c18830250e341484a9ed54da2ea80286cb54744cd7c61977b28009
6
+ metadata.gz: e96977c14039b5dcae415c6525a9e8839b13de2922b495a402f3678046be8f0e29cc9ad59cf122210afa5fbc67a1422a90c773c839ecd062e380821f70ef36cc
7
+ data.tar.gz: d625b3e90a7288a3dac9e8d020e99e1ab7f707bcdbee9298ff3674c4f25ab56a4d45c91a9348d6bac1df632bfd50b39734e57ab92a670a3c44eccfd1bd52b936
data/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # Delayer
2
2
 
3
- [![toshia](https://circleci.com/gh/toshia/delayer.svg?style=svg)](https://circleci.com/gh/toshia/delayer)
4
-
5
3
  Delay Any task. Similar priority-queue.
6
4
 
7
5
  ## Installation
@@ -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, **kwrest, &proc)
36
- if kwrest.empty?
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
@@ -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
- @procedure = Procedure.new(self, &proc)
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 = Time.new.to_f + @expire
64
- run_once while !empty? && (@end_time >= Time.new.to_f)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Delayer
4
- VERSION = '1.0.2'
4
+ VERSION = '1.1.0'
5
5
  end
@@ -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.2
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-12 00:00:00.000000000 Z
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.1.2
101
+ rubygems_version: 3.0.6
102
102
  signing_key:
103
103
  specification_version: 4
104
104
  summary: Delay the processing
@@ -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'