【Ruby】Sinatraで、速攻でWebサイトを公開するための環境構築

初めてSinatraでアプリを作ったので覚えたことまとめ。
事情によりローカルはApach + Passenger、リモートはNginx + Unicornなので、一応両方で動くようまとめ・・・たつもりなのですが。

## 元記事
下記エントリの転載になります。
[【Ruby】Sinatraで、速攻でWebサイトを公開するための環境構築 - rokuroFire](http://www.rokurofire.info/2013/11/20/sinatra_builtenv/)

## 事前準備
* Ruby、bundlerのインストール
* Apacheの場合、Passengerの設定をしておく
* Nginxの場合、Unicornのgemをインストールしておく

## アプリのファイル構成
最小構成は下記。

```
projectname/
-main.rb	// Controller
-config.ru	// Rackの設定ファイル。最初に実行されるファイル
-views/
	-index.haml	// ビューのテンプレート（haml）ファイル
-public/	// webサーバのドキュメントルートはここを指定。画像もここ
```

個人的に、作業ディレクトリは下記のようにしてみました

```
projectname/
-Gemfile	
-config.ru	// Rackの設定ファイル。最初に実行されるファイル
-main.rb	// Controller
-app/		// 何らかの処理
  -module/	// モジュール類のファイル
-views/		// ビューのテンプレート（haml）ファイル
  -index.haml
  -layout.haml
  -style/	// sass（scss）ファイル
    -baes.scss
-db/		// データベースの設定ファイル（yml）を置く
-log/		// ログ置き場
-public/	// webサーバのドキュメントルートはここを指定。画像もここ
-tmp/
-vendor/
	-bundle/	// gemの管理ディレクトリ
-unicorn.rb	// unicornの設定ファイル
```

## Sinatraの設定
プロジェクト直下にconfig.ruを置きます。
また今回はControllerを別ファイル（main.rb）にしました。

```config.ru
require './main.rb'
run MainApp
```
```main.rb
require 'rubygems'
require 'sinatra/base'
require 'logger'
require 'unicorn'
require 'haml'
require 'sass'

# config.ru ではこのクラスをrunしています
class MainApp < Sinatra::Base

	 # cssにアクセスした時の処理
	get %r{^/(.*)\.css$} do
		scss :"style/#{params[:captures].first}" #scssテンプレート
	end

	# '/'にアクセスした時の処理
	# views/index.haml
	get '/' do
		haml :index #views/index.haml（hamlのテンプレート）
	end
end
```

%r{^/(.*)\.css$} の（）の部分は params[:captures] という配列に格納されるようで、firstで0番目の要素を取得します。
つまりこの場合、/base.css にアクセスすると、views以下にある style/base.scss が呼び出されます。

## bundlerでgemの管理
Railsではおなじみですが、Sinatraでもgemの管理にbundlerを使ってみます。

```
# インストール
$ gem install bundler

$ cd projectname
$ bundle init
# Gemfileが作成される
```

```Gemfile
source "https://rubygems.org"
gem 'sinatra'
gem 'haml'
gem 'mysql2'
gem 'activerecord'
gem 'unicorn'		# Nginx + Unicornの場合
・・・# などなど

#【注意】
# gemは "activerecord"
# .rb内でのrequireは "active_record"
```

```
# gemを入れるディレクトリを作成
mkdir vendor
mkdir vendor/bundle

# パスを指定して bundle install
# bundle exec をつけることで、Gemfileで指定されたgem環境でコマンドが実行される
$ bundle install --path vendor/bundle

# リモートのUnicornのバージョンを調べるなど
$ bundle exec unicorn -v
unicorn v4.7.0
```

##Webサーバ（バーチャルホスト）の設定

#####Apache x passengerの場合

静的なサイトと同様、サイト数分、VirtualHostブロックを追加するだけです。
私のUbuntuのApacheはバーチャルホストの設定ファイルがconfigとは別ファイルになっていました。環境によっては少し違うと思われます。

```
$ cd /etc/apache2/sites-available/
$ sudo vim projectname-test			# 既存のプロファイルをコピーでもおｋ
```

```configファイル
# プロジェクトディレクトリ以下の public を指定する
# 今回は /var/www/hogehoge/projectname/ がプロジェクトディレクトリ
<VirtualHost *:80>
	RailsEnv development
	ServerName local.projectname.com
	DocumentRoot /var/www/hogehoge/projectname/public
	<Directory /var/www/hogehoge/projectname/public>
		Options Indexes -MultiViews MultiViews Includes FollowSymLinks
		AllowOverride all
	</Directory>
	ErrorLog ${APACHE_LOG_DIR}/error.log
	LogLevel warn
	CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
```
```
sudo a2ensite projectname-test
// apacheは自動で再起動される
```

##### Nginx x unicornの場合

Nginxをリバースプロキシとして設定します。
Nginx設定ファイルの中に、サイトの数分、upstreamブロックとlocationブロックを増やします。下記では2個のアプリに2個のホストを設定した場合を記述しています。

```nginx.conf
#下記を追記
worker_processes  1;
events {
    worker_connections  1024;
}

http {

    #アプリ1
    upstream app1 {
        server unix:/tmp/app1.sock;
    }

    #アプリ2
    upstream app2 {
        server unix:/tmp/app2.sock;
    }

    #アプリ1
    server {
        listen       80;
        server_name  local.projectname.com;

        location / {
            root /var/www/hogehoge/projectname/public;
            proxy_pass http://app1;   #unicornのupstreamを指定
            proxy_set_header Host $host;
        }
    }

    #アプリ2
    server {
        listen       80;
        server_name  local.projectname2.com;

        location / {
            root /var/www/hogehoge/projectname2/public;
            proxy_pass http://app2;    #unicornのupstreamを指定
            proxy_set_header Host $host;
        }
    }

}
```

unicornの設定ファイル unicorn.rbを編集します。

```unicorn.rb
# coding: utf-8

# プロジェクトディレクトリへのパス
@path = "/var/www/hogehoge/projectname/"

worker_processes 1 # CPUのコア数に揃える
working_directory @path
timeout 300
listen '/tmp/app1.sock'	# Nginxのconfig内にあるupstreamで、このパスを指定
pid "#{@path}tmp/pids/unicorn.pid" # pidを保存するファイル
# logを保存するファイル
stderr_path "#{@path}log/unicorn.stderr.log"
stdout_path "#{@path}log/unicorn.stdout.log"
preload_app true
```

unicornの起動コマンド

```
$ bundle exec unicorn -E production -c unicorn.rb -D

# unicorn.rbのあるアプリディレクトリで上記実行
# -E で環境を指定、-D でデーモン化
# bundlerを利用している場合には bundle exec
# unicornを停止する場合は、pidを調べてkillする
```

ここまでで、とりあえずSinatraでWebサイトを公開することが出来ると思います。

## 自動リロード
通常、アプリケーションファイルを変更してもWebサーバを再起動しないと反映されないため、開発環境では自動でリロードされるように設定。

####Apache+Passengerの場合
```tmp/always_restart.txt
# tmp/以下に always_restart.txt を置くだけ。中身は空でOK
```

####それ以外の場合
```
gem install sinatra-contrib
```
```main.rb
require "sinatra/reloader"
# if development? などの条件分岐をつけておく
```

Gitでデプロイする場合には、.gitignoreに上記txtファイルを追加 or dev環境の時のみ実行されるような分岐を書いておく。

## Basic認証
ついでにベーシック認証についても調べたので書いておく。

##### サイト全体にかける場合

```main.rb
# ルーティングファイルの最初に記述
use Rack::Auth::Basic do |username, password|
	username == "name" && password == "passwd"
end
```

##### 特定のパスにかける場合

```main.rb
helpers do
	def protected!
		unless authorized?
			response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
			throw(:halt, [401, "Not authorized\n"])
		end
	end
	def authorized?
		@auth ||=  Rack::Auth::Basic::Request.new(request.env)
		@auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == ['name', 'passwd']
	end
end

get /protectedurl do
	protected!
	"ベーシック認証ページですよ"
end
```

## 環境の変更
```cmd
# sinatraの中で分岐する場合は以下の定数が使える
ENV['RACK_ENV'] # "development" or "production"

# Sinatraとは別にrubyの処理をコマンドラインで叩く場合、オプションを付ける
# dev環境
$ RACK_ENV=development ruby app/dataset.rb 
# production環境
$ RACK_ENV=production ruby app/dataset.rb

同様の定数に環境名が入るので分岐に使える
ENV['RACK_ENV'] # "development" or "production"
```

##参考
* [SinatraとHamlとScssとCoffeeScriptでモダンなWeb制作環境を構築する #2](http://dev.classmethod.jp/server-side/language/modern-web-creating-environment-2/)
* [Rubyの入門や書き捨てアプリを作る場合は sinatraがオススメ！](http://mukaer.com/archives/2013/06/06/ruby_sinatra/)
* [apache-passengerで動かすsinatra環境のよさげな設定](http://shimada-k.hateblo.jp/entry/2012/12/08/140935)
* [【初心者向け】RubyとSinatra、アンテナサイトの作り方](http://shgam.hatenadiary.jp/entry/2013/08/25/160937)
* [sinatraでBasic認証](http://qiita.com/yaotti/items/11d6cea26d3c7111ea8c)
* [Bundler再履修: bundle execって何？ gemはどこに入るの？](http://memo.yomukaku.net/entries/IpCSQmo)