Ruby の Sinatra を使ってとりあえず Web アプリを立ち上げてみる

# Ruby の Sinatra を使ってとりあえず Web アプリを立ち上げてみる

> 2018-11-27 上から順番に内容を更新中です。大まかな記事内容は変わりません。

---

## 0. 概要

[始めよう - Sinatra](http://www.sinatrarb.com/intro-ja.html) で自学自習するよ、って人には物足りないかもしれません。
各項目での所要時間は自身の適当な経験にもとづき記載しておきました。

### 0.1. この記事でやること、やらないこと

- とりあえず Web アプリをローカル端末で起動し、表示してみる（ GET ）
- フォームに入力して登録（ POST ）とかまではしません（よって DB は使用しません）
- URL に値を指定し、それを画面に表示してみる、はやります

### 0.2. 想定する対象者

- Web アプリとか自分で動かしたことないです、的な方
- Ruby on Rails で挫折しました、的な方
- 想定作業時間
  - 30〜60分

### 0.3. 用意するもの

なるべくあたらしいものをを使用すれば特に問題ないはずです。
参考に記事作成時に利用したバージョンを記載しておきます。

- Ruby
  - ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin18]
- gem
  - bundler
    - Bundler version 1.17.1
    - gem の管理に使用します
  - sinatra
    - 2.0.4
    - Web アプリケーションフレームワーク
  - sinatra-contrib
    - 2.0.4
    - sinatra での開発時に便利な機能とかを追加できる
- お好みのエディタ
  - メモ帳などでもなんとかなりますが私は Visual Studio Code を使用

---

## 1. とにかく立ち上げてみる

所要時間：5〜10分

### 1.1. ディレクトリの作成と `bundle init`

適当なディレクトリを作成し、そのディレクトリに移動します。

```shell-session
mkdir try_sinatra
cd try_sinatra
```

bundler 使用するため `bundle init` を実行します。
すると `Gemfile` というファイルが作成されます。

```shell-session
$ tree
.
└── Gemfile  <<= このファイル

0 directories, 1 file
```

### 1.2. gem の追加とインストール

`Gemfile` に下記を追記するか、、、

```rb:./Gemfile
gem 'sinatra'
```

もしくは `bundle add` コマンドを使って `sinatra` を追加します。

```shell-session
bundle add sinatra --skip-install
```

引き続き gem のインストールを行います。

```shell-session
bundle install --path vendor/bundle
```

