Action_Cableを用いたチャットアプリのメモ -2

##目的
このパートでは、投稿のDBへの保存とuser_idとの紐付けをする。

##おさらい
####データの流れ
- クライアントがデータを入力**(views)**
- フォーム内を取得し、speakメソッドで受け取る**(coffee)**
- speakメソッド**(coffee)**からspeakメソッドへデータを渡す**(channels/rb)**
- speakメソッドから全クライアントへ配信**(channels/rb)**
- recieveメソッドで受け取ったデータをviewsへ追加**(coffee)**
- ブラウザに表示**(views)**

##参考にしたサイト
Rails 5の目玉機能「Action Cable」で双方向通信を実装（２）
https://codezine.jp/article/detail/10255

##手順
####1. speakメソッド(channels/rb)の修正
パート１では直接ブロードキャストしていたが、DBにデータを保存するために以下の手順を踏む。
- speakメソッドでデータをDBに追加する。
- modelに`after_create_commit`を追記し、jobを呼び出す。
- createが終わったら`perform`メソッドでブロードキャストする。


```rb:app/channels/talk_room_channel.rb
(略)
def speak(data)
    # ActionCable.server.broadcast 'talk_room_channel', message: data['message']
    TalkRoom.create! message: data['message'] #messageカラムにフォームの内容を追加
end
```

```rb:app/models/talk_room.rb
class TalkRoom < ApplicationRecord
#create後にperformのjobを呼び出す
    after_create_commit { TalkRoomBroadcastJob.perform_later self }
end
```

####2. jobの生成・編集
#####jobとは
非同期処理をするための機能。さまざまなキュー操作が可能。
`after_create_commit { TalkRoomBroadcastJob.perform_later self }`の`{}`内でキューイングシステムが空いたら`perform`を実行する。

>Active Job の基礎 | Rails ガイド
>https://railsguides.jp/active_job_basics.html

まず、以下のコマンドでbroadcast用のjobを生成する。
`bin/rails g job TalkRoomBroadcast`

次に、`perform`メソッドを編集する。

```rb:app/jobs/talk_room_broadcast.rb
class TalkRoomBroadcastJob < ApplicationJob
  queue_as :default

  def perform(talk_room) #job呼び出しの際に引数で渡されたselfのデータを受け取る
    #元のbroadcast処理と同じ
    ActionCable.server.broadcast 'talk_room_channel', message: talk_room.message
  end
end
```
####3. viewsの編集
手順２までで、DBの保存とブロードキャストができるようになった。
次は、DBの内容があらかじめ表示されるようにviewsを編集する。



まず、controllerで`TalkRoom`モデルの内容を取得する。

```rb:app/controllers/talk_rooms_controller.rb
class TalkRoomsController < ApplicationController
  def index
    @talk_rooms = TalkRoom.all
  end
end
```

次に、viewsの表示部分を修正する。

```erb:app/views/talk_rooms/index/html.erb
<div id='talk_rooms'>
    <% @talk_rooms.each do |talk_room| %>
        <div><%= talk_room.body %></div>
    <% end %>
</div>
(略）
```
ここまでできたら動作するか確認！

####4. ユーザ認証機能の追加(devise)
- deviseの設置
`bin/rails g devise:install`
- viewsの追加
`bin/rails g devise:views`
- Userモデルの追加
`bin/rails g devise User`
- Userテーブルの反映
`bin/rails db:migrate`

ここはさくっと飛ばします

####5. ログインしないとアクセスできないように設定
deviseの`before_action :authenticate_user!`を使用する。

```rb:app/controllers/talk_room_controller.rb
class TalkRoomsController < ApplicationController
  before_action :authenticate_user!
  def index
    @talk_rooms = TalkRoom.all
  end
end
```

####6. 投稿とユーザIDの紐付け

#####6-1. current_userで接続する。
 
deviseの`current_user`を用いて実装したかったのですが、うまく動いたのがこれだけでした。
丸パクリですｗ

>ActionCableでDeviseのcurrent_userを扱う - Qiita
>https://qiita.com/masuidrive/items/fd13ab8d9410206a1b91

```rb:app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    protected
    def find_verified_user
      User.find(session['warden.user.user.key'][0][0])
    rescue
      reject_unauthorized_connection
    end

    def session
      @session ||= cookies.encrypted[Rails.application.config.session_options[:key]]
    end
  end
end
```

`bin/rails s`でサーバを再起動して、うまくいっていたらこうなる。

```
% rails s                                                                                                                                                                               (git)-[develop]
=> Booting Puma
=> Rails 5.2.2 application starting in development
=> Run `rails server -h` for more startup options
Puma starting in single mode...
* Version 3.12.0 (ruby 2.5.1-p57), codename: Llamas in Pajamas
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
Started GET "/cable" for 127.0.0.1 at 2018-12-26 17:37:08 +0900
   (0.1ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
  ↳ vendor/bundle/ruby/2.5.0/gems/activerecord-5.2.2/lib/active_record/log_subscriber.rb:98
Started GET "/cable/" [WebSocket] for 127.0.0.1 at 2018-12-26 17:37:08 +0900
Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: Upgrade, HTTP_UPGRADE: websocket)
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
  ↳ app/channels/application_cable/connection.rb:11
Registered connection (Z2lkOi8vY2hhdC1hcHAtY29kZXppbmUvVXNlci8x)
TalkRoomChannel is transmitting the subscription confirmation
TalkRoomChannel is streaming from talk_room_channel
```

`Registered connection (...)`これが書いてあればok（のはず）。


#####6-2. user_idを投稿に紐付け
modelの生成の際に`current_user.id`も追加する。


```rb:app/channels/talk_room_channel.rb
（略）
def speak(data)
    # ActionCable.server.broadcast 'talk_room_channel', message: data['message']
    TalkRoom.create! user_id: current_user.id, message: data['message']
end
```

#####6-3. userモデルとtalkroomモデルのアソシエーション

```rb:app/models/talk_room.rb
(略)
belongs_to :users
(略)
```

```rb:app/models/user.rb
(略)
has_many :talk_rooms
(略)
```

#####6-4. jobとcoffeeの修正

`user_id`と`message`を表示するように修正する。
6-2でTalkRoomモデルに`user_id`は追加したため、jobでbroadcastする際に`user_id`も追加する。
また、receiveメソッドでも`user_id`を追記する。（`data['user_id']`で同様に取り出せる。)

```rb:app/jobs/talk_room_broadcast.rb
(略)
def perform(talk_room)
    ActionCable.server.broadcast 'talk_room_channel', user_id: talk_room.user_id, message: talk_room.message
end
```

```rb:app/aseets/javascripts/channels/talk_rooms.coffee
(略)
received: (data) ->
    $('#talk_rooms').append '<div>' + data['user_id'] + ' : ' + data['message'] + '</div>'
（略）
```

#####6-5. viewsの変更

現在ログインしているユーザのIDと投稿者のIDを表示するように変更する。

```erb:app/views/talk_rooms/index.html.erb
<h3>現在のユーザID: <%= current_user.id %></h3>
<div id='talk_rooms'>
    <% @talk_rooms.each do |talk_room| %>
        <div><%= "#{talk_room.user_id} : #{talk_room.message}" %></div>
    <% end %>
</div>
<br/>
<form>
    <label>
        発言する: <input type="text" data-behavior="speak_talk_rooms">
    </label>
</form>
```

#####6-6. 動作確認
ここまでで、ユーザと投稿との紐付けが完了した。
TalkRoomモデルにuser_idカラムが空の投稿が残っているはずなので、パート３の前に`bin/rails db:migrate:reset`を実行しておく。