mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
78 lines
2.6 KiB
Ruby
78 lines
2.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module ActionCable
|
|
module Channel
|
|
module PeriodicTimers
|
|
extend ActiveSupport::Concern
|
|
|
|
included do
|
|
class_attribute :periodic_timers, instance_reader: false, default: []
|
|
|
|
after_subscribe :start_periodic_timers
|
|
after_unsubscribe :stop_periodic_timers
|
|
end
|
|
|
|
module ClassMethods
|
|
# Periodically performs a task on the channel, like updating an online
|
|
# user counter, polling a backend for new status messages, sending
|
|
# regular "heartbeat" messages, or doing some internal work and giving
|
|
# progress updates.
|
|
#
|
|
# Pass a method name or lambda argument or provide a block to call.
|
|
# Specify the calling period in seconds using the <tt>every:</tt>
|
|
# keyword argument.
|
|
#
|
|
# periodically :transmit_progress, every: 5.seconds
|
|
#
|
|
# periodically every: 3.minutes do
|
|
# transmit action: :update_count, count: current_count
|
|
# end
|
|
#
|
|
def periodically(callback_or_method_name = nil, every:, &block)
|
|
callback =
|
|
if block_given?
|
|
raise ArgumentError, "Pass a block or provide a callback arg, not both" if callback_or_method_name
|
|
block
|
|
else
|
|
case callback_or_method_name
|
|
when Proc
|
|
callback_or_method_name
|
|
when Symbol
|
|
-> { __send__ callback_or_method_name }
|
|
else
|
|
raise ArgumentError, "Expected a Symbol method name or a Proc, got #{callback_or_method_name.inspect}"
|
|
end
|
|
end
|
|
|
|
unless every.kind_of?(Numeric) && every > 0
|
|
raise ArgumentError, "Expected every: to be a positive number of seconds, got #{every.inspect}"
|
|
end
|
|
|
|
self.periodic_timers += [[ callback, every: every ]]
|
|
end
|
|
end
|
|
|
|
private
|
|
def active_periodic_timers
|
|
@active_periodic_timers ||= []
|
|
end
|
|
|
|
def start_periodic_timers
|
|
self.class.periodic_timers.each do |callback, options|
|
|
active_periodic_timers << start_periodic_timer(callback, every: options.fetch(:every))
|
|
end
|
|
end
|
|
|
|
def start_periodic_timer(callback, every:)
|
|
connection.server.event_loop.timer every do
|
|
connection.worker_pool.async_exec self, connection: connection, &callback
|
|
end
|
|
end
|
|
|
|
def stop_periodic_timers
|
|
active_periodic_timers.each { |timer| timer.shutdown }
|
|
active_periodic_timers.clear
|
|
end
|
|
end
|
|
end
|
|
end
|