Sinatraの初心者向け入門 〜簡単にWebサイトを作れるRubyのフレームワーク〜

広告

どうも、ゴトーだ。

f:id:hogehoge223:20170208081817j:plain

これまでPHPとJavaScriptを中心に学習してきたが、「Rubyもいいらしい」ということで少しかじってみている。

とはいえあっちこっち浮気するのも良くないと思って、ほんの触り程度にしたが、予想以上に優れもので学習しやすいということで、ハマりつつある。
そんなわけで今回紹介記事を書くに至っている。

Sinatraを始めるにあたって

f:id:hogehoge223:20161121123724p:plain

まず最初に断っておきたいのは、俺はまだRubyとSinatraを触りはじめて4日目なので、あまり深い意味での理解はしていない。
そこ違うだろという所があるかもしれないが、生暖かく見守ってほしい。良い意味で初学者に対してアプローチしていきたいと思う。

さて本題に戻ると、SinatraとはRubyというプログラミング言語を使ったWebアプリケーションフレームワークだ。
特徴としては初学者でもちょっとしたものを動かすなら、学習することも少なく、そして直感的にプログラムを書けるので、すぐに習得できるということだ。

俺はPHPを3週間ほど続けてきたが、個人的にはPHPよりもSinatra(+Ruby)の方がよほど楽に感じた。
ぶっちゃけもう一回やり直すならPHPからではなくSinatraから学習したいとすら思っている。(まあどちらも触り程度しかやっていないからこれから見えてくるものはあるだろうが)

具体的にどこがどう楽なのか、というとなかなかプログラムを書いてみないことには分からないが、最も個人的に気持ちよく感じたのは「直感的にプログラムを書ける」ということだ。
例えば生成したいURLのパターンを書いて、そのURLではどんな動作をさせるのかを簡単に書くことができる。

これが今までのPHP(フレームワークなし)だと、プロジェクトに置いたディレクトリやファイルのパスがそのままURLになってしまって、そのURLがどんな処理をするのかが分かりづらかったし、他にもURLに文字列を埋め込んだりといったことができずに困っていた。

そういった諸々の問題点をSinatraを使うことで解決してくれたところを魅力に感じ、この記事を書くに至っている。

Sinatraとは

とはいえこれだけだと初心者が言ってるだけで信用ならねーよ、という人もいるはずなので、もう少し踏み込んで紹介したい。

Sinatraの公式の紹介文はこうなっている。

Sinatra is a DSL for quickly creating web applications in Ruby with minimal effort:

「Sinatraは最小限の労力で、Rubyを使ってWebアプリケーションを素早く作るためのDSLです。」

要はSinatraを使えば普通に作るよりも楽にWebアプリケーションが作れちゃうよ、ということだ。
ただし調べたところあまり大きなアプリケーションの作成には向いていないらしく、ちょっとした掲示板やホームページを作る時に良いらしい。

そして当たり前のことだが、Rubyというプログラミング言語で書かれているので、Rubyが動作する環境でないとSinatraを使うことはできない。
自分の場合はMacなので元から入っていたが、Windowsを使っている人はRubyをインストールする必要がある。

またSinatraを使うのはRubyのプログラムを書くということなので、Rubyについて少しは理解している必要がある。
ただし、簡単な動作だけなら本当に何もプログラミングがわからなくても可能なので、まずはこの記事を最後まで見てもいいかもしれない。

どこで使われているのか

Sinatraはマイナーなものではなく、かなり有名な使用事例があるらしい。

Sinatraは、いくつかの企業や団体によって、使用されている。代表的なところでは、アップル、BBC、イギリス政府、LinkedIn、Heroku、GitHub、Songbird、スタンフォード大学、レッドハットなどである。

Sinatra - Wikipedia

実際に「Sinatra」と検索しても腐るほど入門サイトがあるし、学習には困らないと思う。
この記事をだけでも、簡単な動かし方は分かるように説明しているつもりだが、わからないところやもっと詳細に知りたい人は各自検索するなり公式サイトのドキュメントを見るなりして欲しい。

STEP1. Sinatraのインストール

先程も紹介したようにSinatraはRubyで動作するものなので、Rubyの動作環境がないと何も始まらない。
自分の場合はMacを使っているので最初から入っているが、Windowsを使っている人はRubyをインストールしておいて欲しい。

申し訳ないが、ここではRubyが動作するという前提で進めさせてもらう。

そしてRubyが入っているのなら話は簡単だ。
Rubyにはgemというライブラリを管理する仕組みが備わっていて、gemのコマンドを使えば一発でSinatraをインストールすることができる。

以下のコマンドをコマンドラインで実行するだけで良い。

gem install sinatra

