ArchLinuxでWebsocket-railsをスタンドアロンで動かし、Nginx+Unicornと連携させるまでやってみた

Arch Linuxをインストールしたので練習がてら以前うまく行かなかったことに挑戦
とはいえ、ディレクトリ構成が違うだけで他のディストリとあまり変わらない

# 前提条件
* Ruby をインストール済み
* Rails をインストール済み

Archさんならどっちもすぐにインストール出来ちゃうからありがたい

# Nginxのインストール
ちゃちゃっとNginxをインストール

```
sudo pacman -S nginx
```

とりあえずNginxの設定ファイルをバックアップ

```
sudo cp /etc/nginx/nginx.conf /etc/nginx/default.conf.bk
```

設定ファイルは後でいじるので、とりまNginxは放置

# Unicornインストール

[Rails 4.2 + Unicorn + Nginx でアプリケーションサーバの構築](http://qiita.com/Salinger/items/5350b23f8b4e0dcdbe23)
を参考にUnicornの設定まで行う。
現段階ではDevelopment環境のままで。
Unicornは実行しておく

# Nginxの設定

Archだと上記ページのNginxの設定だけでは動かなかったので
nginx.confを以下のように編集

```/etc/nginx/nginx.conf
events {
        worker_connections 1024;
}

http {
        upstream unicorn {
                server unix:/tmp/unicorn.sock;
        }

        server {
                listen 80 default_server;

                access_log /var/log/nginx/access.log;
                error_log /var/log/nginx/error.log;

                root Railsプロジェクトのルートパス;

                client_max_body_size 100m;
                error_page 404 /404.html;
                error_page 500 502 503 504 /500.html;
                try_files $uri/index.html $uri @unicorn;

                location @unicorn {
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_set_header Host $http_host;
                        proxy_pass http://unicorn;
                }
        }
}
```

どうやらpermissionでエラーをはくことがあるらしいので
railsプロジェクトのルートまでのディレクトリ(特にユーザルートディレクトリ)のアクセス権限変更

```
chmod o+x /home/username/
```

最後にNginxを再起動

```
sudo systemctl restart nginx
```


ここまでで普通のRailsアプリケーションは動くはず

# Redisインストール
次にRedisをインストール

```
sudo pacman -S redis
sudo redis-server /etc/redis.conf
```

これでRedisサーバが起動するはず

## Redis-railsの設定
Websocket-railsをスタンドアロンで実行するにはRedisが必要みたい
[RailsのセッションストアとしてRedisを使う(Mac/EC2:AmazonLinux)](http://qiita.com/Kta-M/items/b4852c849d408e7445c6)
を参考にRailsの設定

Gemfileに以下を追記

```Gemfile
gem 'redis'
gem 'redis-rails'
```

Bundle installする

```
bundle install --path vendor/bundle
```

config/initializers/session_store.rbをコメントアウト

```config/initializers/session_store.rb
# Rails.application.config.session_store :cookie_store, key: '_sample_session'
```

config/environments/development.rbに追加

```config/environments/development.rb
  config.session_store :redis_store, servers: 'redis://localhost:6379/0', expire_in: 1.day
```

# Websocket-railsのインストール
ここからは[Railsで複数グループ対応のチャットアプリを作ってみた](http://altarf.net/computer/rails/2502)
を参考に

Websocket-railsをGemfileに追加

```Gemfile
gem 'websocket-rails'
```

Bundle installする

```
bundle install --path vendor/bundle
```

Websocket-rails関係のファイルを生成

```
rails g websocket_rails:install
```

config/environments/development.rbに追記

```config/environments/development.rb
config.middleware.delete Rack::Lock
```

今回はスタンドアロンで実行するので
config/initializers/websocket_rails.rbを以下のように編集

```config/initializers/websocket_rails.rb
WebsocketRails.setup do |config|
  config.standalone = true
  config.synchronize = false
  config.standalone_port = 3001

  config.redis_options = { host: 'localhost', port: '6379' }
end
```

Nginxの設定にWebsocket-railsのルーティング追加
nginx.confは以下のようになる

追加したのは
upstream thin と location /websocket

```/etc/nginx/nginx.conf
events {
        worker_connections 1024;
}

http {
        upstream unicorn {
                server unix:/tmp/unicorn.sock;
        }

        upstream thin {
                server 127.0.0.1:3001;
        }

        server {
                listen 80 default_server;

                access_log /var/log/nginx/access.log;
                error_log /var/log/nginx/error.log;

                root Railsプロジェクトのルートパス;

                client_max_body_size 100m;
                error_page 404 /404.html;
                error_page 500 502 503 504 /500.html;
                try_files $uri/index.html $uri @unicorn;

                location @unicorn {
                        proxy_set_header X-Real-IP $remote_addr;
                        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                        proxy_set_header Host $http_host;
                        proxy_pass http://unicorn;
                }

                location /websocket {
                        proxy_pass http://thin;
                        proxy_http_version 1.1;
                        proxy_set_header Upgrade $http_upgrade;
                        proxy_set_header Connection "upgrade";
                }
        }
}
```

# Websocket-railsを使う
## Websocket用のController作成
rails g controller は使えないので自分で作成
今回は
app/controllers/sockets_controller.rb

```app/controllers/sockets_controller.rb
class SocketsController < WebsocketRails::BaseController
  def message_recieve
    WebsocketRails[:channel1].trigger :message, message
  end
end
```

次にルーティングの設定
Websocket-railsはconfig/events.rbでルーティング設定する

```config/events.rb
WebsocketRails::EventMap.describe do
  subscribe :websocket, to: SocketsController, with_method: :message_recieve
end
```

## Chat画面の作成
### Controller作成
まずはChatsコントローラ作成

```
rails g controller chats
```

chats_controller.rbにindexを追加

```app/controller/chats_controller.rb
class ChatsController < ApplicationController
  def index
  end
end
```

chats_controllerをconfig/routes.rbでルーティング

```config/routes.rb
Rails.application.routes.draw do
  root 'chats#index'
end
```

### Viewの作成
app/views/chats/index.html.erbを以下のように作成

```app/views/chats/index.html.erb
<h1>Chat</h1>

Name : <input id="name" type="text"><br />
Comment : <input id="comment" type="text"><br />
<input id="send" type="button" value="send">

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Comment</th>
    </tr>
  </thead>

  <tbody id="chat_body">
  </tbody>
</table>
```

次に
app/assets/javascripts/chats.coffeeを以下のように編集

```app/assets/javascripts/chats.coffee
class @ChatClass

  constructor: () ->
    @dispatcher = new WebSocketRails('localhost/websocket', true)
    @channel = @dispatcher.subscribe('channel1')
    @events()

  events: () =>
    $('#send').on 'click', @sendMessage
    @dispatcher.bind 'message', @receiveMessage
    @channel.bind 'message', @receiveMessage

  sendMessage: (event) =>
    name = $('#name').val()
    comment = $('#comment').val()
    @dispatcher.trigger 'websocket', { name: name, comment: comment }
    $('#comment').val('')

  receiveMessage: (message) =>
    name = document.createElement('td')
    name.innerHTML = message.name

    comment = document.createElement('td')
    comment.innerHTML = message.comment

    row = document.createElement('tr')
    row.appendChild name
    row.appendChild comment

    $('#chat_body').prepend row

$ ->
  window.chatClass = new ChatClass()
```

# Development環境で実行
まずはRedisが動いてることを確認
動いてなければ

```
sudo redis-server /etc/redis.conf
```

次にWebsocket-railsをスタンドアロンで起動

```
bundle exec rake websocket_rails:start_server RAILS_ENV=development
```

その後Unicornを起動

```
bundle exec rake unicorn:start
```

最後にNginxを再起動

```
sudo systemctl restart nginx
```

これで
http://localhost
にアクセスするとチャット画面がでてくるはず

次はproduction環境で動かしてみようかな