そろそろwebアプリケーションを作ってみたいのでやってみる。
rubyでやるとなるとrailsがいちばん有名みたいなんだけど、数年前rubyに最初にトライしたとき参考書にrailsを使うとdbを使ったサイトが簡単に出来ますって良く書いてあって、db使わないサイト作るのは出来るけどめんどいですって書いてあったのであきらめた経緯がある。
いまはどうか知らないけどそのトラウマからrailsは敷居高い感じがするので、その次に有名っぽいsinatraでやってみる。
一応ここらへんを参考にひととおりざっくりとだけ読んでやってみる。
ちなみにだれも教えてくれるひとがいない環境でやってるので、どれが最新でどういう組み方がトレンドでスタンダードかは己のググり力に頼るしかないので、あわよくばすべてが間違っている可能性もあるけどとりあえずいまは気にしない。
- Sinatra: README (Japanese)
- SinatraによるWebアプリケーション開発入門 - TechDojo
- Rubyist Magazine - Sinatra 再入門、 Padrino / Rack / その先の何か
ざっくり読んだ限りだとこんな感じで作るのがいいっぽい。
require 'sinatra/base' class MyApp < Sinatra::Base set :sessions, true set :foo, 'bar' get '/' do 'Hello world!' end end
config.ruはこんな感じにした。
app_dir = File.expand_path(File.dirname(__FILE__)) require app_dir + '/app' MyApp.run! :host => 'localhost', :port => 9090
実行してみる。
$ bundle exec rackup [2013-04-01 20:10:42] INFO WEBrick 1.3.1 [2013-04-01 20:10:42] INFO ruby 1.9.3 (2013-02-06) [x86_64-darwin12.2.0] == Sinatra/1.4.2 has taken the stage on 9090 for development with backup from WEBrick [2013-04-01 20:10:42] INFO WEBrick::HTTPServer#start: pid=1570 port=9090 localhost - - [01/Apr/2013:20:11:02 JST] "GET / HTTP/1.1" 200 12 - -> / localhost - - [01/Apr/2013:20:11:02 JST] "GET /favicon.ico HTTP/1.1" 404 482 - -> /favicon.ico
動いた。
Sinatra::Baseのところをみてみると
Sinatra::Baseのサブクラスで使えるメソッドはトップレベルのDSLを経由して確実に使うことができます。 ほとんどのトップレベルで記述されたアプリは、以下の2点を修正することでSinatra::Baseコンポーネントに変えることができます。
sinatraの代わりにsinatra/baseを読み込む
(そうしない場合、SinatraのDSLメソッドの全てがメインネームスペースにインポートされます)ルート、エラーハンドラー、フィルター、オプションをSinatra::Baseのサブクラスに書く
Sinatra::Base はまっさらです。ビルトインサーバを含む、ほとんどのオプションがデフォルト で無効になっています。オプション詳細についてはOptions and Configuration をご覧下さい。補足: SinatraのトップレベルDSLはシンプルな委譲(delgation)システムで実装されています。 Sinatra::Applicationクラス(Sinatra::Baseの特別なサブクラス)は、トップレベルに送られる :get、 :put、 :post、:delete、 :before、:error、:not_found、 :configure、:set messagesのこれら 全てを受け取ります。 詳細を閲覧されたい方はこちら(英語): Sinatra::Delegator mixin included into the main namespace.
Options and Configuration -> http://www.sinatrarb.com/configuration.html
たしかにソースみてみるとほとんどfalseになってる。
sinatra/lib/sinatra/base.rb at master · sinatra/sinatra · GitHub
気になったのが、Configuring SettingsのUsing set, enable, and disableに
set :foo, 'bar' get '/foo' do "foo is set to " + settings.foo end
って書いてあって、set呼ぶだけでなんでsettingsにfooのメソッドが生えるのかわからなくてソースみたらsetの最後でdefine_singleton呼んでclass_evalってやつでメソッド生やしてるみたいだった。
ソースの難易度高すぎてあんまり理解できないけどそういうやりかたもあるんだってすごい勉強になった。
settings
https://github.com/sinatra/sinatra/blob/master/lib/sinatra/base.rb#L896
set
sinatra/lib/sinatra/base.rb at master · sinatra/sinatra · GitHub
define_singleton
sinatra/lib/sinatra/base.rb at master · sinatra/sinatra · GitHub
やってみる。
class MyApp def self.add_method(name, logic) (class << self; self; end).class_eval do class_eval("def #{name}() #{logic}; end"); end end end MyApp.add_method "umaibou", "puts 'umaibou'" MyApp.umaibou $ ruby test.rb umaibou
できた。すごい。
でもclass << self; self; endってわざわざ書くのいまいち意味がわからない。
selfで普通にやってだめなんだろうか。
やってみる。
class MyApp def self.add_method(name, logic) self.class_eval do class_eval("def self.#{name}() #{logic}; end"); end end end MyApp.add_method "umaibou", "puts 'umaibou'" MyApp.umaibou $ ruby test.rb umaibou
普通にできた。
ただこっちはclass_evalの中にselfを書かなきゃならない。
どっちも同じことやってると思うんだけどなんか意味があってやってるのかなあ。
ソースの難易度が高すぎてよくわからない。
まだソース読んでるけど難易度高すぎてよく理解できない。
とりあえずいま意味わからないのが set :logging, true にしたときなんでloggerが有効になるのか。
ざっくりソースを検索すると
- setup_default_middlewareが呼ばれないとloggerが設定されないっぽい
- setup_default_middlewareはbuildから呼ばれる
- buildはnewから呼ばれる
- じゃあnewしてんのっていうとrun!を呼んでるからしてないと思う...
- newしてるとしたらprototypeの中
- prototype呼んでるのはcall
- callの中でprototype.callしてる
- callってどこで呼んでるんだよ...
というところで闇にはまってずっと悩んでたんだけど、ふとnewのコメント読むと
# Create a new instance of the class fronted by its middleware
# pipeline. The object is guaranteed to respond to #call but may not be
# an instance of the class new was called on.
ざっくり理解するとclassのnewじゃなくcallのとき動くんすよって書いてある。
callってどこで呼ぶんだよ...ってまた思ったけど、そういえばかなりまえにドラクエでrackを覚えるとかなんとかそんなのをみた気がすると思ってぐぐってみたらあった!
1.rackupコマンドはWebサーバを起動する。
2.その際、config.ruという設定ファイル(Rubyスクリプト)を読み込む。
3.config.ruでは、Webアプリのインスタンスをrunメソッドに渡す。
4.Webアプリのインスタンスは、1引数のcallメソッドを持っている必要がある。
5.callメソッドは、3つの返り値、すなわち(1)100以上の数字からなるステータスコード、(2)少なくとも"Content-Type"をキーに、文字列を値に持つハッシュによるヘッダー、および(3)eachできるボディを返す。
なるほど、rackで動かすにはrunとcallがいるのか...
たまたまrunって名前でメソッドを定義して呼んでるからだってしか思ってなかったし、config.ruもそういうファイルがないと動かないって思ってただけだけど、ちゃんと理由があったのか...
すごい勉強になる。
ということでなんとなくひととおり理解できたので次から使っていくことにする。