LoginSignup
4
3

More than 5 years have passed since last update.

RailsとHerokuでノーティフィケーションをプッシュする / PusherとTurbolinksの兼ね合い

Last updated at Posted at 2015-01-11

ブログを更新しました。元の記事はコチラ


リアルタイムでユーザーにお知らせを通知したい。
Facebookの「○○さんがあなたの投稿をLikeしました」みたいなやつ。
やってみたら意外とすぐできた。
(herokuのaddonのおかげ)

こんなの。
20150112-064343_capture

Pusher

http://pusher.com/
めんどくさそうだと思ってたけど、Pushserを使えばそんなに難しくない。
Websocketの仕組みは分からないけど、意識しなくて良い。
RailsとHeroku使ってます。HerokuじゃなくてもPusherは使えますが。
Screen Shot 2015-01-11 at 10.50.05 PM

herokuドキュメント
Pusher | Heroku Dev Center
Adding in-app notifications with Pusher | Heroku Dev Center

herokuドキュメントに沿ってやったんですが、
Turbolinksとの絡みがあるので、いろいろ変えました。

  
Gemfile


gem 'pusher'

HerokuのダッシュボードからPusherを追加。
リミットはあるけど無料で使える。

Pusher  Development — App keys

設定

APIキーとかを追加。Herokuのプロダクションは何にもしなくても面倒をみてくれるみたい。
Pusher.url とか Pusher.app_id とかは自分のローカル環境とHeroku上のアプリとでは違うものなので注意。

config/environments/development.rb


  # Real time notification
  require 'pusher'
  Pusher.url = "http://7f115110ccdsxxxxxxxxx:f7b9a0f68dxxxxxxxxxx@api-eu.pusher.com/apps/100000"
  Pusher.logger = Rails.logger

  # Lines below are needed only for development environment.
  # In production, Pusher will automatically take care of this.
  require 'pusher'
  Pusher.app_id = '000000'
  Pusher.key    = '7f1151xxxxxxxxxxxxxxxxxx'
  Pusher.secret = 'f7b9a0f68dc6xxxxxxxxxxxxx'


Javascriptを読み込み

ダウンロードしてassets pipelineに入れてもいいかもしれないけど、リンクがおすすめって書いてあったのでとりあえずそうしあた。

We highly recommended that you link to our hosted version which is minified and served by a CDN. By linking to a minor version (e.g. 2.2) you will automatically receive patch updates.

views/layouts/application.html.erb


  <!-- Pusher (Heroku addon) -->
  <script src="//js.pusher.com/2.2/pusher.min.js" type="text/javascript"></script>

環境変数を設定

Javascriptを必要に応じて呼び出したいので、Api_keyを環境変数にしました。
公式ドキュメントにはないけど。api_keyをどうやってproduction/developmentで切り替えるかだけど、HTMLに埋め込んじゃいました。ついでにログインしているユーザーのIDも。
なんかいい方法ないかな。

ローカル環境


$ vi ~/.bash_profile
export PUSHER_API_KEY="7b01788fd3cxxxxd05470"

Heroku


$ heroku config:set YADOKALY_PUSHER_API_KEY=7b01788fdxxxxx05470

HTML(views/layouts/application.html.erbとかどこでも)


<span id="current_user_id" data-value="<%= current_user.id %>" style="display:none;"></span>
<span id="pusher_api_key" data-value="<%= ENV['PUSHER_API_KEY'] %>" style="display:none;"></span>

サーバーサイド

current_userにログインしているユーザーが入っているとうい前提です。
テストとしてpushというメソッドをつくりました。


 class PusherController < ApplicationController
  protect_from_forgery :except => :auth # stop rails CSRF protection for this action

  def auth
    if current_user
      response = Pusher[params[:channel_name]].authenticate(params[:socket_id])
      render :json => response
    else
      render :text => "Not authorized", :status => '403'
    end
  end

  def push
    Pusher['private-'+current_user.id.to_s].trigger('new_message', {:sender => current_user.name, :link => "/chatroom/123"})
    render nothing: true
  end

