#ruby-on-rails #actioncable #hotwire-rails
#ruby-on-rails #actioncable #горячая проволока-рельсы
Вопрос:
Я хочу транслировать обновления модели с помощью Hotwire в Rails. В документации упоминается создание потока с использованием модели, но я хочу создать динамический поток для каждого пользователя, чтобы изменения в моделях, сделанные пользователем, передавались только этому пользователю.
У меня есть модель видеоконференции, которая имеет и принадлежит многим пользователям через объединяющую таблицу, поэтому модель выглядит следующим образом, и у меня есть трансляция как таковая
class VideoCall < ApplicationRecord
has_and_belongs_to_many :users
broadcasts_to ->(video_call) {
video_call.users.map{|u| "video_calls:user_#{u.id}"}
}, inserts_by: :prepend
# more code here
end
В шаблоне ERB, где я хочу получать обновления для модели, которую я добавил
<%= turbo_stream_from "video_calls:user_#{current_user.id}" %>
Это работает, если у меня есть один пользователь в таблице users_video_calls. Но как только в video_call появляется несколько пользователей, он не работает, и я хочу транслировать в поток всех этих пользователей.
Является ли подход, который я использую, правильным и как я могу добиться динамической трансляции потоков нескольким пользователям.
Ответ №1:
Итак, я решил это следующим образом. Глядя на исходный код, я понял, что имеет смысл переопределить три обратных вызова, которые вызываются при создании, обновлении и уничтожении. Итак, я удалил этот код
broadcasts_to ->(video_call) {
video_call.users.map{|u| "video_calls:user_#{u.id}"}
}, inserts_by: :prepend
и заменил его этим
after_create_commit -> {
self.users.each do |u|
broadcast_action_later_to "video_calls:users_#{u.id}", action: :prepend, target: broadcast_target_default
end
}
after_update_commit -> {
self.users.each do |u|
broadcast_replace_later_to "video_calls:users_#{u.id}"
end
}
after_destroy_commit -> {
self.users.each do |u|
broadcast_remove_to "video_calls:users_#{u.id}", action: :prepend, target: broadcast_target_default
end
}
В приведенном выше коде теперь я могу перебирать каждую связанную модель, которая в моем случае — это много пользователей на видеозвонок, и транслировать на имя канала, содержащее идентификатор пользователя. Таким образом, все пользователи, подписавшиеся на видеозвонок, получат только обновление, а не все.
Комментарии:
1. Выглядит хорошо. Но я не думаю, что вам нужно
action: :prepend
в вашемbroadcast_remove_to
заявлении.2. Я давно хотел сделать то же самое — использовать Hotwire для управления списком сообщений, ограниченным для вошедшего в систему пользователя. Спасибо за доработку вашего решения.
3. Спасибо, хорошее решение. Однако after_destroy_commit не работает, если вы хотите обновить представление одного и того же пользователя (допустим, веб-сайт открыт в 2 разных браузерах). Причина в том, что self.users не содержит пользователя, который только что удалил все, что есть self (в моем случае это ссылки). Я не уверен, как это сделать. Есть идеи?
4. Вы можете извлечь пользователей перед вызовом destroy.