55
Crafting Rails 4 Applications 8Key-valueバックエンドを使用して アプリケーションを翻訳する

08 translating applicationsusingkeyvaluebackends

Embed Size (px)

Citation preview

Page 1: 08 translating applicationsusingkeyvaluebackends

Crafting Rails 4 Applications

第8章 Key-valueバックエンドを使用してアプリケーションを翻訳する

Page 2: 08 translating applicationsusingkeyvaluebackends

この章で学ぶこと• i18n(internationalization:国際化)のフレームワーク• Sinatra

• Railsのルーター• Devise

• Capybara

他にも盛りだくさん(i18nの話題は意外に少なめ)

Page 3: 08 translating applicationsusingkeyvaluebackends

YAMLを使用したローカライズ

言語コードをファイル名に持つYAMLファイルを準備する

I18n.lやi18n.tで呼び出すことで、ロケールに合うYAMLの文字列が使用される文字列をYAMLに集めるのは行儀も良い

Page 4: 08 translating applicationsusingkeyvaluebackends

おまけi18n-tasks gemを使用すると、YAMLロケールとビュー内i18nの照合などを行ってくれる

http://qiita.com/fakestarbaby/items/d9ad517fe674059041cd

Page 5: 08 translating applicationsusingkeyvaluebackends

本章の目標YAMLによるローカライズの特徴:

• Railsのデフォルト機能•複数のアプリケーションでyamlを共有しようとすると面倒• yaml更新後アプリの再起動が必要

本章ではKey-Valueストア(ここではRedis)をYAMLの代わりに使用してみるさらにSinatraを使用して訳語を動的に更新してみる

(本章ではプラグインではなくRailsアプリを作る)

Page 6: 08 translating applicationsusingkeyvaluebackends

8.1 Rails::Applicationの復習

(Railsのソースコード)

Rails::ApplicationクラスはRails::Engineを継承

これにより以下が行われる• Active Supportの読み込み• 読み込みパスの設定• ログ設定• アプリケーション自身のルーターとmiddlewaresスタックの使用 (7-

4, p149)

• すべてのプラグインの読み込み・初期化• コードとルートが更新されたときの再読み込み• タスクの生成とgenerate(必要に応じて)

Page 7: 08 translating applicationsusingkeyvaluebackends

3つのconfigファイル(第一章参照)

Application.initialize!で最終的にアプリケーションの初期化が行われる

Page 8: 08 translating applicationsusingkeyvaluebackends

初期化(p88)エンジンの初期化(railties)

初期化の例

Page 9: 08 translating applicationsusingkeyvaluebackends

参考コンソールで以下を実行するとイニシャライザを確認できる

Rails.application.initializers.map(&:name)

アプリ自身のイニシャライザの他にrailtiesとエンジンのイニシャライザもある

Page 10: 08 translating applicationsusingkeyvaluebackends

Rakefileapplicationファイルをrequireし、アプリケーションのrakeタスクを読み込んでいるデフォルトではenvironment.rbを読み込んでいないことに注意(速度をかせぐため)

rakeで:environmentを指定することでenvironment.rbを読み込む(rake db:migrateなどデータベースへのアクセスが必要な場合)

Page 11: 08 translating applicationsusingkeyvaluebackends

config.ruconfig.ruはRackの設定ファイルここではconfig/environment.rbをrequireしているさらにRailsアプリケーションをRackアプリケーションとして実行

Railsの設定ファイルはこれらの多くのファイルに分割されている

これらをhttp.confのごとく1つのファイルにまとめてみると初期化がよくわかる

Page 12: 08 translating applicationsusingkeyvaluebackends

1つにまとめた初期化

Page 13: 08 translating applicationsusingkeyvaluebackends

rackを起動空ディレクトリに前述のconfig.ruを置いてrackupを実行するとrack

が動作し、これだけでwebアプリケーションになる(localhost:9292

をブラウザで参照)

たった1ファイルでも立派なRailsアプリであり、通常のRailsはこれを拡張したものにすぎない

Page 14: 08 translating applicationsusingkeyvaluebackends

1ファイルRailsWebサーバーはSingleFile#call()を実行し、これによってRailsルーターが指し示すミドルウェアに渡される

Railsルーターはroot toパターンにマッチするリクエストをカスタムRackアプリケーションに渡す

