momota.txt

hello, hello, hello, how low?

Rails から外れた僕らは Sinatra で I Did It My Way (1)

sinatra

sinatra は ruby の web アプリケーションフレームワーク。rails よりもシンプルで柔軟なプログラムが可能となる。つまり、しかれた rails に乗り切れない人のためのフレームワーク。 本稿では以下について書く。

  1. sinatra インストール
  2. hello world アプリケーション
  3. ルーティング
  4. フィルタ
  5. ヘルパー
  6. テンプレートの呼び出し

前準備: インストール

ruby 2.0 を rbenv でインストールする。 以下、作業は /Users/momota/dev/sinatra/ で行う。

1
2
3
4
5
6
7
$ rbenv install 2.0.0-p247
$ rbenv local 2.0.0-p247
$ rbenv rehash
$ rbenv version
2.0.0-p247 (set by /Users/momota/dev/sinatra/.ruby-version)
$ ruby -v
ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin12.4.0]

bundler で sinatra と関連 gem をインストールする。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ gem install bundler
$ bundle -v
Bundler version 1.3.5
$ bundle init
Writing new Gemfile to /Users/momota/dev/sinatra/Gemfile
$ cat << EOF >> ./Gemfile
heredoc> gem "activerecord", "3.2.13"
heredoc> gem "sqlite3", "1.3.7"
heredoc> gem "sinatra", "1.4.3"
heredoc> gem "sinatra-contrib", "1.4.0"
heredoc> EOF
$ bundle install --path vendor/bundle
Fetching gem metadata from https://rubygems.org/...........
Fetching gem metadata from https://rubygems.org/..
Resolving dependencies...
Installing i18n (0.6.1)
Installing multi_json (1.8.0)
Installing activesupport (3.2.13)
Installing builder (3.0.4)
Installing activemodel (3.2.13)
Installing arel (3.0.2)
Installing tzinfo (0.3.37)
Installing activerecord (3.2.13)
Installing backports (3.3.4)
Installing eventmachine (1.0.3)
Installing rack (1.5.2)
Installing rack-protection (1.5.0)
Installing rack-test (0.6.2)
Installing tilt (1.4.1)
Installing sinatra (1.4.3)
Installing sinatra-contrib (1.4.0)
Installing sqlite3 (1.3.7)
Using bundler (1.3.5)
Your bundle is complete!
It was installed into ./vendor/bundle

hello world を返す sinatra アプリケーションを書く

サイトのルートへアクセスが遇ったときに “hello world” を返す main.rb を作成する。適当なエディタで以下を書いて保存する。

1
2
3
4
5
require 'sinatra'

get '/' do
  "hello world"
end

アプリケーションを実行する。

1
$ bundle exec ruby main.rb

動作確認をする。

1
2
$ curl http://localhost:4567/
hello world%

ちゃんと返ってきている。簡単。 ここでは、 curl で確認したが、もちろん web ブラウザで http://localhost:4567/ へアクセスして確認してもOK。 アプリケーションの終了は Ctrl-c でOK。

ルーティング: 複数のリクエストをさばく

sinatra では、以下のように HTTP メソッド (GET, POST, …)とURLがペアでルーティングされる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
get '/' do
  # show something
end

post '/' do
  # create something
end

put '/' do
  # replace something
end

patch '/' do
  # modify something
end

delete '/' do
  # annihilate something
end

options '/' do
  # appease something
end

link '/' do
  # affiliate something
end

unlink '/' do
  # separate something
end

先ほど書いた main.rb を修正していろいろな HTTP GET リクエストに対応する。 ここでは http://localhost:4567/about にアクセスが有った場合、 “about this site” 文字列を返すように追記する。 以下は、git diff の結果を示す。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
diff --git a/main.rb b/main.rb
index 713861d..9423680 100644
--- a/main.rb
+++ b/main.rb
@@ -1,5 +1,11 @@
 require 'sinatra'
+require 'sinatra/reloader'

 get '/' do
   "hello world"
 end
+
+get '/about' do
+  "about this site"
+end
+

通常、スクリプトの修正をアプリケーションへ反映するためには、アプリケーションの再起動が必要となる。require 'sinatra/reloader' により、その必要がなくなる。

URL からのパラメータを扱う

以下のメソッドを書くことにより、http://localhost:4567/hello/SOME_NAME へアクセスすると SOME_NAME 部分を扱うことができる。

1
2
3
get '/hello/:name' do
  "hello #{params[:name]}"
