【Rails7で遊んでみた】画像つき投稿をSPAっぽく投稿する・ブロードキャストを使ってリアルタイム通信する

### はじめに
この記事は簡単なCRUDアプリをSPA(Single Page Application)っぽくしたり、ブロードキャストを使ってリアルタイム通信したりと、ちょっとRails7に触ってみての感想になります。

Railsの5系・6系を使ったことはあるけれど、7系はまだ触れていない人向けに、この記事を読んでRails7系に興味をもってもらえると嬉しいです。

■ 開発環境
・Ruby：3.1.2
・Rails：7.0.4

## イメージの共有
今回試したことをパッと見で分かりやすく伝わるようにと動画を用意しました。試してみた中で今日は以下の２つについて書いていきます。
- 画像つき投稿をSPAっぽく投稿する
- ブロードキャストを使ってリアルタイム通信する

【 画像つき投稿をSPAっぽく投稿する動画 】
[![Image from Gyazo](https://t.gyazo.com/teams/startup-technology/6936b8fb17cd56ce18f3887235f3a065.gif)](https://startup-technology.gyazo.com/6936b8fb17cd56ce18f3887235f3a065)

【 ブロードキャストを使ってリアルタイム通信する動画 】
[![Image from Gyazo](https://t.gyazo.com/teams/startup-technology/007aa391a65cf86edcf14cee44f9a8a3.gif)](https://startup-technology.gyazo.com/007aa391a65cf86edcf14cee44f9a8a3)

<br />

### 画像つき投稿をSPAっぽく投稿する
これを試したのは、よくあるCRUDアプリだと「新規投稿」用のボタン等をクリック→新規投稿画面→一覧画面or詳細画面に遷移させますが、SPAっぽく1つのページ上で投稿が完結して尚且それを確認できるようにしたかったので遊んでみました。

ただ文字列が投稿されるだけだと寂しかったので、Active Storageを使って画像も一緒に投稿できるようにしました。

[![Image from Gyazo](https://t.gyazo.com/teams/startup-technology/6936b8fb17cd56ce18f3887235f3a065.gif)](https://startup-technology.gyazo.com/6936b8fb17cd56ce18f3887235f3a065)

動画を見ていただければ分かるかと思いますが、Post一覧画面にある"新規投稿"ボタンを押すとページはそのままにモーダルが表示されます。そのモーダルにはpostの投稿に必要なtitle, body, imageが用意されており、それらを入力し終えて"Create Post"を押すと先程のPost一覧画面に居ながらpostの投稿が完了しているのが見て取れると思います。

参考にさせていただいた「[猫でもわかるHotwire入門 Turbo編](https://zenn.dev/shita1112/books/cat-hotwire-turbo)」では画像の投稿はなかったので、「画像があったとしてもきっと同じようにできるよね？？ → 試しにやってみよう！」といった考えからやってみました。

今回はposts_controllerのindexアクションで用意した`@posts`をビュー側で`render @posts`する箇所にturbo_frame_tagを使っています。

```erb
<%= turbo_frame_tag 'posts-list' do %>
  <div class='row' id='posts'>
    <%= render @posts %>
  </div>
<% end %>
```

新規投稿フォームに値を入力して、それが送信されるとposts_controllerのcreateアクションが動くわけですが、よくあるCRUDアプリの以下のような書き方ではなく、`@post.save`成功時にredirect_toの記載をしていません。

```ruby
# よくあるCRUDアプリ *post_params, current_userは別途定義されているものとします。
def create
  @post = current_user.posts.build(post_params)
  if @post.save
    redirect_to posts_path
  else
    render :new
  end
end

# 今回のコード
def create
  @post = current_user.posts.new(post_params)
  if @post.save
    # 成功時の処理を記載します。今回は空行で検証しました。
  else
    render :new, status: :unprocessable_entity
  end
end
```

代わりにapp/views/posts/create.turbo_stream.erbを用意して以下の記載をしています。

```erb
<%= turbo_stream.append "posts", @post %>
```

これによって"posts"というIDの箇所の最後尾に投稿された内容を追加してくれているようです。

因みにPostモデルにバリデーションを定義しているので、空欄のまま投稿しようとすると、ちゃんとバリデーションエラーメッセージも出してくれます。
[![Image from Gyazo](https://t.gyazo.com/teams/startup-technology/96e59d2a17deadf1f7bd092e717c2746.png)](https://startup-technology.gyazo.com/96e59d2a17deadf1f7bd092e717c2746)

<br />

### ブロードキャストを使ってリアルタイム通信する
こちらは先程の資料だけでなく、「[Railsの公式ページ](https://rubyonrails.org/)」にあるDHHのデモ動画を見て、誰かが投稿した内容をリアルタイムで共有できるのを試してみたくて遊んでみました。

[![Image from Gyazo](https://t.gyazo.com/teams/startup-technology/007aa391a65cf86edcf14cee44f9a8a3.gif)](https://startup-technology.gyazo.com/007aa391a65cf86edcf14cee44f9a8a3)

動画にあるように右側で投稿された内容が、左側のブラウザ上でページのリロードをしなくても表示されているのが分かるかと思います。

こちらも記載は少なく、以下の記述で動いています。（事前にredisを立ち上げておきましょう！）

```ruby
# article.rb
class Article < ApplicationRecord
  belongs_to :user
  broadcasts_to -> (_article) { "broadcasts" }, inserts_by: :prepend
end
```

```ruby
# articles_controller.rb
def create
  @article = current_user.articles.new(article_params)
  if @article.save
    # 成功した際の処理。今回は未記入。
  else
    render :new, status: :unprocessable_entity
  end
end
```

```erb
<%# app/views/articles/index.html.erb %>
<%= turbo_stream_from "broadcasts" %>
<h1>articles#index</h1>
<%= link_to '新規投稿', new_article_path, class: 'btn btn-secondary my-3', data: { turbo_frame: 'modal' } %>

<%= turbo_frame_tag 'articles-list' do %>
  <div class='row' id='articles'>
    <%= render @articles %>
  </div>
<% end %>
```

Articleモデルに記載している`{ "broadcasts" }`でビューで対応する箇所を、`:prepend`で先頭に加えることを指定しているようです。

<br />

### 感想・その他
この記事では重要な箇所のコードだけ掲載しましたが、遊んでいる中でJavaScriptを書き加えたり、複雑なコードを書いたりすることもなく、Rails7が用意してくれているレールの上に乗ってコードを書くだけで今回紹介したような機能実装できました。この他にも一部分だけ変更するとか面白い機能を実装できるので、少しでも興味をもった方は今すぐRails7系入れましょう！そして、いろいろ遊んで語り合いましょう！！

### 参考記事
- 「[猫でもわかるHotwire入門 Turbo編](https://zenn.dev/shita1112/books/cat-hotwire-turbo)」
- 「[Railsの公式ページ](https://rubyonrails.org/)」