寝過ぎると帰ってきて寝て起きて飯食って出掛けるだけになっちゃうな。気をつけよう。
るびま用にRubyゲーム会議の紹介を書かないといけないんだけどなかなか。 あー、いまさらだがRubyゲーム会議にマスコットキャラがあれば良かったなー。
某イベントのページ作るのにまたGoogleAppEngineを使うのでappengine-jrubyを使ってたんですよ。
前回はRack素+なんかテンプレートエンジン*1+素でDatastore使うなどかなり力技で組んでたんだけど、今回はもうすこしおとなしく、sinatra+erb+datamapperで。
DataMapperとかよくわからんけどなんか動いた。何が起きてるのかあまりにもよくわからないんだがこれでいいんだろうか。不思議である。
しかし動いたと思ったらやっぱり動いてなくてsqlite3にバイナリの0をつっこもうとしたらNULL文字扱いされるのかそこで値が終わってしまった。dm-appengineならBlobって型指定ができるんだけどdm-sqliteにはないわ。本番ではsqliteいらないのでin_memoryてのを指定して回避できた。
SinatraはRack::Testでテストできるよって書いてあるんだけど、Rack::Testが無いんですよ。さんざん探したらrack-testを別途入れないといけないことにしばらくして気付いた。別途入れるならそう書いてよ…。
Rack::Testでクッキーを食わせたことをテストしようとしたけど全然わからなかったので諦めた。
というかテストは普通のRubyで動かしてるんだけど、これappengine-jrubyのローカル開発環境でも動かせるんだよなきっと?やりかたわからないのでこれも諦めたが。
そんなこんなで画像のアップローダが出来てテストは手元でちゃんとうごいたので設置。普通に動いてるなーと思ってたんだけど500エラーで上がんねーとの報告が。
でも俺の画像は上がるなーと思ったら、でかいファイルが上がらない模様。いろいろ試したら64k以上のファイルが上がらない。
エラー場所はJRuby版Rackの中?StringIOをTempfileにキャストできねーよ!ってさ。知らんがな。
appengine-rackではTempfileをStringIOに置き換えてるようだ。これはAppEngineでTempfile作れないからだね。わかる。
JRuby版Rackでは入力が一定サイズ(既定で64k)を越えたらTempfileを使うようにしてる。これもわかる。
Tempfileを作るところはJavaのRubyTempfileクラスにRubyのTempfileクラスを渡してRubyオブジェクト作ってねーってやって、戻り値をRubyTempfile型の変数につっこんでる。appengine-jrubyだとRubyのTempfileはStringIOのサブクラスになっちゃうのでこのキャストはできなくて例外。
場所は分かったんだけど原因と解決法はどうすりゃいいんだ。appengine-jrubyのバグかと思ったけど特に報告も出てないようだし俺の使い方なり環境がおかしいというのも排除できないな。
どうしようか悩んだけど、夜だったのでとりあえず動かして寝たい。Tempfile作ってるorg.jruby.rack.RackRewindableInputにDefaultThresholdというstaticフィールドがあってこれがTempfileを作るかどうかの判定に使われてる。閾値以下のサイズはメモリに保持するようになってるんでStringIOと同じ意味だし、閾値をめっちゃ上げて対処したい。
JRubyはいまいちよくわからなかったが、適当に書いたら動いた。
##highlight ruby if defined?(JRuby) then import org.jruby.rack.RackRewindableInput RackRewindableInput.default_threshold = 1024*1024*1024 end
これをconfig.ruの頭の方に書いといた。 1GBもいらないんだが適当に。いやでかすぎてまずいかも。まあ動いてるからいいか。
*1 radiantとかなんとか
ソース整理してちょっとすっきりした。
pragmaディレクティブをすっかり忘れてたのも追加した。
includeディレクティブは拡張で追加されるのは分かったんだけど、拡張は別ファイルをrequireするとパースできるようにしたいと思って考えてみた。
まあもちろんできなくはないが、何も考えずに作るとルールは後から上書きするしかない。上書きはたぶんできたはず。
たとえば
##highlight ruby #in tokenparser.rb rule :directives do any( :define_directive, :if_directive, :line_directive, (中略) :unknown_directive) end
こんなルールがあったとしよう。:directivesはいずれかのディレクティブにマッチするルールということを定義してるんだけど。
こいつに#includeディレクティブを追加したい。
##highlight ruby #in include_directive.rb rule :directives do any( :include_directive, #追加したぞ! :define_directive, :if_directive, :line_directive, (中略) :unknown_directive) end
追加するだけなのに全部コピペ上書きかー。
仮にここに新しく#nantokaディレクティブが追加されたときはどう対処すればいいんだろうか。nantoka_direcitive.rbは#includeについては記述するべきじゃないだろうけど、上書きだから入れないと#includeのパースルールが消えちゃうね…。
というわけで単に上書きするのは無理か。
##highlight ruby #in tokenparser.rb Directives = [ :define_directive, :if_directive, :line_directive, (中略) :unknown_directive, ] rule :directives do any(*Directives) end
こんな感じにしておけばDirectives定数にルール名を追加するだけで増やせるんだけどね。でもこれを拡張できそうなところ全部に仕込んでいくのは大変かも。あとこのままだと展開のタイミングがはやすぎる。
無駄に悩んでるよりも手動かしちゃった方が早いかもねー。