Page 15: 08 translating applicationsusingkeyvaluebackends

ルーティング• draw()メソッドは、routes.rbが更新されるたびに既存のルーティングをすべてクリアしてエントリを再読み込みする

• さっき使ったroutes.appendとroutes.prependは一度読み込んだルーティングを常駐させる点が異なる

Page 16: 08 translating applicationsusingkeyvaluebackends

8.2 i18nのバックエンドと拡張

i18n.translate (エイリアス: i18n.t)

i18n.localize (エイリアス: i18n.l)

上記メソッドは、i18n.backend()に保存されたバックエンドに処理を委譲しているRailsではデフォルトで以下の3つのバックエンドが使用可能

i18n::Backend::Simple

デフォルト。YAMLを読み込んでメモリに保持i18n::Backend::KeyValue

任意のKey-Valueストアを使用可能i18n::Backend::Chain

複数のバックエンドを連鎖させることができる

Page 17: 08 translating applicationsusingkeyvaluebackends

i18nの機能config/environment/production.rbの以下のオプションは該当のロケールがない場合にフォールバックする

Rails外部でi18nを行っている場合フォールバックを以下のように変更できる

アクセントを除去する機能

※これらはバックエンドそのものではなくi18nの機能

Page 18: 08 translating applicationsusingkeyvaluebackends

i18nの機能• I18n::Backend::Cache: 翻訳結果にキャッシュストアを適用する(参照、式展開、

pluralize後)

• I18n::Backend::Cascade: カスケード。"foo.bar.baz"がない場合に"foo.bar"も探す• I18n::Backend::Fallbacks: 現在のロケールがない場合にデフォルトのロケールにフォールバックする

• I18n::Backend::Gettext: 国際化ライブラリgettextと.po ファイルをサポート• I18n::Backend::InterpolationCompiler: 式展開のキー (%{model}など) を訳文にコンパイルしてパフォーマンスを向上

• I18n::Backend::Memoize: 結果を参照。I18n::Backend::Cacheと異なりインメモリハッシュを使用する。Key-Valueバックエンドを使用する際に便利

• I18n::Backend::Metadata: メタデータを訳語に追加。複数形の個数や式展開の値など• I18n::Backend::Pluralization: “i18n.plural.rule"に基いて複数形/単数形をサポート• I18n::Backend::Transliterator: i18n.transliterate.ruleに基いてアクセント記号除去をサポート

Page 19: 08 translating applicationsusingkeyvaluebackends

今回はI18n::Backend::KeyValueとI18n::Backend::Memoizeのみを使用

以下のAPIを満たすKey-Valueバックエンドであればどんなものでもi18nのストアとして使用できる

• @store[] キーに対応する値を読み出す• @store[]= キーに対応する値を保存• @store.keys 保存されているキーをすべて読み出す

ここでは条件に合うストアとしてRedis gemを使用する

Page 20: 08 translating applicationsusingkeyvaluebackends

Redisサーバーをインストールして起動brew install rails

redis-server

RedisをGemfileに追加してbundle installを実行

コンソールを起動して動作を確認

Page 21: 08 translating applicationsusingkeyvaluebackends

lib/translator.rbを作成し、Redisインスタンスを設定するI18n::backend::Memoizeがポイント

config/application.rbでi18nフレームワークを設定

Page 22: 08 translating applicationsusingkeyvaluebackends

Key-ValueバックエンドはYAMLファイルの訳語の読み込みを各リクエストの前ではなく必要に応じて行なう

YAMLのすべての訳語をRedisストアに読み込んでおくには以下をターミナルで実行すればよい (YAML=>JSON変換も行われる)

(rails runnerはrubyファイルを実行するコマンド)

コンソールで確認:

Page 23: 08 translating applicationsusingkeyvaluebackends

8.3 Sinatraで書くここまで準備ができたところで、 Sinatraでアプリを書いてみる

Sinatraを使うと以下のように恐ろしくシンプルなWebアプリを書ける

ここでは一風変わった方法として、Sinatraを単独で使うのではなくRailsに統合する(というよりテスト/認証/セッションなどを拝借するという感じか)

Page 24: 08 translating applicationsusingkeyvaluebackends

テストから作成準備としてCapybaraを導入(p28)

Page 25: 08 translating applicationsusingkeyvaluebackends

テストロケールをポーランド語にすると日付表記がポーランド語になるか

