класс ActionCable::Channel::Base
Канал предоставляет базовый структуру для группирования поведения в логические блоки при общении через подключение WebSocket. Можно представить канал как вид контроллера, но способного, помимо ответа на прямые запросы от подписчика, ещё и отправлять ему контент.
Объекты Channel долгоживущие. Объект канала будет инициализирован, когда потребитель кабеля станет подписчиком, и останется живым до момента отключения потребителя. Это может быть от нескольких секунд до минут, часов или даже дней. Это означает, что необходимо быть особенно внимательным, чтобы в канале не было ничего, что бы привело к чрезмерному росту потребления памяти. Ссылки на объекты канала постоянны, поэтому они не будут освобождаться, как обычно происходит с экземпляром контроллера, который удаляется после каждого запроса.
Долгоживущие каналы (и подключения) также означают, что вы несёте ответственность за обеспечение актуальности данных. Если вы сохранили ссылку на запись пользователя, но имя пользователя было изменено, пока эта ссылка хранится, вы можете отправлять устаревшие данные, если не предпримите меры предосторожности, чтобы этого избежать.
Преимущества долгоживущих экземпляров канала заключаются в том, что вы можете использовать переменные экземпляра для сохранения ссылок на объекты, с которыми будущие запросы от подписчиков могут взаимодействовать. Вот быстрый пример:
class ChatChannel < ApplicationCable::Channel
def subscribed
@room = Chat::Room[params[:room_number]]
end
def speak(data)
@room.speak data, user: current_user
end
end
Действие speak просто использует объект Chat::Room, который был создан, когда канал был впервые подписан потребителем, когда этот подписчик хочет что-то сказать в комнате.
Обработка действий
В отличие от подклассов ActionController::Base, каналы не придерживаются RESTful ограничений для своих действий. Вместо этого Action Cable работает по модели вызова удалённых процедур. Вы можете объявить любой открытый метод в канале (необязательно принимающий data аргумент), и этот метод будет автоматически доступен для вызова клиентом.
Пример:
class AppearanceChannel < ApplicationCable::Channel
def subscribed
@connection_token = generate_connection_token
end
def unsubscribed
current_user.disappear @connection_token
end
def appear(data)
current_user.appear @connection_token, on: data['appearing_on']
end
def away
current_user.away @connection_token
end
private
def generate_connection_token
SecureRandom.hex(36)
end
end
В этом примере методы subscribed и unsubscribed не являются вызываемыми методами, так как они уже были объявлены в ActionCable::Channel::Base, но #appear и #away являются. #generate_connection_token также не является вызываемым методом, так как это закрытый метод. Вы увидите, что он принимает параметр data, который затем используется в вызове модели. #away не принимает, так как это просто триггерное действие.
Также обратите внимание, что в этом примере current_user доступен, поскольку он был помечен как идентифицирующий атрибут подключения. Все такие идентификаторы автоматически создадут метод делегирования с таким же именем в экземпляре канала.
Отклонение запросов на подписку
Канал может отклонить запрос на подписку в обратном вызове subscribed, вызвав метод reject:
class ChatChannel < ApplicationCable::Channel
def subscribed
@room = Chat::Room[params[:room_number]]
reject unless current_user.can_access?(@room)
end
end
В этом примере подписка будет отклонена, если у current_user нет доступа к чат-комнате. На стороне клиента вызов Channel#rejected будет вызван, когда сервер отклонит запрос на подписку.
Атрибуты
Методы публичного класса
# File actioncable/lib/action_cable/channel/base.rb, line 113
def action_methods
@action_methods ||= begin
# All public instance methods of this class, including ancestors
methods = (public_instance_methods(true) -
# Except for public instance methods of Base and its ancestors
ActionCable::Channel::Base.public_instance_methods(true) +
# Be sure to include shadowed public instance methods of this class
public_instance_methods(false)).uniq.map(&:to_s)
methods.to_set
end
end Список имён методов, которые следует рассматривать как действия. В него входят все публичные методы экземпляра канала, за исключением внутренних методов (определённых в Base), с добавлением обратно любых внутренних методов, которые всё ещё существуют в самом классе.
Возвращает
-
Set- Набор всех методов, которые следует рассматривать как действия.
# File actioncable/lib/action_cable/channel/base.rb, line 140
def initialize(connection, identifier, params = {})
@connection = connection
@identifier = identifier
@params = params
# When a channel is streaming via pubsub, we want to delay the confirmation
# transmission until pubsub subscription is confirmed.
#
# The counter starts at 1 because it's awaiting a call to #subscribe_to_channel
@defer_subscription_confirmation_counter = Concurrent::AtomicFixnum.new(1)
@reject_subscription = nil
@subscription_confirmation_sent = nil
delegate_connection_identifiers
end Методы приватного класса
# File actioncable/lib/action_cable/channel/base.rb, line 129 def clear_action_methods! # :doc: @action_methods = nil end
::action_methods кэшируются, и иногда возникает необходимость их обновления. ::clear_action_methods! позволяет сделать это, чтобы в следующий раз при выполнении ::action_methods они были перерасчитаны.
# File actioncable/lib/action_cable/channel/base.rb, line 134 def method_added(name) # :doc: super clear_action_methods! end
Обновление кэшированных ::action_methods при добавлении нового action_method.
Методы публичного экземпляра
# File actioncable/lib/action_cable/channel/base.rb, line 160
def perform_action(data)
action = extract_action(data)
if processable_action?(action)
payload = { channel_class: self.class.name, action: action, data: data }
ActiveSupport::Notifications.instrument("perform_action.action_cable", payload) do
dispatch_action(action, data)
end
else
logger.error "Unable to process #{action_signature(action, data)}"
end
end Извлечение имени действия из переданных данных и обработка его через канал. Обработка гарантирует, что запрошенное действие является публичным методом канала, объявленным пользователем (а не одним из обратных вызовов, например, subscribed).
# File actioncable/lib/action_cable/channel/base.rb, line 175
def subscribe_to_channel
run_callbacks :subscribe do
subscribed
end
reject_subscription if subscription_rejected?
ensure_confirmation_sent
end Этот метод вызывается после добавления подписки к соединению и подтверждает или отклоняет подписку.
Методы приватного экземпляра
# File actioncable/lib/action_cable/channel/base.rb, line 222 def defer_subscription_confirmation! # :doc: @defer_subscription_confirmation_counter.increment end
# File actioncable/lib/action_cable/channel/base.rb, line 226 def defer_subscription_confirmation? # :doc: @defer_subscription_confirmation_counter.value > 0 end
# File actioncable/lib/action_cable/channel/base.rb, line 216 def ensure_confirmation_sent # :doc: return if subscription_rejected? @defer_subscription_confirmation_counter.decrement transmit_subscription_confirmation unless defer_subscription_confirmation? end
# File actioncable/lib/action_cable/channel/base.rb, line 234 def reject # :doc: @reject_subscription = true end
# File actioncable/lib/action_cable/channel/base.rb, line 195 def subscribed # :doc: # Override in subclasses end
Вызывается, когда потребитель стал подписчиком канала. Обычно здесь настраиваются потоки, которые этот канал должен отправлять подписчику.
# File actioncable/lib/action_cable/channel/base.rb, line 230 def subscription_confirmation_sent? # :doc: @subscription_confirmation_sent end
# File actioncable/lib/action_cable/channel/base.rb, line 238 def subscription_rejected? # :doc: @reject_subscription end
# File actioncable/lib/action_cable/channel/base.rb, line 207
def transmit(data, via: nil) # :doc:
logger.debug "#{self.class.name} transmitting #{data.inspect.truncate(300)}".tap { |m| m << " (via #{via})" if via }
payload = { channel_class: self.class.name, data: data, via: via }
ActiveSupport::Notifications.instrument("transmit.action_cable", payload) do
connection.transmit identifier: @identifier, message: data
end
end Передача хеша данных подписчику. Хеш автоматически будет обернут в JSON-конверт с правильным идентификатором канала, отмеченным как получатель.
# File actioncable/lib/action_cable/channel/base.rb, line 201 def unsubscribed # :doc: # Override in subclasses end
Вызывается, когда потребитель разорвал соединение. Может использоваться для очистки соединений или для маркировки пользователей как оффлайн и т. п.
© 2004–2018 David Heinemeier Hansson
Licensed under the MIT License.