GetTextへの自動対応
これは、Amrita2の最も重要な機能と言えるかもしれません。Amrita2では、ほとんど自動的にテンプレートを国際化することができます。
Gettext用のフィルターがあってこれを組み込むと、テンプレート内の文字列に対して、自動的に_()を呼び出す処理を行います。
require "amrita2/template" require "amrita2/gettext" include Amrita2 tmpl_text = <<-END <<html< <<body< <<h1 :title |Attr[:title, :body]>> <<p < Amrita2 is a html template libraly for Ruby. END tmpl = Amrita2::Template.new(tmpl_text) do |e, src, filters| filters << Amrita2::Filters::GetTextFilter.new end tmpl.set_trace(STDOUT) tmpl.text_domain = 'test' puts tmpl.render_with(:title=>{:title=>"hello world", :body=>"hello world" })
このサンプルから出力されたコンパイル済みソースを抜粋すると、次のようになっています。
__stream__.concat("<html><body>") __stream__.concat(XXTitleInstance.render_with($_.amrita_value("title"), __binding__)) __stream__.concat("<p>") __stream__.concat(_("Amrita2 is a html template libraly for Ruby.") % $_) __stream__.concat("</p></body></html>")
"Amrita2 is a ..."という文字列が、_()呼び出し後に出力されています。全ての文字列に対して自動的にこの処理が組み込まれます。
Rails用のブリッジでは、自動的にこのフィルターを組み込んでいますので、Rails上では、何もしないで国際化対応になります。
次のRakeタスクを実行すると、テンプレート上の全ての静的な文字列を国際化対象の文字列としてpoファイルを生成します。
task :updatepo do $: << 'vendor/plugins/amrita2/lib' require 'gettext/utils' require 'amrita2/gettext' require 'amrita2/macro' GetText.update_pofiles("rmind", Dir.glob("{app,config,components,lib}/**/*.{rb,rhtml,a2html,a2}"), "rmind 1.0.0" ) end
レビュアブルマインドは、この機能を利用して国際化しています。
「ザリガニが見ていた...。」さんのAmrita2紹介記事
- Amrita2でビューはさらにシンプルに! - ザリガニが見ていた...。
- Amrita2での書き方例OKとNG - ザリガニが見ていた...。
- Amrita2のフィルター色々 - ザリガニが見ていた...。
「ザリガニが見ていた...。」さんでAmrita2についての解説記事を書いていただきました。
「ザリガニが見ていた...。」さんのRailsについてのエントリは参考になるものが多くて、私もレビュアブルマインドを開発する時や、現在行なっているRails2.0対応の作業で参考にさせていただいています。
そういうブログでAmrita2について書いていただけたことは、本当にありがたいことです。特に、Rails(ERB)に慣れている方からの視点でAmrita2について書いてもらうと、開発している側にはわからないことがいろいろ見えてきます。
そこで、この3つの記事に対して、補足、感想等を書かせていただきます。
インストールについて
id:zariganitoshさんは、tarボールを vendor/plugin に展開して使用されているようですが、UsingWithRailsに書いてあるように、gemでインストールすることもできます。
$ sudo gem install amrita2 $ cd (アプリケーションルート) $ mkdir vendor/plugins/amrita2 $ cp /usr/lib/ruby/gems/1.8/gems/amrita2-2.0.1/init.rb vendor/plugins/amrita2/
/usr/lib/ruby/gems/1.8/gemsは、環境によって違いますが、gemsがインストールされているディレクトリです。
ERbからの段階的な移行
最初からAmrita2のすべてを理解する必要はなく、便利だと思う機能から利用していけば良いのだ。
これは、まさに私が意図していたことで、従来のERbテンプレートから少しづつ段階的に移行していけるように工夫しています。
次の例のように(これはanalyticsのjsコードを挿入している所)、<<%< というマークから段を下げた部分は、ERbテンプレートと同じように扱われます。
<<%< <script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> var pageTracker = _gat._getTracker("<%= $rmind_config[:google_analytics_account] %>"); pageTracker._initData(); pageTracker._trackPageview(); </script>
CDATAで囲んだ所も同様にERbとして扱われます。
だから、Amrita2の仕様やエラーメッセージがよくわからない場合は、すぐに、ERbに戻ってそこだけERbで書くことができます。
出力ソースのインデント
ブラウザの描画結果はまったく同じなのだが、やはりHTMLソースも改行されて欲しい...。
Amtita2の出力結果は、はっきり言ってインデントがおかしくて、人間が見るには見づらいものになっています。
後の記事にありますが、これを補うには、tidy等の外部コマンドをフィルターとして組みこんでインデントしてください。
次のようにすると、デバッグ時のみインデントされた出力を行なうことができます。プロダクション環境では、余分なコマンド呼び出しを行ないません。
(config/environment.rb) if ENV['RAILS_ENV'] == "development" IndentByTidyIfDebug = Amrita2::Filters::CommandFilter['tidy -q -xml -indent -utf8'] else IndentByTidyIfDebug = nil end
(layout/application.html.a2) <<html :| IndentByTidyIfDebug < <<head< <title>Reviewable Mind</title>
ただし、tidyのインデントは、railsの javascript_tag の出力を壊してしまうようで、javascriptが正しく実行されないことがあります。(他に適当なHTMLのプリティプリンタが無いか物色中です)
表記述の仕様
ここは(ここも)、説明が完全に不足していた所ですが、仕様を読み取ってわかりやすくまとめていただけました。
- 「|」で区切った範囲が、tdタグの1セルと解釈される。
- 「||」で区切った左側で属性を指定すると...
- その行はtdタグの属性値の指定になる。
- 属性値の指定が無ければ、半角スペースで埋めておけば良い。
- 上下のハイフンの連続は、無くてもOK。
- 「|」の間隔も最低1スペースあればOK。(インデントを揃えておかないと、この書式の意味は無いが。)
- ちなみに、「||」の右側の「|」を「||」に置き換えるとthタグと解釈された。
- td、th以外の目的で「|」を利用するときは、エスケープ「\|」しておく必要がある。
まったくその通りです。
この記法は激しく好みが分かれる所のような気がしますが、次のようにフォームと組み合わせた場合等は、有効ではないかと思います。
次の例は、レビュアブルマインドのサインアップフォームです。
<% signup_form = amrita_define_form(:user, :action=>:login) do |f| f.text_field :login f.password_field :password f.add_field_element :password_confirm, password_field_tag(:password_confirm, "") f.text_field :email, :size=>50 f.add_field_element :terms_of_use, check_box_tag(:terms_of_use, false) f.add_field_element :link_to_terms_of_use, link_to('terms of use', '/term.html') end %> << :signup_form | AcceptData[:hook] < <<two_columns_form< <<<--------------------------------------------------------------- ||| Login ID: | <<:login>> | <<<--------------------------------------------------------------- ||| Password: | <<:password>> | <<<--------------------------------------------------------------- ||| Password(confirm): | <<:password_confirm>> | <<<--------------------------------------------------------------- ||| E-Mail: | <<:email>> | <<<--------------------------------------------------------------- ||| Terms of use: | <<:terms_of_use>>   | ||| | I have read and agree to the   | ||| | <<:link_to_terms_of_use>> | <<button_bar <--------------------------------------------------- <<:| Join[:nbsp]< %= submit_tag _('Signup') %= link_to _('Login'), :action => 'login'
two_columns_formは、マクロという機能を使用して、tableタグに置き代わりますが、ここに直接 table タグを指定してもかまいません。
ちなみに、この例では、lableタグを後から埋めこむためにマクロを使用しています。
Rubyブロック内のルール
# ブロック内にRuby式以外が存在するとエラー。 # NG % 3.times do <p> index.html.a2 </p> % end # ブロック内がすべてRuby式なら大丈夫。 # OK % 3.times do %= "<p>" %= "index.html.a2" %= "</p>" % end
行頭の%の記述ですが、ここは、ちょっと特殊な処理を行なっています。
- 行頭に%が続く限り、(インデントを無視して)一つのCDATAブロックとしてまとめる(後にERbとして一括処理)
- 行頭が%=だったら、その1行分の結果を出力する処理に変換
この記法は単発の短い処理を書くのには適していますが、ブロックを使う処理や、複数行にまたがる処理をERbで書く時は、次の書き方の方がおすすめです。
<<%< <% 3.times do %> <p> index.html.a2 </p> <% end %>