Translator.reloadで既存のキーを捨てて訳語を再度読み込む

Page 26: 08 translating applicationsusingkeyvaluebackends

Translator::reload!を実装

Sinatraをビルドしてないのでこの時点ではテストは失敗

Page 27: 08 translating applicationsusingkeyvaluebackends

Sinatraを準備↓

app.rbにSinatra

アプリを実装→

ルーティングは/:from/:toの形式(fromは元のロケール、toは変更先ロケール)

Sinatraに明示的に転送している

ルーティングを設定

hamlテンプレートの評価はアプリケーションと同じコンテキストで行われていることに注意

Page 28: 08 translating applicationsusingkeyvaluebackends

hamlテンプレートapp.rbに書く

Page 29: 08 translating applicationsusingkeyvaluebackends

Sinatraのhamlテンプレートはアプリケーションと同じコンテキストで評価されていることに注意

Railsのテンプレートはビューのコンテキストで評価されるため、Railsはコントローラのインスタンス変数をビューで使用できるよう、背後でせっせとビューにコピーしている(p9)

ビューでのコントローラメソッドの呼び出しも明示的にコントローラ.メソッド()としなければいけない

Page 30: 08 translating applicationsusingkeyvaluebackends

hamlテンプレート内でlocale_valueが呼び出されている(キーと値を受け取ってRedisの値を返す)

このメソッドは標準のi18nハッシュも扱える

Page 31: 08 translating applicationsusingkeyvaluebackends

i18nのハッシュマッチングしやすくするため、{ "foo.bar" => "baz" }という訳のハッシュを保存すると、"foo.bar" は分解されて { "foo" => { "bar" =>

"baz"} } というハッシュも保存される

この場合、ハッシュの中には重複した訳文が含まれることになる

Page 33: 08 translating applicationsusingkeyvaluebackends

以下が表示されるがPOSTが未実装のためボタンを押すとエラー

テストも失敗する

Page 34: 08 translating applicationsusingkeyvaluebackends

テストを通すためにSinatraにpostルートを追加する1. i18nバックエンドに訳語を保持2. 訳語をJSONからRubyにデコード3. save()でRedisに保存し、ファイルシステムに出力4. 訳語を再表示する

※escape: falseとしているのは複合ハッシュを{ "foo\000.bar" =>

"baz" }のようにしないため

Page 35: 08 translating applicationsusingkeyvaluebackends

成功今度はStore translationボタンを押すと訳語が保存される

Page 36: 08 translating applicationsusingkeyvaluebackends

8.4 Deviseによるアプリケーション間認証とCapybara

おなじみDevise gemを使用して実装する

※DeviseはミドルウェアとしてWardenを使用する

bundle installでgemをインストール後、以下を実行してgenerateする

これでデフォルトのロケールファイルやイニシャライザも導入されるあとは若干追加設定を行なえばよい

Page 37: 08 translating applicationsusingkeyvaluebackends

Action Mailerdevelop環境にAction Mailerを導入(なぜ?)

アプリケーションビューにflashを追加

rootへのルーティングを追加

HomeコントローラでSinatraへのリンクをレンダリング

Page 38: 08 translating applicationsusingkeyvaluebackends

Deviseモデルを生成deviseでAdminモデルを生成

マイグレーション

(生成されたtest/fixtures/admins.ymlは不要なので削除してテストがパスするようにする)

Page 39: 08 translating applicationsusingkeyvaluebackends

認証画面rails sを実行して/admins/sign_in/にアクセス→

Deviseが提供するヘルパーメソッドを使用して、他のコントローラでも認証を導入できる(Adminモデルなのでauthentication_admin!)

Page 40: 08 translating applicationsusingkeyvaluebackends

SinatraでDeviseを使うには

DeviseにはSinatra用のヘルパーがないが、Wardenを使ってできる

コントローラでauthenticate_admin!()を実行すると実際には以下が実行される

env[“warden”]オブジェクトはWardenのプロキシであり、DeviseによってRails::Engine経由でRailsミドルウェアに追加される

このミドルウェアはルーターより前に実行されるので、Sinatraから使用できる

Page 41: 08 translating applicationsusingkeyvaluebackends

リクエストの流れ-WardenなどのミドルウェアはRailsルーターより前に実行される