> `bundle add` 時の `--skip-install` オプションは無くても構いませんが、
> その場合 `Gemfile` への追加と gem のインストールを行います。
> 上記によりシステムグローバルに `sinatra` がインストールされます。
> （ `bundle config` などの設定をしていない場合など）
> 実際の開発時はアプリケーション（プロジェクト）ごとに分けることが推奨されます。
> また `bundle install` 時の `--path` オプション（インストール先の指定）についても
> 同様の理由から指定が推奨されます（2022/06 現在だとされていました、と書いた方が良いのかな）
> なお bundler の詳細については下記の記事など参考にしてください。
> [Bundlerの使い方 - Qiita](https://qiita.com/oshou/items/6283c2315dc7dd244aef)

### 1.3. アプリケーションの作成と起動

`app.rb` ファイルを作ります。
作成したファイルに下記のように記載します。

```rb:./app.rb
require 'sinatra'

get '/' do
  'hello'
end
```

続いてアプリケーションを起動します。

```shell-session
bundle exec ruby app.rb
```

> `bundler` を使用しているため、単に `ruby app.rb` ではなく
> `bundle exec ruby app.rb` となります。

参考までにターミナルには下記のように表示されていると思います。

```shell-session
$ bundle exec ruby app.rb
[2018-11-27 23:26:03] INFO  WEBrick 1.4.2
[2018-11-27 23:26:03] INFO  ruby 2.5.1 (2018-03-29) [x86_64-darwin18]
== Sinatra (v2.0.4) has taken the stage on 4567 for development with backup from WEBrick
[2018-11-27 23:26:03] INFO  WEBrick::HTTPServer#start: pid=61737 port=4567
```

ブラウザで下記にアクセスします。

<http://localhost:4567>

`hello` と表示されていれば成功です。

### 1.4. 補足: 画面に表示される内容について

ちなみに下記のように変更すると、、、

```rb:./app.rb
get '/' do
 'hello'
 'good bye' # この行を追加
end
```

`good bye` のみが表示されます。
つまり `get` のメソッドの最後の値（戻り値）が表示されます。

---

## 2. ちょっと便利にしてみる

所要時間：5〜10分

### 2.1. まずは `app.rb` の内容を変えてみる

`app.rb` の内容を以下のように変更してください。

```rb:./app.rb
require 'sinatra'

get '/' do
  'how are you?'
end
```

変更後、ブラウザをリロードしても表示内容が変化しないはずです。
コードの変更を反映するためには、アプリケーションを再起動する必要があります。
しかし、コード変更の度に再起動を行うのは面倒なので、次項のように変更しましょう。

### 2.2. `sinatra/reloader` の導入

> 起動したアプリケーションは一旦停止してから行ってください。  
> もしくは導入後アプリケーションは再起動してください。

`Gemfile` に `sinatra-contrib` を追加します。

```rb:./Gemfile
gem 'sinatra'
gem 'sinatra-contrib' # この行を追加
```

追加後 `bundle install` を実施してください。
引き続いて `app.rb` を編集します。

```rb:./app.rb
require 'sinatra'
require 'sinatra/reloader' # この行を追加。 sinatra-contrib はこのために必要

get '/' do
  'how are you?'
end
```

再度アプリケーションを起動します。

```shell-session
bundle exec ruby app.rb
```

`sinatra/reloader` の追加後は `app.rb` の変更後に毎回アプリケーションを再起動する必要がなくなります。
試しにアプリケーションを起動したままで、以下のように変更してみましょう。

```ruby:./app.rb
require 'sinatra'
require 'sinatra/reloader'

get '/' do
  'fine!' # ここを変更
end
```

ブラウザのリロード後に `fine!` と表示されれば OK です。
これで変更の度にアプリケーションの再起動の必要がなくなりました。

---

## 3. Sinatra のルーティングとか追加してみる

所要時間：5〜10分

### 3.1. 単純にルーティングを追加してみる

```rb:./app.rb
require 'sinatra'
require 'sinatra/reloader'

get '/' do
  'how are you?'
end

# ここから
get '/path/to' do
  "this is [/path/to]"
end
# ここまでを追加
```

ブラウザで以下にアクセスしてください。

<http://localhost:4567/path/to>

この要領で必要なルートを追加していき、表示するページを追加していきます。

なお、追加時の注意として記述した順（上から順）にマッチングされるのでその点は注意が必要です。
例えば、`get '/path/*'`の記載位置より下に `get '/path/to'` を書き
<http://localhost:4567/path/to>
にアクセスしても、先に `get '/path/*'` にマッチするため、
`get '/path/*'` の内容が表示されます。

パスの指定にワイルドカードなどを使う場合は注意してください。

### 3.2. 静的ファイルの追加とアクセス

ルートディレクトリ配下に下記のディレクトリを作成します

```shell-session
mkdir public
```

そこに HTML ファイルを配置してみます。

```shell-session
echo "this is html file." > ./public/test.html
```

ブラウザで以下にアクセスしてみます。

<http://localhost:4567/test.html>

追加した HTML の内容が表示されます。

つまり `public` 配下は URL の `/` 以下にマッピングされます。
CSS や JS なども `public` 配下に置くことで参照できます。

なお `app.rb` に記載のルートとコンフリクトした場合は
`public` 配下の静的ファイルが優先のようです。

### 3.3. URL から値を取得する

`app.rb` を下記のように変更してみます。

```rb:./app.rb
require 'sinatra'
require 'sinatra/reloader'

get '/' do
  'how are you?'
end

get '/path/to' do
  "this is [/path/to]"
end

# ここから
get '/hello/*' do |name|
  "hello #{name}. how are you?"
end
# ここまでを追加
```

ブラウザで以下にアクセスします。

- <http://localhost:4567/hello/Alice>
  - `hello Alice. how are you?` と表示される
- <http://localhost:4567/hello/Bob>
  - `hello Bob. how are you?` と表示される

つまり `*` 指定したワイルドカード部分の文字列が引数 `name` に格納されます。
値の取得方法は他にもやりかたがあるので、
[始めよう - Sinatra](http://www.sinatrarb.com/intro-ja.html) などで確認してください。

---

## 4. VIEW を使う

所要時間：10〜30分

> ToDo: 時間ができたら書く。
> [始めよう - Sinatra ビュー / テンプレート](http://www.sinatrarb.com/intro-ja.html#%E3%83%93%E3%83%A5%E3%83%BC%20/%20%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88(Views%20/%20Templates))

### 4.1. erb

`app.rb` を下記のように変更します。

```rb:./app.rb
require 'sinatra'
require 'sinatra/reloader'

get '/' do
  'how are you?'
end

get '/path/to' do
  "this is [/path/to]"
end

get '/hello/*' do |name|
  "hello #{name}. how are you?"
end

# ここから
get '/erb_template_page' do
  erb :erb_template_page
end
# ここまでを追加
```

さらにディレクトリとファイルを追加します。

```shell-session
mkdir views
touch views/erb_template_page.erb
```

上記 erb ファイルに下記のように追加します。

```erb:./views/erb_template_page.erb
<%= Time.now %>
```

ブラウザで以下にアクセスします。

<http://localhost:4567/erb_template_page>

現在時刻が表示されれば成功です。
erb の詳細などは省略します。

### 4.2. Markdown

`redcarpet` を導入します（ほかにもあるけど）。
`Gemfile` に下記のように追加してください。

```rb
gem 'sinatra'
gem 'sinatra-contrib'
gem 'redcarpet' # この行を追加
```

追加後 `bundle install` を実行します。

引き続き `app.rb` を下記のように変更します。

```rb:./app.rb
require 'sinatra'
require 'sinatra/reloader'

get '/' do
  'how are you?'
end

get '/path/to' do
  "this is [/path/to]"
end

get '/hello/*' do |name|
  "hello #{name}. how are you?"
end

get '/erb_template_page' do
  erb :erb_template_page
end

# ここから
get '/markdown_template_page' do
  markdown :markdown_template_page
end
# ここまでを追加
```

さらに新規ファイルを追加します。

```shell-session
touch views/markdown_template_page.markdown
```

上記 Markdown ファイルに下記のように追記します。

```markdown:./views/markdown_template_page.markdown
# this is markdown_template_page!
```

ブラウザで以下にアクセスします。

<http://localhost:4567/markdown_template_page>

Markdown が HTML にレンダリングされて表示されます。
単純に静的ページをサクッと作りたい場合は erb よりも簡単かと思います。

### 4.3. erb と Markdown を組み合わせてみる

`app.rb` を下記のように変更します。

```rb:./app.rb
require 'sinatra'
require 'sinatra/reloader'

get '/' do
  'how are you?'
end

get '/path/to' do
  "this is [/path/to]"
end

get '/hello/*' do |name|
  "hello #{name}. how are you?"
end

get '/erb_template_page' do
  erb :erb_template_page
end

get '/markdown_template_page' do
  markdown :markdown_template_page
end

# ここから
get '/erb_and_md_template_page' do
  erb :erb_and_md_template_page, :locals => { :md => markdown(:erb_and_md_template_page) }
end
# ここまでを追加
```

erb ファイルを作成します。

```shell-session
touch views/erb_and_md_template_page.erb
```

下記のように記載します。

```erb:./views/erb_and_md_template_page.erb
<%= Time.now %>>
<%= md %>
```

続いて Markdown ファイルを作成します。

```shell-session
touch views/erb_and_md_template_page.markdown
```

下記のように記載します。

```markdown:./views/erb_and_md_template_page.markdown
# this is erb_and_md.markdown
```

ブラウザで以下にアクセスします。

<http://localhost:4567/erb_and_md_template_page>

現在時刻と `this is erb_and_md.md` が表示されていれば OK です。
もしくは以下のように erb の中で直接 `#markdown` メソッドを実行する方法もあります。

```erb
<%= Time.now %>
<%= markdown(:erb_and_md_template_page) %>
```

この辺を組み合わせると Markdown に対して `layout.erb` を適用することなどができるようになります。

---

## 5. レイアウトの適用

> たとえば常に同じ内容を表示するヘッダーやフッターについて
> いつか書く

---

## 6. 参考資料

### 6.1. [始めよう - Sinatra](http://www.sinatrarb.com/intro-ja.html)

- 公式ドキュメントの日本語訳。部分的に情報が古いままのこともある。
- が、基本的には翻訳されているので、読んでおくことをおすすめします。

### 6.2. [sinatra/README.ja.md at master · sinatra/sinatra](https://github.com/sinatra/sinatra/blob/master/README.ja.md)

- 上記「始めよう - Sinatra」の GitHub にある元ファイル
- 比較的最近にメンテナンスが入っているのでこちらも見るほうがいいかも

### 6.3. [入門 Sinatra――Web制作のためのシンプルなRuby DSL](http://www.oreilly.co.jp/books/9784873115597/)

- 電子書籍

ほか、おすすめあれば教えていただけると幸いです。