класс ActionCable::Channel::Base
Базовый канал Action Cable
Канал предоставляет базовую структуру для группирования поведения в логические единицы при общении через соединение 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
В этом примере подписывающие и отписывающие методы не являются вызываемыми методами, так как они уже были объявлены в 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 120
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 147
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 136 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 141 def method_added(name) # :doc: super clear_action_methods! end
Обновить закэшированные action_methods, когда добавляется новый action_method.
Публичные методы экземпляра
# File actioncable/lib/action_cable/channel/base.rb, line 167
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 182
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 233 def defer_subscription_confirmation! # :doc: @defer_subscription_confirmation_counter.increment end
# File actioncable/lib/action_cable/channel/base.rb, line 237 def defer_subscription_confirmation? # :doc: @defer_subscription_confirmation_counter.value > 0 end
# File actioncable/lib/action_cable/channel/base.rb, line 227 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 245 def reject # :doc: @reject_subscription = true end
# File actioncable/lib/action_cable/channel/base.rb, line 202 def subscribed # :doc: # Override in subclasses end
Вызывается, когда потребитель становится подписчиком канала. Обычно это место для настройки любых потоков, которые вы хотите, чтобы этот канал отправлял подписчику.
# File actioncable/lib/action_cable/channel/base.rb, line 241 def subscription_confirmation_sent? # :doc: @subscription_confirmation_sent end
# File actioncable/lib/action_cable/channel/base.rb, line 249 def subscription_rejected? # :doc: @reject_subscription end
# File actioncable/lib/action_cable/channel/base.rb, line 214
def transmit(data, via: nil) # :doc:
logger.debug do
status = "#{self.class.name} transmitting #{data.inspect.truncate(300)}"
status += " (via #{via})" if via
status
end
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 208 def unsubscribed # :doc: # Override in subclasses end
Вызывается, когда потребитель разорвал свое кабельное соединение. Может использоваться для очистки соединений или пометки пользователей как находящихся в автономном режиме или тому подобное.
© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.