- env[“warden”]プロキシはここに作成される- ルーターからコントローラとSinatraに振り分けられる- Sinatraからenv[“warden”].authenticate!を直接呼ぶ

Page 42: 08 translating applicationsusingkeyvaluebackends

before_filterしたいSinatraでも、Railsのコントローラと同じようにbefore_filterでDevise認証を追加できるだろうか…?

それも、Sinatraアプリケーション自体を変更せずに(変更できないサードパーティ製Sinatraアプリにも認証を追加したいので)

Page 43: 08 translating applicationsusingkeyvaluebackends

ルーターレベルの認証実は、ルーティングで認証を行なうことで、Sinatraアプリを変更せずに済む

ルーティングをこのように変更するとテストがfailするので認証が機能していることがわかる

Page 44: 08 translating applicationsusingkeyvaluebackends

DeviseをインストールするとRailsルーターでauthenticate()が使用できるようになる

authenticate()のDeviseソースコード↓

Page 45: 08 translating applicationsusingkeyvaluebackends

Capybaraによる認証操作テストを通すために結合テストにセットアップフックを追加する

Page 46: 08 translating applicationsusingkeyvaluebackends

これでテストはパスする

Page 47: 08 translating applicationsusingkeyvaluebackends

Capybaraによる認証操作Capybaraの認証操作は、ブラウザに対して直接入力している

CapybaraでセッションやCookiesをいじって認証を行なうことはできないそのわけは…

Page 48: 08 translating applicationsusingkeyvaluebackends

Capybara本書でCapybaraでテストするときにはActionController::IntegrationTestではなくActiveSupport::IntegrationCaseを使用していることに注意

ActiveController::IntegrationTestはセッションやcookiesにフルアクセスできるが、Capybara APIからはアクセス不可

⇒ActiveController::IntegrationTestでCapbaraは使えない

Page 49: 08 translating applicationsusingkeyvaluebackends

CapybaraCapybaraはそもそもエンドユーザーの立場での操作を再現する結合テストのためのもの

エンドユーザーはセッションのような実装を気にかけることはない

テストが実装に密着しすぎていると、たとえばセッションをcookiesに変更しただけでテストが失敗してしまう

⇒そういうわけでCapybaraはあえて実装を覆い隠すように作られた

Page 50: 08 translating applicationsusingkeyvaluebackends

CapybaraとドライバCapybaraは複数のドライバをサポートする

(Rack、Selenium、Poltergeistなど)

テストが実装に密着しているとドライバやブラウザを切り替えできない

ブラウザによってAPIの範囲が違うので、Capybaraは最大公約数的にサポートする

Page 51: 08 translating applicationsusingkeyvaluebackends

CapybaraとドライバCapybaraのデフォルトドライバはRack

⇒パフォーマンスは高いが機能が少ない(JavaScriptをテストできないなど)

ドライバをSeleniumやPoltergeistなどに切り替えることでJavaScriptをテストできるtest_helpers.rbの記述例↓

Page 52: 08 translating applicationsusingkeyvaluebackends

CapybaraとドライバSeleniumはデフォルトでFirefoxをブラウザとして使用する

Capybara+Seleniumでテストを実行するとFirefoxが起動するのですぐわかる

Page 53: 08 translating applicationsusingkeyvaluebackends

Transactional FixturesCapybaraを使用するときはtransactional fixturesをオフにすること

理由:

- Capybaraはスレッドごとに新しいブラウザインスタンスを起動する(実際の動かしてみるとわかりやすい)

- データベーストランザクションのテストでtransaction fixturesをオンにしても、インスタンス同士が関連していないのでうまくいかない(トランザクションデータはコミットするまで共有できない)

- transactional fixturesを使うとテスト間でデータベースがクリアされない(Database Cleaner gemなどを使えばできる)

Page 54: 08 translating applicationsusingkeyvaluebackends

8.5 まとめ• Railsの構造を改めて理解• Rackのおかげで異なるフレームワーク(RailsやSinatra)が共存できる

• Deviseかわいいよ• Capybaraかわいいよ• Railsを構成する多くの要素と連携を熟知することで、Railsアプリケーションの開発に役立てよう• 自分のコードをスリム&DRYにできる• 他人のコードを読む時に役立つ• デバッグに役立つ

Page 55: 08 translating applicationsusingkeyvaluebackends

お疲れさまでした!