end

これは以下のようにも書ける。

1
2
3
get '/hello/:name' do |n|
  "hello #{n}"
end

複数のパラメータを扱うときは以下のように書く。 http://localhost:4567/hello/FAMILY_NAME/LAST_NAME へアクセスすると、FAMILY_NAME と LAST_NAME を扱える。

1
2
3
get '/hello/:fname/?:lname?' do |f, l|
  "hello #{f} #{l}"
end

パラメータは、ワイルドカードや正規表現で扱うことができる。

1
2
3
get '/from/*/to/*' do |f, t|
  "from #{f} to #{t}"
end

結果は以下のとおり。

1
2
$ curl http://localhost:4567/from/fukuoka/to/tokyo
from fukuoka to tokyo%

正規表現で扱う場合は、以下。ここでは、/users/ 以下がすべて数字であるアクセスの場合にマッチングさせている。

1
2
3
get %r{/users/([0-9]+)} do |i|
  "user id = #{i}"
end

結果は以下のとおり。

1
2
$ curl http://localhost:4567/users/12345
user id = 12345%

before/after filter

sinatra のフィルタは、ルーティングされたコンテキストを実行する前後(before / after)で、リクエストとレスポンスを変更することができる。

1
2
3
4
5
6
7
8
9
10
11
before do
  @before_value = "foo"
end

get '/' do
  "before_value has been set to #{@before_value}"
end

after do
 puts "After filter called to perform some task."
end

以下のように記述することで、フィルタは特定パスのリクエストにのみ評価される。

1
2
3
4
before '/path/you/want/*'
  # execute only before the '/path/you/want/*' route
  authenticate
end

helper

ヘルパーメソッドを定義して、ルートハンドラーやテンプレートとして使える。

1
2
3
4
5
6
7
8
9
10
helpers do
  # 共通関数を書ける
  def say_hello( name )
    "hello, #{name}"
  end
end

get '/:name' do |n|
  say_hello( n )
end

上記は、モジュールとしても記述できる。

1
2
3
4
5
6
7
8
9
module Hello
  def say_hello( name ) "hello, #{name}" end
end

module Goodbye
  def say_goodbye( name ) "goodbye, #{name}" end
end

helpers Hello, Goodbye

テンプレートエンジンを扱う

sinatra では様々なテンプレートエンジンを扱うことができる。 例えば、haml, erb, sass, markdown, slim, coffescript など。詳細は http://www.sinatrarb.com/intro.html#Available%20Template%20Languages このあたりを参照。

ここでは、erb の例を示す。

views/index.erb を作成する。スクリプトからは以下のように呼び出せばOK。

1
2
3
get '/' do
  erb :index
end

テンプレートエンジンにパラメータを渡す

インスタンス変数に渡して、テンプレート側からコールする。

1
2
3
4
5
get '/:name' do |n|
  @name  = n
  @title = 'sinatra sample'
  erb :index
end

views/index.erb は以下。

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="urf-8">
  <title><%= @title %></title>
</head>
<body>
<h1>hello <%= @name %></h1>
</body>
</html>

実行結果は以下のとおり。

1
2
3
4
5
6
7
8
9
10
11
$ curl http://localhost:4567/taro
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="urf-8">
  <title>sinatra sample</title>
</head>
<body>
<h1>hello taro</h1>
</body>
</html>

layout テンプレート

テンプレートが複数存在するときに、ヘッダーやフッターなど、共通部分が出てくる。 そのような共通部分を views/layout.erb 切り出すことができる。(erb以外のときはファイル拡張子を変えればよい。) views/layout.erb は優先的に読み込まれる。

共通部分を layout.erb に記述し、個別記述部分を yield で呼び出す。以下に layout.erb の例を示す。

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="urf-8">
  <title>test</title>
</head>
<body>
  <%= yield %>
</body>

この時、以下のような index.erb テンプレートと

1
2
  <h1>test</h1>
  <h2><%= @name%></h2>

以下のルーティングにより

1
2
3
4
5
6
7
require 'sinatra'
require 'sinatra/reloader'

get '/:name' do |n|
  @name = n
  erb :index
end

以下の様な結果になる。

1
2
3
4
5
6
7
8
9
10
11
12
$ curl http://localhost:4567/hanako
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="urf-8">
  <title>test</title>
</head>
<body>
    <h1>test</h1>
  <h2>hanako</h2>

</body>

Comments