久しぶりにRubyを触っているこの頃ですが, Railsはやはり覚える事が多いのでまだまだSinatraの方が私には楽です.
遠い記憶になっているので少しまとめました.
基本的にClassicスタイルで書いていきます.
書いてて思ったのは大体は公式に記載があるということです.
基本
ルーティング
普通のルーティング
HTTPのメソッド名 + ルート + BlockというSinatraが有名になった美しい構文.
Rubyらしく最後に評価された式の値でレスポンスになります.
get '/' do
'ここは/にgetリクエストを送ったときに呼び出される場所'
end
post '/' do
...
end
put '/' do
...
end
delete '/' do
...
end
URLパラメータでのルーティング
URL内の
:変数名
がパラメータになります.
require 'sinatra'
get '/:hoge' do #=> localhost:4567/fooへアクセス -> fooと表示
params[:hoge]
end
公式を読むと他にも条件付きマッチなどかなり色々できます.
リダイレクト
URL指定
get '/' do
redirect 'https://www.google.com'
end
パス指定
get '/' do
redirect to('/topath')
end
元いた場所へ戻る
get '/back' do
redirect back
end
変わり種
正規表現でのルーティングやhalt/passというメソッドが面白いです.
halt
haltは途中で処理を止めてレスポンスを返します.
以下の例ではクエリパラメータhiにhiが渡された時だけhiと表示します.
require 'sinatra'
get '/' do
halt "hi" if params['hi'] == "hi"
"Hello"
end
他にもステータスコードを渡したりもできるようです.
正規表現でのルーティングとpass
ルーティングのURLには正規表現が使えます.
また, passメソッドを使うと他のマッチするルートへ移ります.
以下の例ではgreeting以下のhiにアクセスする時以外は全て2番目のルートで処理しますが,
greeting/hiへのアクセス時にクエリパラメータでhi=hiが設定されているときはやはり2番目のルートで処理します.
(例が悪かったかな)
require 'sinatra'
get '/greeting/hi' do
pass if params['hi'] == 'hi'
"Hi"
end
get '/greeting/*' do
"Hello"
end
パラメータ(params)
GetパラメータもPostパラメータもURLパラメータも以下だけでOKです.
params[:パラメータ名のシンボル]
ルーティングのブロックから取ったりもできるようですが.
外部テンプレート(Views)
viewsディレクトリを作っておくといい感じにしてくれます.
(erbの例)
.
├── server.rb
└── views
└── index.erb
Rubyコードからはerbメソッドで拡張子抜きのファイル名のシンボルを呼び出すとviews以下のerbが呼ばれます.
require 'sinatra'
get '/' do
erb :index
end
erbはこんな感じで普通に書きます.
<h1>Hello</h1>
<%= DateTime.now.strftime('%D %X') %>
共通レイアウト
共通レイアウトをまとめるlayoutも使えます.
viewsディレクトリの中にlayout.erbで配置するだけです.
.
├── server.rb
└── views
├── index.erb
└── layout.erb
layout.erbの中ではyieldで個々のテンプレートへ移譲します.
<html>
<head>
<title>layout Sample</title>
</head>
<body>
<%= yield %>
</body>
</html>
Rubyのコードでテンプレートを呼び出すときにlayoutの有無を選択できるようです.
get '/' do
erb :index, layout: false
end
JSONを返す
content_typeを明示的に指定する必要がある.
require 'sinatra'
require 'json'
get '/' do
content_type :json
data = { message: 'msg' }
data.to_json
end
ヘルパーメソッド
ビューで使いたいメソッドはhelpersメソッドのブロックに書いておくとテンプレート内から呼び出せるようになります.
require 'sinatra'
get '/' do
erb :index
end
helpers do
def help
"Help!"
end
end
HTMLはこんな感じ.
<%= help %>
これで"Help!"と表示されます.
静的ファイル配信
デフォルトでpublicディレクトリが公開されます.
こんな配置にしておけば
.
├── public
│ └── css
│ └── style.css
├── server.rb
└── views
└── index.erb
HTMLからはこれで呼び出せます.
<html>
<head>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
Link style
</body>
</html>
ファイルアップロード
params[:fileのinput名]にファイルに関する情報が入っているのでこれを使います.
params[:fileのinput名][:filename] # ファイル名
params[:fileのinput名][:tempfile] # ファイルそのもの
Rubyのコードはこんな感じ.
require 'sinatra'
get '/' do
erb :index
end
post '/upload' do
if params[:file1]
# カレントのimagesディレクトリへ保存する
path = "images/#{params[:file1][:filename]}"
File.open(path, 'wb') { |f|
f.write params[:file1][:tempfile].read
}
end
redirect back
end
indexのアップロードフォームも適当に.
enctype指定は忘れがち.
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="file1">
<input type="submit">
</form>
ファイルダウンロード
send_fileにカレントからのファイルパスを書けば送信できます.
typeでmimeも指定できる.
get '/' do
send_file 'images/img.jpg', type: :jpg
end
フィルタ
beforeとafterメソッドがあります.
そのままですね.
before do
...
end
after do
...
end
条件付きマッチもできます.
before '/loggerarea/*' do
logger.info "logging"
end
DB接続
Railsの様に永続化機能がフレームワークで用意されているわけではないので, 自由に選べます.
ActiveRecordの例が多いですが, 個人的にはそのレベルならRails使う方が良い気がするのでSequelが簡単かなと思います.
セキュリティ系
この話題は避けられない割にキリがないのですが, とりあえず最低限だけ.
XSS対策
helperにRackのescape_htmlの呼び出しを書いておくとviewでも使えるようになります.
helpers do
def h(text)
Rack::Utils.escape_html(text)
end
end
erbなら以下.
<%= h "<script>alert()</script>" %>
Sessionを使う
戸惑うことはないでしょう.
# 有効化
enable :sessions
# 記録
session[:message] = "This line use session"
# 読出
session[:message]
CSRF対策
Rack::ProtectionへSinatra本体が依存しているので, 既に利用がマージされてるようです.
コードの先頭で呼び出せばさらに色々使えます.
# server.rb
require 'sinatra'
require 'rack/protection' #ここ
use Rack::Protection #ここ
enable :sessions
全機能使おうとするとsessionが有効でないと使えないので注意.
Basic認証
StackOverFlowまんまですが.
def authorized?
@auth ||= Rack::Auth::Basic::Request.new(request.env)
@auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == ["ユーザ名","パスワード"]
end
def protected!
unless authorized?
response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
throw(:halt, [401, "認証が必要です\n"])
end
end
これで以下のように使えます.
get '/' do
"認証なし"
end
get '/auth' do
protected!
"認証あり"
end
古いですが, Sinatra RecipesにはDigest認証の方法についても記述がありました.
エラーページ
not foundはその名の通りnot_foundメソッドでキャッチできます.
他のエラーはerrorメソッドでキャッチできます.
errorメソッドは開発環境ではデバッグの表示が出るので, プロダクションに設定してやると分かりやすいです.
require 'sinatra'
configure do
set :environment, :production
end
get '/' do
"Hello"
raise "not found"
end
not_found do
"not found"
end
error do
"sorry"
end
ちなみにREADMEを読むとerrorは引数もとれるらしいです.
ロギング
普通にRubyのloggerです.
get '/' do
logger.info 'access to root'
end
Sinatra(というかRack?)がデフォルトで用意している出力先はenv['rack.logger']の様.
プロダクションではアプリケーションサーバが担当するのか?
テスト
Classicスタイルの場合.
Modulerスタイルの場合はappのアプリケーションを変えてやればよいそうで.
require './app.rb'
require 'test/unit'
require 'rack/test'
class MyAppTest < Test::Unit::TestCase
include Rack::Test::Methods
def app
Sinatra::Application
end
def test_root
get '/'
assert last_response.ok?
assert_equal last_response.body, "Hello"
end
end
デプロイ
長くなるので別記事にしよう.
他
便利ライブラリがあります.
GitHub - sinatra/sinatra-contrib
参考
公式
StackOverFlow - Display Sinatra Basic HTTP Auth On One Page Only
Rubyのlogger