RamazeでAmrita2が使えるようになりました

id:keita_yamaguchiさんが、RamazeのテンプレートエンジンをAmrita2 2.0.0 で動くように直してくれました。

これに便乗して、Ramazeのサンプルにあった132行のブログエンジンのAmrita2バージョンを作ってみました。

コントローラのソースを一部抜粋するとこんな感じです。

  def index slug = nil
    title = nil
    if slug.nil?
      @posts = BlogPost.collect
      raise Ramaze::Error::NoAction,
            'No blog posts found, create
             entries/YYYY.MM.DD.My.First.Blog.Post' unless @posts.any?
    else
      raise Ramaze::Error::NoAction,
           'Invalid blog post' unless post = BlogPost[slug]
      post = BlogPost[slug]
      title = post.title
      @posts = [ post ]
    end

    title_hook = Amrita2::Core::Hook.new do
      if title
        render_child(:post_title, :href=>'/', :text=>title)
      else
        render_child(:index_title, :text=>TITLE)
      end
    end

    @data = binding

    %(
      <<h1 :title_hook | AcceptData[:hook] <
        <<a :post_title | Attr[:href] <
          <<:text>>
        <<span :index_title <
          <<:text>>

      <<div.hentry :posts <
        <<h2 { :url=> '/' + $_.slug } <
          <<abbr.updated title="$1" :| Eval["Time.parse($_.date).iso8601", :date] <
            $2
          <<a.entry-title href="$1" rel="bookmark" :| NVar[:url, :title] <
            $2
  
        <<div.entry-content :body | NoSanitize >>
    )
  end

あえて、Amrita2のいろんな機能を詰め込んでみました。特に、Hookという機能は、けっこう面白いと思います。

Hook オブジェクトを作成すると、テンプレートエンジンがレンダリングしている途中で、ユーザコードに一時的に制御を戻すことができます。

上記の例だと、title_hookというタイトルを作成する所に介入しています。ここで、title というローカル変数がnilかどうかで、:index_titleか:post_titleの、どちらの要素を使うかを選択しています。また、その要素のレンダリングはAmrita2が行なうわけですが、そこに渡すデータもこの処理の中で作成しています。

テンプレートの中に条件判断を書かないで、Rubyのコードとして書けますので、これをうまく使うとプレゼンテーションとプレゼンテーションロジックがきれいに分離できるのではないかと思います。

また、これを使うとevalを使わないでテンプレートの処理を行なえるので、ruby1.9では少しは早くなるかもしれません。(その代わりに ブロックのネストが深くなるので、どうなるかはわかりません)

なお、Amrita2は今の所はruby1.9には未対応ですが、レビュアブルマインドの開発が落ち着いたら、1.9をターゲットにして最適化していこうと思っています。HTMLをRubyコンパイルして動かしているので、YARVが喜ぶようなRubyコードを吐くように直していけば、いくらでも速くできるのではないかと考えています。