Spec-Zone.ru › Ruby on Rails 8.1

class ActionCable::Channel::Base

Родительский класс:
Object
Подключённые модули:
ActionCable::Channel::Callbacks, ActionCable::Channel::PeriodicTimers, ActionCable::Channel::Streams, ActionCable::Channel::Naming, ActionCable::Channel::Broadcasting, ActiveSupport::Rescuable

Action Cable 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 также недоступен для вызова, поскольку он является закрытым. Обратите внимание, что appear принимает параметр 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 будет вызван, когда сервер отклонит запрос на подписку.

Атрибуты

connection [R]
identifier [R]
params [R]

Открытые методы класса

action_methods () Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 128
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) -
      # Except the internal methods
      internal_methods).uniq

    methods.map!(&:name)
    methods.to_set
  end
end

Список имён методов, которые следует считать действиями. В него входят все открытые методы экземпляра канала, кроме внутренних методов (определённых в Base), а также добавляются любые внутренние методы, которые при этом существуют непосредственно в классе.

Возвращает

  • Set — набор всех методов, которые следует считать действиями.

new (connection, identifier, params = {}) Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 163
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
  @unsubscribed = false

  delegate_connection_identifiers
end

Закрытые методы класса

clear_action_methods! () Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 148
def clear_action_methods! # :doc:
  @action_methods = nil
end

action_methods кэшируются, и иногда возникает необходимость обновить их. Метод ::clear_action_methods! позволяет это сделать, чтобы при следующем вызове action_methods они были пересчитаны.

method_added (name) Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 153
def method_added(name) # :doc:
  super
  clear_action_methods!
end

Обновляет кэшированный action_methods при добавлении нового action_method.

Вызывает метод суперкласса

Открытые методы экземпляра

perform_action (data) Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 184
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).

subscribe_to_channel () Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 199
def subscribe_to_channel
  run_callbacks :subscribe do
    subscribed
  end

  reject_subscription if subscription_rejected?
  ensure_confirmation_sent
end

Этот метод вызывается после добавления подписки к соединению и подтверждает или отклоняет её.

Закрытые методы экземпляра

defer_subscription_confirmation! () Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 258
def defer_subscription_confirmation! # :doc:
  @defer_subscription_confirmation_counter.increment
end
defer_subscription_confirmation? () Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 262
def defer_subscription_confirmation? # :doc:
  @defer_subscription_confirmation_counter.value > 0
end
ensure_confirmation_sent () Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 252
def ensure_confirmation_sent # :doc:
  return if subscription_rejected?
  @defer_subscription_confirmation_counter.decrement
  transmit_subscription_confirmation unless defer_subscription_confirmation?
end
reject () Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 270
def reject # :doc:
  @reject_subscription = true
end
subscribed () Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 226
def subscribed # :doc:
  # Override in subclasses
end

Вызывается, когда потребитель становится подписчиком канала. Обычно здесь настраиваются потоки, которые канал будет отправлять подписчику.

subscription_confirmation_sent? () Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 266
def subscription_confirmation_sent? # :doc:
  @subscription_confirmation_sent
end
subscription_rejected? () Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 274
def subscription_rejected? # :doc:
  @reject_subscription
end
transmit (data, via: nil) Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 239
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-конверт с корректным идентификатором канала в качестве получателя.

unsubscribed () Показать исходный код
# File actioncable/lib/action_cable/channel/base.rb, line 232
def unsubscribed # :doc:
  # Override in subclasses
end

Вызывается, когда потребитель разрывает кабельное соединение. Может использоваться для очистки соединений, отметки пользователей как находящихся не в сети и тому подобного.

© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API