これでSinatraのインストールは終了だ。

STEP2. まずは簡単な動作を行う

それではまずはSinatraを動作させるためのディレクトリを作成する。
基本的に今後はコマンドラインで説明していく。

mkdir ~/sinatra
cd ~/sinatra

これはディレクトリを作り、そのディレクトリに移動するということだ。
ディレクトリの場所、ディレクトリ名は各自自由に設定してほしい。

そうしたらエディタを使ってRubyのプログラムを書いていく。
ここでは「main.rb」というファイル名にしよう。

require 'sinatra'

get '/' do
  'Hello! Sinatra!'
end

このファイルを作成したら、以下のコマンドを実行する。

ruby main.rb

すると以下のようなメッセージが表示されれば成功だ。

== Sinatra (v1.4.7) has taken the stage on 4567 for development with backup from Puma
Puma starting in single mode...

そうしたらブラウザで「http://localhost:4567/」にアクセスしよう。

f:id:hogehoge223:20161121134946p:plain

ブラウザではこんな感じに味気ないメッセージが表示されるはずだ。

どういう原理なのか

これを一行ずつ説明していきたい。

require 'sinatra'

1行目のこの部分は「Sinatra」を使いますよという宣言のようなもの。これがないと何も始まらない。

get '/' do
  'Hello! Sinatra!'
end

これは「/」というURLにアクセスしたら「Hello! Sinatra!」という文字列を表示しますよ、という意味。
これらの文字列をいじると、また違った動作になってくるのだが、その前に一つやっておくことがある。

STEP2. sinatra-contribのインストール

この段階で「sinatra-contrib」というものをインストールしておきたい。

これはSinatraの便利ツールで、重要な機能として、プログラムを変更したらそれを自動で反映してくれるものがある。

むしろsinatra-contribを使わないとプログラムを変更したら、その度に再起動しなければならなくて非常に面倒なので、ぜひ入れていこう。
インストールはSinatraのときと同じくコマンドラインで行う。

gem install sinatra-contrib

そうしたらプログラムの中で、sinatra-contribの機能を使うために、以下のように変更して欲しい。

require 'sinatra'
require 'sinatra/reloader'

get '/' do
  'Hello! Sinatra!'
end

そうしたら一度sinatraを動作させているコマンドラインのプログラムを停止して、再度開始しよう。
(この時点ではsinatra/reloaderが有効になっていないので再起動する必要がある)

STEP3. URLをカスタマイズ

先程は「/」というURLだったが、今度は下のように変更するとどうか。

require 'sinatra'
require 'sinatra/reloader'

get '/shout' do
  'Hello! Sinatra!'
end

そして「http://localhost:4567」にアクセスするとこのように表示される。

f:id:hogehoge223:20161121133740p:plain

そんなURLないよ、という感じで怒られてしまった。
つまり「get ~ do」で指定したURLにしかアクセスできないということだ。

今回は「/shout」と書いてあるので「http://localhost:4567/shout」にアクセスすれば良い。

f:id:hogehoge223:20161121134904p:plain

どんなURLにしたいのか、というのはこのように自分で自由に決めることができる。

STEP4. 出力する文字列をカスタマイズ

せっかくshoutというURLなら実際に叫んでもらおう。

get '/shout' do
  'うおおおおおおおおおおお!'
end

f:id:hogehoge223:20161121135059p:plain

「get ~ do」と「end」で囲まれている中の文字列をいじることで、表示される内容が変わるのがわかる。
もし2行以上書いた場合はどうなるのか。

get '/shout' do
  'うおおおおおおおおおおお!'
  'ちくしょおおおおおおおお!'
end

f:id:hogehoge223:20161121135053p:plain

このように最後の文字列だけが表示される。

STEP5. HTMLファイルを読み込む

ただしこれだけでは味気なさ過ぎるので、HTMLファイルを使っていこう。
現在のプロジェクトに「views」というディレクトリを作成し、その中に「shout.erb」というファイルを作成し、そこに以下の内容を書き込む。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8"/>
  <title>Shout!</title>
</head>
<body>
<h1>うおおおおおおおおおおお!</h1>
<h1>ちくしょおおおおおおおお!</h1>
</body>
</html>

これは「.erb」という見慣れない拡張子だが、これはRubyを使ってHTMLを書けるファイルで、HTMLの中に実際にRubyのプログラムを埋め込むことができる優れものだ。
ここではまだ純粋なHTMLしか使っていない。

そして「main.rb」のshoutのところを以下のように変更しよう。

get '/shout' do
  erb :shout
end

この状態で「http://localhost:4567」にアクセスする。

f:id:hogehoge223:20161121135705p:plain

無事にHTMLファイルが読み込まれている。

