虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

Hotwire(Turbo Streams) 任意のタイミングでブロードキャストしたい

皆さん、こんにちは。おっくんです。

最近は HotWire に関する記事を続けていますが、ラボ内のメンバーから前回の記事について、とある一つの意見がありました。 「Turbo Streams で変更のブロードキャストをするとき、モデルからビューに対しての更新が発信されるのは、少し気持ち悪いです。」

「なるほど、確かに!」と思いましたので、今回はコントローラーから明示的にブロードキャスト操作を呼び出してみたいと思います。

実行環境

  • macOS 10.15.17
  • Redis 5.0.7
  • Ruby 3.0.0

作るもの

Hotwire に トースト通知 機能を作るで定義したアプリケーションの要件は以下の通りでした。

  • 簡単な 1 部屋 1 画面だけのチャットアプリ
  • 書き込みを検索できる機能
  • 書き込みの検索結果を表示している間に新しい投稿があった時は、新着通知を表示する機能
  • 投稿者の投稿の保存結果をトーストで通知する

こちらについて、モデルからビューに直接ブロードキャストさせず、コントローラから明示的にブロードキャストをトリガーさせます。

実装

サンプルコードでのブロードキャストの実装例を手始めに、前回作成したアプリケーション、今回のコントローラから明示的にブロードキャストをトリガーさせるパターンを順に紹介します。

サンプルコード

hotwired / hotwire-rails-demo-chat/app/models/room.rb を見ると、次のようにモデルが実装されています。

github.com

[app/models/message.rb]

class Room < ApplicationRecord
  has_many :messages, dependent: :destroy
  broadcasts
end

broadcasts により、Turbo Streamsでのブロードキャストを設定しています。

前回作成したアプリケーション

前回作成したアプリケーションでは、チャンネルや明示的に使用するテンプレートの指定し ActiveRecord のコールバックで呼び出しました。

[app/models/post.rb]

class Post < ApplicationRecord

  validates :comment, presence: true

  after_create_commit do
    broadcast_prepend_to "post-list", target: "post-list-el", partial: "posts/post"
    broadcast_replace_to "post-info", target: "post-info-el", partial: "posts/info"
  end
end

この方法で実装すると、詳細に設定できる上に複数のチャンネルへのブロードキャストをすることができます。 また、create 時だけブロードキャストするなどの操作が容易なので、個人的にはこちらの記述方法の方が好みです。

コントローラから明示的にブロードキャストとトリガーさせるパターン

ここからが今回の本題です。

app/models/post.rbを以下のように修正し、新たなメソッドでブロードキャストを呼び出します。

[app/models/post.rb]

class Post < ApplicationRecord

  validates :comment, presence: true

  def broadcast
    broadcast_prepend_to "post-list", target: "post-list-el", partial: "posts/post"
    broadcast_replace_to "post-info", target: "post-info-el", partial: "posts/info"
  end
end

このようにメソッドで定義すると、コントローラーで以下のように呼び出すことができます。

[app/controllers/posts_controller.rb]

class PostsController < ApplicationController
 def index
    # 省略
  end

  def create
    @post = Post.new(post_params)
    if @post.save
      @success = "success"

      # ブロードキャスト操作をコントローラーで呼び出す
      @post.broadcast
    end

    return respond_to do |format|
      format.turbo_stream do
        render turbo_stream: turbo_stream.replace("post-toast-el", partial: "posts/save_result", locals: { success: @success })
      end
    end
  end

  # 省略
end

このようにすることで、ブロードキャストのきっかけを実装者がよりコントロールすることができます。 ActiveRecord のコールバックを呼び出さないようにデータを更新する手段が無いわけではありませんが、やや不便な印象があります。

今回のラボ内での意見を受けて、Turbo Streams のブロードキャスト機能をモデルの更新から切り出してみました。 実際やってみて、「一般ユーザーの更新は通知するが管理者ユーザーの更新は通知しない」や、「他のアクセスしている人向けのブロードキャストはするが、投稿者自体はリダイレクトさせる」など柔軟なコントロールがよりしやすいのではないかな?と感じました。

P.S.

直近のイベント情報

■5月21日(金)19:30~ とらのあなラボエンジニア座談会Vol.8【なぜとらラボエンジニアはラジオを始めたのか】
 を開催します!興味のある方はぜひご参加ください!

yumenosora.connpass.com

■5月28日(金)19:30~ 【オンライン】オタクが最新技術を追うLTイベント#24【初心者歓迎】【テーマフリー】  を開催します!こちらは発表者も募集中ですので、LT初心者という方でもぜひご応募ください!

yumenosora.connpass.com

採用情報

■募集職種
yumenosora.co.jp

カジュアル面談も随時開催中です

■お申し込みはこちら!
yumenosora.connpass.com

■ToraLab.fmスタートしました!

メンバーによるPodcastを配信中!
是非スキマ時間に聞いて頂けると嬉しいです。
anchor.fm

■Twitterもフォローしてくださいね!

ツイッターでも随時情報発信をしています
twitter.com