Railsを使わずにWebアプリを書く

だいぶご無沙汰になっております。生きてます。


今はWikiFormeを記法エンジンに使ったWikiのような何かを開発しています。これがなかなか難航していて、公開までにはもう少し時間がかかりそうです。(と言ってもソースコードCodeReposにありますが)


Rubyで作っているのですが、Railsベースではありません。これは勝ち組ですねー


まじめなところ、Railsで作ると導入が面倒になるのでやめました。
Railsその他のWebフレームワークは非常に便利なのですが、CGIで動かすと絶望的に遅くてやってられません。生成したHTMLをオンメモリでキャッシュしても、ゼロから作ったCGIのキャッシュ無しよりも遅い。ずっと遅い。たとえキャッシュ機能を2, 3行のコードで入れられたとしても、これでは悲しい。


RailsをCGIで動かすなと言うのはもっともな話で、どう見ても設計からしてCGIで動かすことは眼中に無いわけです。でも、ちょっとWikiを置きたいとか、HTMLを書くのが面倒だからとりあえずCMSを置くという用途に、gemsでRailsをインストールして、その前にgemsを入れて、それからFastCGIを設定するというのはいかがなものか。



というわけで、最初にフレームワークの類は一切使わずにゼロから書いてみました。新しいインスタンスを作るとき、実際に使われるようになるまで遅延させて、不要なインスタンスを作らないようにしています。…遅延評価というよりはシングルトンなのですが、↓こんな管理クラスが核になっています。

  # すべてのクラスをシングルトンにするラッパー
  class Resource
    def method_missing(name)
      klass = const_get(name)
      create(klass)
    end
    def create(klass, name = klass.name, *initialize_args)
      instance = klass.new(*initialize_args)
      self.class.module_eval {
        define_method(name) {
          instance
        }
      }
      instance
    end
  end
  Rs = Resource.new

たとえばRs.Cacheという具合にアクセスすると、ResourceクラスにはCacheというメソッドは定義されていないので、method_missingが呼ばれます。そしてmethod_missingの中でCacheクラスのインスタンスを作って、Cacheというメソッドを定義しています。CacheメソッドはCacheクラスのインスタンスを返しています。2回目以降はCacheメソッドがそのまま使われるので、1つのクラスに付きインスタンスしか作られないという寸法です。
2回目以降のアクセス時のオーバーヘッドはメソッド呼び出し1回分で済みます。Hashを使うより見た目も良い。


Webフレームワークだと、テンプレートエンジンなどのインスタンスをあらかじめ作ってキャッシュしているわけですが、CGIだとオンメモリでキャッシュしてもまったく無意味で、遅くなるだけです。CGIだと1回走って終わりなので、コントローラやキャッシュクラスでも必要なときだけ作成すると速くなります。
ただ1回作って捨てるのではなくてキャッシュもしているので、FastCGIで動かしても高速です。



…と、速いのは良いのですが、ゼロから作るのは非常に面倒なのでお勧めできません。大半が車輪の再発明になって悲しくなります。やめましょう :-P


Railsの他にもWebフレームワークはいろいろあって、忘れられたRubyのWebフレームワーク - InfoQにいろいろ書いてあります。
その中でRamazeは「超シンプル」らしいので、使ってみたところ、依存しているライブラリが非常に少なくてインストールは簡単。確かに構造はシンプルで、ブラックボックスが薄めで良い感じ。でも、やっぱりCGIだと遅い。うーむ。



そんな紆余曲折を経て、Ramazeからさらに一枚皮をはがして、現在rackをベースにして書き直しています。Webフレームワークほどの便利機能はないものの、CGI/FastCGI/Mongrel/WEBrickを抽象化してくれたり、セッションの設定が楽だったり、ゼロから作るよりも圧倒的に楽。
そもそもコードを書かずにWebアプリケーションが作れてしまうのは困るわけです。コードを書かせろと。


今こんな感じです↓ CodeReposのlang/ruby/wikiforme/pressmateにあるので、フライング上等どんどんいじってくださいな。