delayer 1.0.2 → 1.1.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 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'