Class: Ztimer

Inherits:
Object
  • Object
show all
Defined in:
lib/ztimer.rb,
lib/ztimer/slot.rb,
lib/ztimer/version.rb,
lib/ztimer/watcher.rb,
lib/ztimer/sorted_store.rb

Overview

Implements a timer which allows to execute a block with a delay, recurrently or asynchronously.

Defined Under Namespace

Classes: Slot, SortedStore, Watcher

Constant Summary collapse

VERSION =
'1.0.0'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(concurrency: 20) ⇒ Ztimer

Returns a new instance of Ztimer.



14
15
16
17
18
19
20
21
22
# File 'lib/ztimer.rb', line 14

def initialize(concurrency: 20)
  @concurrency  = concurrency
  @watcher      = Ztimer::Watcher.new { |slot| execute(slot) }
  @workers_lock = Mutex.new
  @count_lock   = Mutex.new
  @queue        = Queue.new
  @running      = 0
  @count        = 0
end

Instance Attribute Details

#concurrencyObject

Returns the value of attribute concurrency.



12
13
14
# File 'lib/ztimer.rb', line 12

def concurrency
  @concurrency
end

#countObject (readonly)

Returns the value of attribute count.



12
13
14
# File 'lib/ztimer.rb', line 12

def count
  @count
end

#queueObject (readonly)

Returns the value of attribute queue.



12
13
14
# File 'lib/ztimer.rb', line 12

def queue
  @queue
end

#runningObject (readonly)

Returns the value of attribute running.



12
13
14
# File 'lib/ztimer.rb', line 12

def running
  @running
end

#watcherObject (readonly)

Returns the value of attribute watcher.



12
13
14
# File 'lib/ztimer.rb', line 12

def watcher
  @watcher
end

Class Method Details

.method_missing(name, *args, &block) ⇒ Object



141
142
143
144
# File 'lib/ztimer.rb', line 141

def self.method_missing(name, *args, &block)
  @default_instance ||= Ztimer.new(concurrency: 20)
  @default_instance.send(name, *args, &block)
end

Instance Method Details

#after(milliseconds, &callback) ⇒ Object

Execute the code block after the specified delay



36
37
38
39
40
41
42
43
44
# File 'lib/ztimer.rb', line 36

def after(milliseconds, &callback)
  enqueued_at = utc_microseconds
  expires_at  = enqueued_at + milliseconds * 1000
  slot        = Slot.new(enqueued_at, expires_at, -1, &callback)

  add(slot)

  slot
end

#async(&callback) ⇒ Object

Execute the code block asyncrhonously right now



25
26
27
28
29
30
31
32
33
# File 'lib/ztimer.rb', line 25

def async(&callback)
  enqueued_at = utc_microseconds
  slot        = Slot.new(enqueued_at, enqueued_at, -1, &callback)

  incr_counter!
  execute(slot)

  slot
end

#at(datetime, &callback) ⇒ Object

Execute the code block at a specific datetime



47
48
49
50
51
52
53
54
# File 'lib/ztimer.rb', line 47

def at(datetime, &callback)
  enqueued_at = datetime.to_f * 1_000_000

  slot = Slot.new(enqueued_at, enqueued_at, -1, &callback)
  add(slot)

  slot
end

#daily(days, offset: 0, &callback) ⇒ Object

Raises:

  • (ArgumentError)


92
93
94
95
96
# File 'lib/ztimer.rb', line 92

def daily(days, offset: 0, &callback)
  raise ArgumentError, "Days number should be > 0: #{days.inspect}" if days.to_f <= 0

  hourly(days.to_f * 24, offset: offset.to_f * 24, &callback)
end

#day_of_week(day, &callback) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/ztimer.rb', line 98

def day_of_week(day, &callback)
  days = %w[sun mon tue thu wen fri sat]
  current_day = Time.now.wday

  index = day.to_i
  if day.is_a?(String)
    # Find day number by day name
    index = days.index { |day_name| day.strip.downcase == day_name }
    raise ArgumentError, "Invalid week day: #{day.inspect}" if index.nil?
  elsif index.negative? || index > 6
    raise ArgumentError, "Invalid week day: #{day.inspect}"
  end

  offset = 0
  offset = (current_day > index ? index - current_day : current_day - index) if current_day != index

  daily(7, offset: offset, &callback)
end

#days_of_week(*args, &callback) ⇒ Object



117
118
119
# File 'lib/ztimer.rb', line 117

def days_of_week(*args, &callback)
  args.map { |day| day_of_week(day, &callback) }
end

#every(milliseconds, start_at: nil, &callback) ⇒ Object

Execute the code block every N milliseconds. When :start_at is specified, the first execution will start at specified date/time



58
59
60
61
62
63
64
65
66
# File 'lib/ztimer.rb', line 58

def every(milliseconds, start_at: nil, &callback)
  enqueued_at = start_at ? start_at.to_f * 1_000_000 : utc_microseconds
  expires_at = enqueued_at + milliseconds * 1000
  slot = Slot.new(enqueued_at, expires_at, milliseconds * 1000, &callback)

  add(slot)

  slot
end

#hourly(hours, offset: 0, &callback) ⇒ Object

Run ztimer every N hours, starting at the nearest time slot (ex. hourly(2) will run at hour 0, 2, 4, 6, etc.)



88
89
90
# File 'lib/ztimer.rb', line 88

def hourly(hours, offset: 0, &callback)
  minutely(hours.to_f * 60, offset: offset.to_f * 60, &callback)
end

#jobs_countObject



121
122
123
# File 'lib/ztimer.rb', line 121

def jobs_count
  @watcher.jobs
end

#minutely(minutes, offset: 0, &callback) ⇒ Object

Run ztimer every N minutes, starting at the nearest time slot (ex. minutely(2) will run at minute 0, 2, 4, 6, etc.)



83
84
85
# File 'lib/ztimer.rb', line 83

def minutely(minutes, offset: 0, &callback)
  secondly(minutes.to_f * 60, offset: offset.to_f * 60, &callback)
end

#secondly(seconds, offset: 0, &callback) ⇒ Object

Run ztimer every N seconds, starting with the nearest time slot (ex. secondly(5) will run at second 0, 5, 10, 15, etc.)



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/ztimer.rb', line 70

def secondly(seconds, offset: 0, &callback)
  start_time = utc_microseconds
  milliseconds = (seconds.to_f * 1000).to_i
  enqueued_at = start_time - (start_time % (milliseconds * 1000)) + offset * 1_000_000
  expires_at = enqueued_at + milliseconds * 1000

  slot = Slot.new(enqueued_at, expires_at, milliseconds * 1000, &callback)
  add(slot)

  slot
end

#statsObject



132
133
134
135
136
137
138
139
# File 'lib/ztimer.rb', line 132

def stats
  {
    running: @running,
    scheduled: @watcher.jobs,
    executing: @queue.size,
    total: @count
  }
end