end

対応するRouteを追加します。
config/routes.rb


  post 'pusher/auth'
  get 'pusher/push'

クライアントサイド

/pusher/pushを叩くリンクをViewのどこかに入れておいてください。


<%= link_to "push", "/pusher/push", remote: true %>

bodyの中に書くとTurbolinksと色々あって、1回ノーティフィケーション送ったつもりが2個も3個も表示されるって状況になったので、ページ遷移するたびにPushrer instancesをカラにしてみました。

assets/javascripts/pusher.coffee


pusher_reset = ->
    # Disconnect and remove all pusher instance
    # to avoide multiple execution.
    if Pusher
        $.each(Pusher.instances, (index, instance) ->
            instance.disconnect()
        )
        Pusher.instances = []
        console.log 'pusher_reset fired.'


ready = ->

    # Pusher notification
    # Pusher['private-1'].trigger() in controller
    current_user_id = $('#current_user_id').attr('data-value')
    api_key = $('#pusher_api_key').attr('data-value')

    if Pusher && current_user_id
        pusher  = new Pusher(api_key, { cluster: 'eu' })
        channel = pusher.subscribe('private-' + current_user_id)

        channel.bind('new_message', (data) ->
            console.log 'fire'
            # Will be executed when the method in controller called.
            if !$('.notify-wrap').length
                $('body').prepend('<div class="notify-wrap"></div>')
            msg = data.sender + ' さんからメッセージを受信しました。'
            msgDiv = '<div class="notify">' + 
                                    '<span class="got-it"><i class="fa fa-times-circle"></i></span>' + 
                                    '<a href="' + data.link + '">' + msg + '</a>' + 
                                '</div>'
            $(msgDiv).prependTo('.notify-wrap').animate({ right: 0 }, 350)
            ### if you want to hide it after some seconds
            setTimeout( ->
                $('#notify').fadeOut()
            , 10000)
            ###
        )
    $('body').on 'click', '.got-it', (e) ->
        e.preventDefault()
        $(this).closest('.notify').remove()

# For turbolinks
$(document).ready(ready)
$(document).on 'page:load', ready
$(document).on 'page:receive', pusher_reset

見た目

ちょっとかっこよくします。
横からスッと入ってくるようにしたいので、right: -300px;をデフォルトにしておいて、
前述の.animate({ right: 0 }, 350) でアニメーションさせてます。

assets/stylesheets/pusher.scss


$c_light_gray: #F7F7F7;
$c_blue: #114aa9;
$c_gray: #666666;

.notify-wrap {
    z-index: 10;
    position: fixed;
    top: 10px;
    right: 10px;
    .notify {
        position: relative;
        right: -300px;
        width: 200px;
        border-radius: 5px;
        font-size: 0.85em;
        border: 3px solid $c_light_gray;
        background-color: #fff; 
        box-shadow: 0 4px 10px #ddd;
        margin-bottom: 10px;
        &:hover {
            border-color: lighten($c_blue, 42);
        }
        a {
            height: 100%;
            width: 100%;
            display: block;
            padding: 10px 15px;
            &:hover {
                text-decoration: none;
                //color: #fff;
            }
        }
        .got-it {
            position: absolute;
            top: -7px;
            right: -7px;
            font-size: 20px;
            width: 24px;
            height: 24px;
            line-height: 20px;
            border: 2px solid #fff;
            background-color: #fff;
            border-radius: 50%;
            text-align: center;
            cursor: pointer;
            &:hover {
                color: #fff;
                border: 2px solid $c_gray;
                background-color: $c_gray;
            }
        }
    }
}

以上かな。
これで pushボタンを押したらスッとノーティフィケーションが現れるはず。
楽しい!

参考

Pusher | Heroku Dev Center
Adding in-app notifications with Pusher | Heroku Dev Center
Documentation | Pusher
Pusher connections stack up when using turbo links · Issue #48 · chrismccord/sync · GitHub
Turbolinks support for Pusher.js
rails/turbolinks · GitHub

http://workabroad.jp/posts/2127

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3