これはどういうことかというと「erb :shout」というプログラムによって「viewsディレクトリのshout.erbを読み込んでくれ」と指示していて、ブラウザにアクセスした時に実際にshout.erbの内容が表示されている。

例えば「cry.erb」というファイル名だったら、main.rbの内容は「erb :cry」というように変更すれば良い。

STEP6. ERBでRubyを使ってみる

せっかくerbを使っているので、HTMLにRubyのプログラムを埋め込んでみよう。
ここでは分かりやすく、100回ループを回してたくさんの文字列を表示してみる。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8"/>
  <title>Shout!</title>
</head>
<body>
<ul>
<% 100.times do |i| %>
  <li><%= i %>番目です</li>
<% end %>
</ul>
</body>
</html>

f:id:hogehoge223:20161121140410p:plain

するとこんな感じで100回繰り返された内容が表示される。
「<% %>」という形式でRubyのプログラムを埋め込むことができるが、Ruby自体の書き方についてはここでは省略させてもらう。

STEP7. ERBに値を送ってみる

main.rbからerbファイルに変数の値を送ることができる。

get '/shout' do
  @blog_name = 'はてなブログ'
  erb :shout
end

「@blog_name」という変数に「はてなブログ」という値を入れている。
ここで注意すべきなのはERBで使いたい変数には必ず「@」をつけることだ。これを忘れると動作しない。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8"/>
  <title>Shout!</title>
</head>
<body>
私が使っているブログサービスは「<%= @blog_name %>」です
</body>
</html>

f:id:hogehoge223:20161121140936p:plain

@blog_nameの値がHTMLに反映されているのが分かる。
RubyからERBに値を送る時はこの「@」を使って、必要な変数を定義すれば良い。

STEP8. URLにパターンを使用する

使いたいURLは必ずしも固定されている必要はなく、以下のようにパターンを使うことでより便利に使用することができる。

get '/article/:id' do
  id = params[:id]
end

これはどういうことかというと、例えば「article/999」というURLにアクセスした時に「999」の部分が「:id」という箇所に該当する。

そしてURLで「:id」と埋め込まれたところは「params[:id]」というプログラムによって取り出すことができる。

例えば「/article/:title」という形式なら「params[:title]」という風にparamsの値が変更される。
ここはあまり深く考えずに直感的にやったほうがわかりやすいかもしれない。

もちろんURLの値をERBに送ることもできる。

get '/article/:id' do
  @id = params[:id]
  erb :article
end
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8"/>
  <title>Shout!</title>
</head>
<body>
記事のIDは<%= @id %>です
</body>
</html>

f:id:hogehoge223:20161121141953p:plain

入門編はここで終了

本当に簡単なWebサイトならここまでやったことを応用すればできてしまう。

基本的にやるべきことは「URLを定義する」→「そのURLの処理を書く」→「それをHTMLで表示する」の3ステップだけで良い。
もちろん複雑な処理をしたい、という場合は色々面倒になってくるが、Sinatra自体の入門としてはここまでで良いだろう。

ただこれだけだとどうしても味気ないと感じる人も多いはずなので、ここからは合間合間の説明は飛ばしていくが、掲示板を作るサンプルを書いていく。
そこで一つ必要な知識として「ActiveRecord」というものがある。

ActiveRecordとはMySQLなどのデータベースをRubyから簡単に使えるようにする便利なものなのだが、ここでActiveRecordの説明をイチから始めてしまうと収拾がつかなくなってしまうので、申し訳ないが、ActiveRecordを知っている前提で書かせてもらう。

そもそもSinatra入門記事なのに知ってる前提なんておかしいだろ、と自分でも思うが、まあ「こんな感じで応用していくんだ」というケーススタディとして利用してもらえればと思う。
ActiveRecordを覚えたあたりで、どうやってSinatraと連動するんだっけ、という時にでも見返してもらえれば幸いだ。

STEP9. ActiveRecordの準備

まず掲示板を作るにあたり「topics」テーブルと「comments」テーブルを用意する。
SQLは以下の通り。

CREATE TABLE `topics` (
  `id` int NOT NULL AUTO_INCREMENT,
  `title` varchar(255),
  `body` text,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
);

CREATE TABLE `comments` (
  `id` int NOT NULL AUTO_INCREMENT,
  `topic_id` int,
  `body` text,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL,
  PRIMARY KEY (`id`)
);

topicsテーブルはスレッド、commentsテーブルがコメントに当たる。

そしてデータベースの接続設定と、それぞれのテーブルのクラスを作成。

ActiveRecord::Base.establish_connection(
  adapter: 'mysql2',
  database: 'bbs',
  host: 'localhost',
  username: 'root',
)

class Topic < ActiveRecord::Base
end

