ENECHANGE Developer Blog

ENECHANGE開発者ブログ

Action Cableでいいねボタン!

こんにちわ エンジニアのMariMurotaniです。

社内のアイデアソン/ハッカソンイベントで利用するために、 Action Cableの機能を使って「いいね!」ボタンを作成しました。 websocker-railsを利用します。 社内のWD(ディレクターかつデザイナー)が10分で作ってくれた画像を使って1時間で開発しました。

f:id:mari-murotani:20190404113159g:plain:w150

最終更新日時がApr 2016と大分更新されていないのでちょうど出たときに話題になっただけのようなのでWebSocketの本格的な利用を考えると今後はAPI Gatewayなどを利用してみるといいかもしれません。 aws.amazon.com

1. gemの追加

gem 'websocket-rails'

bundle installします

2. 初期化

rails g websocket_rails:install

3. 設定

config/environments/development.rbに下記の行を追加します

Rails.application.configure do
.....
    config.middleware.delete Rack::Lock
.....
end

3. ビューとモデルの作成

まずは普通にcontrollerとviewを作成しておきます

rails generate model Counter count:integer
rails generate controller home
bin/rake db:migrate

/config/routes.rbの設定をします

root 'home#index'
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
  root to:'application#home'
end

HomeControllerの中身はこんな感じにしておきましょう。

class HomeController < ApplicationController
  def index
    counter = Counter.last
    @count = counter.count
  end
end

HomeControllerの中身はこんな感じにしておきましょう 次にSubscribe用のチャンネルを作成します

rails g channel room speak vote

f:id:mari-murotani:20190404120424p:plain

app/channels/room_channel.rbを書き換えます

class RoomChannel < ApplicationCable::Channel
  def subscribed
    stream_from "room_channel"
  end

  def unsubscribed
    # Any cleanup needed when channel is unsubscribed
  end

  def speak(data)
    ActionCable.server.broadcast 'room_channel', message: data['message']
  end

  def vote
    counter = Counter.last.presence || Counter.new
    counter.attributes = {count: counter.count+1}
    counter.save!
    @count = counter.count
    ActionCable.server.broadcast 'room_channel', message: @count
    system('afplay /System/Library/Sounds/Submarine.aiff')
  end
end

app/assets/javascripts/channels/room.coffeeを書き換えます

App.room = App.cable.subscriptions.create "RoomChannel",
  connected: ->
    # Called when the subscription is ready for use on the server
    #console.log("subscribe start")
  disconnected: ->
    # Called when the subscription has been terminated by the server

  received: (data) ->
    # Called when tconsole.log(data)
    # here's incoming data on the websocket for this channel
    $("#msg_box").html(data["message"].toString())
  speak: (message)->
    @perform 'speak', message
  vote: ->
    @perform 'vote'

app/view/home/index.html.erbを書き換えます

<div class="main_content">
  <div>
    <span class="card-number" id="msg_box"><%= @count %></span>
  </div>
  <a id="iine_button" class="button" ></a>
</div>


<!-- Compiled and minified CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">

<script>
  jQuery(document).ready(function(){
    $("#iine_button").on('click',function(){
      App.room.vote();
    });
  });

</script>

4. サーバー起動とテスト

bin/rails server -b 0.0.0.0 -p 4649

でサーバを立ち上げます 今回はvoteとspeakの関数を作ったのでコンソールから両方呼び出ししてみましょう ボタンについているアクションはvoteの方だけなのでVoteを押すと画面の数値が繰り上がります 複数の端末で接続した際にActionCable.server.broadcastを通して接続した端末すべてに変更の通知が飛びます これがWebSocketを利用したリアルタイム更新のユースケースです

f:id:mari-murotani:20190404121745p:plain f:id:mari-murotani:20190404121753p:plain

サーバーログを確認すると/cableのエイリアスが自動で処理をしてくれているのが確認できます WebSocketの機能をRails内に隠蔽したもので手間がかからずデモなんかを作れるのでこういったgemのアイデアっていいなと思いました

f:id:mari-murotani:20190404122413p:plain