久しぶりに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で個々のテンプレートへ移譲します.
<!DOCTYPE html> <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