class Comment < ActiveRecord::Base
end

STEP10. トピックの一覧表示

トップページではトピックの一覧を取得して、それをHTMLでリスト形式で表示していく。
ActiveRecordを使ってまずは全て取得する。

get '/' do
  @topics = Topic.all
  erb :index
end

それをHTMLで表示。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8"/>
  <title>掲示板</title>
</head>
<body>
<ul>
  <% @topics.each do |topic| %>
      <li>
        <a href="./topic/<%= topic.id %>">
          <%= topic.title %>
        </a>
      </li>
  <% end %>
</ul>
</body>
</html>

@topicsは配列で返ってくるので、それをループにあたる「each」で回しながらそれぞれ一つずつliタグで囲っていく。
また、トピックのURLは「/topic/:id」という形式にしておく。

STEP11. トピックを作成する

今回はトップページにトピックの作成フォームを設置した。

<form method="post" action="/topic_create">
  <input type="text" name="title">
  <textarea type="text" name="body"></textarea>
  <input type="submit" value="投稿">
</form>

これは「/topic_create」というURLにPOST通信で入力した値を送信するというもの。
送信された値はSinatraで取得し、その値をもとにActiveRecordで新しいレコードを作成する。

post '/topic_create' do
  Topic.create(
    title: params[:title],
    body: params[:body]
  )

  redirect '/'
end

入力フォームで例えば「input type="text" name="title"」となっていたら「name="title"」に注目して欲しい。
このnameの中の部分が「params」で取得する名前に連動している。

ここでは「input type="text" name="title"」の部分がトピックのタイトルに、「textarea type="text" name="body"」の部分がトピックの本文になっている。

STEP12. トピックを表示する

トピックのURL形式は「/topic/:id」となっているので、トピックのIDはURLから取得する。
例えば「/topic/2」ならデータベースのIDが「2」のものを探すという処理を行う。

またコメントも同時に表示したいので、このトピックIDを持つコメントも取得しておく。

get 'topic/:id' do
  @topic = Topic.find(params[:id])
  @comments = Comment.where(topic_id: params[:id])
  erb :topic
end

これをHTMLで表示する。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8"/>
  <title>トピック</title>
</head>
<body>
<div class="topic">
  <div class="title">
    <%= @topic.title %>
  </div>

  <div class="body">
    <%= @topic.body %>
  </div>
</div>

<ul>
  <% @comments.each do |comment| %>
    <li>
      <%= comment.body %>
    </li>
  <% end %>
</ul>
</body>
</html>

STEP13. コメントを投稿する

最後にコメントの投稿フォームを作成しておこう。
これはトピックの作成とほとんど同じで良いのだが、一つだけ問題がある。

それは現在見ているトピックのIDをフォームの値に送っておかなければならない。
なぜならトピックIDがなければ、これから作成しようとしているコメントがどのトピックに紐付いているものかがわからなくなってしまうからだ。

ここでは「input type="hidden"」で値を埋め込んでいる。

<form method="post" action="/comment_create">
  <input type="hidden" name="topic_id" value="<%= @topic.id %>">
  <textarea type="text" name="body"></textarea>
  <input type="submit" value="投稿">
</form>

この値を「/comment_create」で受け取りコメントを作成する。

post '/comment_create' do
  Comment.create(
    topic_id: params[:topic_id],
    body: params[:body]
  )

  redirect "/topic/#{params[:topic_id]}"
end

「input type="hidden"」にしても同じようにparamsで値を受け取れるので、それを利用してコメントを作成する。
処理が終了したら元のトピックページに飛ばしておく。

超シンプルだが、これだけで最低限の掲示板のプログラムが動作する。
もちろん改行をbrに反映させたり、リンクを付与したりといった処理は別途必要で、実用的なものを作ろうと思ったら何かと時間がかかるが。

まとめ

Sinatraは「URLの定義」「その中の処理」「HTMLに表示」という3プロセスがどれも直感的に書くことが出来て、フレームワークを使わないPHPよりも随分楽に感じられた。
Macを使っていればRubyが元から入っているのですぐにSinatraを始められるし、WindowsでもRubyを入れさえすればすぐに始められるはずなので、環境設定のコストはむしろPHPよりも少ないかもしれない。

唯一コマンドラインを多少使わないといけない所が障壁になるかもしれないが、Sinatraを起動させるところだけ覚えてしまえば問題ない。
というか自分もドットインストールで学習しただけで、基本的なコマンド以外はほとんど覚えていないが、ここで使うものくらいなら数十分で覚えられるはずだ。

自分自身もこれからRubyを続けるかは決めてないが、今のところSinatraが一番快適に学習を進められているのでおすすめだと思う。