プラグイン化と公開準備 のバックアップソース(No.1)
更新- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- バックアップ を表示
- ソフトウェア/rails/言語ネゴシエーション/プラグイン化と公開準備 へ行く。
- 1
[[ソフトウェア/rails/言語ネゴシエーション]] #contents * プラグイン化 [#j4d21556] http://akimichi.homeunix.net/hiki/rails/?%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3 ~ http://blog.s21g.com/articles/374 ~ あたりを参考に。 まずは script/generate でプラグインの雛型を作成し、 起動用スクリプト init.rb に require を追加。 LANG:console $ script/generate plugin LanugageNegotiation create vendor/plugins/lanugage_negotiation/lib create vendor/plugins/lanugage_negotiation/tasks create vendor/plugins/lanugage_negotiation/test create vendor/plugins/lanugage_negotiation/README create vendor/plugins/lanugage_negotiation/MIT-LICENSE create vendor/plugins/lanugage_negotiation/Rakefile create vendor/plugins/lanugage_negotiation/init.rb create vendor/plugins/lanugage_negotiation/install.rb create vendor/plugins/lanugage_negotiation/uninstall.rb create vendor/plugins/lanugage_negotiation/lib/lanugage_negotiation.rb create vendor/plugins/lanugage_negotiation/tasks/lanugage_negotiation_tasks.rake create vendor/plugins/lanugage_negotiation/test/lanugage_negotiation_test.rb create vendor/plugins/lanugage_negotiation/test/test_helper.rb $ echo 'require "lanugage_negotiation"' >> vendor/plugins/lanugage_negotiation/init.rb そして、config/environment.rb の内容をごそっと vendor/plugins/lanugage_negotiation/lib/lanugage_negotiation.rb へ移動。 動作チェック LANG:console $ wgetc http://localhost/negotiation/test English: Test OK $ wgetc http://localhost/negotiation/test.ja 日本語: テスト ばっちり $ wgetc http://localhost/negotiation/test 日本語: テスト ばっちり うーん、簡単すぎる。 恐らく今回の場合、以下のファイルは変更の必要は無い? vendor/plugins/lanugage_negotiation/Rakefile vendor/plugins/lanugage_negotiation/install.rb vendor/plugins/lanugage_negotiation/uninstall.rb vendor/plugins/lanugage_negotiation/tasks/lanugage_negotiation_tasks.rake 残るはドキュメントとテストケースだ。 * テストケース [#z90f29b8] プラグインのテストケースを書かなければ。 テストケースとして書けるのかどうか自信はないのだけれど、 存在しない言語テンプレートがあった場合や、 言語に依存しないテンプレートがある場合にどうなるか、 ちゃんと試してみる必要がありそう。 ** テストの動かし方 [#u8d040b6] まずいきなりテストしてみる。 LANG:console $ cd vendor/plugins/lanugage_negotiation/ $ rake (in (rails)/negotiation/vendor/plugins/lanugage_negotiation) /usr/bin/ruby1.8 -I"(rails)/negotiation/vendor/plugins/lanugage_negotiation/lib" \ -I"(rails)/negotiation/vendor/plugins/lanugage_negotiation/lib" \ -I"(rails)/negotiation/vendor/plugins/lanugage_negotiation/test" \ "/usr/lib/ruby/1.8/rake/rake_test_loader.rb" "test/lanugage_negotiation_test.rb" /usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- active_support (LoadError) from /usr/lib/ruby/1.8/rubygems/custom_require.rb:31:in `require' from (rails)/negotiation/vendor/plugins/lanugage_negotiation/test/test_helper.rb:2 from ./test/lanugage_negotiation_test.rb:1:in `require' from ./test/lanugage_negotiation_test.rb:1 from /usr/lib/ruby/1.8/rake/rake_test_loader.rb:5:in `load' from /usr/lib/ruby/1.8/rake/rake_test_loader.rb:5 from /usr/lib/ruby/1.8/rake/rake_test_loader.rb:5:in `each' from /usr/lib/ruby/1.8/rake/rake_test_loader.rb:5 rake aborted! Command failed with status (1): [/usr/bin/ruby1.8 -I"(rails)/...] (See full trace by running task with --trace) $ cat test/test_helper.rb require 'rubygems' require 'active_support' require 'active_support/test_case' パスが通っていないらしい。 確かに通ってないんだけど、いくら何でも通ってなさすぎ。~ どうしてこんな事に? ああ、この rake をいきなり起動しちゃだめなのね。 アプリケーションのホームから、 LANG:console $ cd ../../.. $ pwd (rails)/negotiation $ rake test:plugins (in (rails)/negotiation) /usr/bin/ruby1.8 -I"(rails)/negotiation/lib" -I"(rails)/negotiation/test" \ "/usr/lib/ruby/1.8/rake/rake_test_loader.rb" "vendor/plugins/lanugage_negotiation/test/lanugage_negotiation_test.rb" Loaded suite /usr/lib/ruby/1.8/rake/rake_test_loader Started . Finished in 0.035457 seconds. 1 tests, 1 assertions, 0 failures, 0 errors うまく行ってる。 ** プラグインのテストの作成 [#n11b0fa7] あとは vendor/plugins/lanugage_negotiation/test/lanugage_negotiation_test.rb に テストケースをもりもり書いていけばいい。 テストの書き方は~ LANG:ruby(linenumber) test "what to test" do assert true assert_equal 1, 1 end という感じ。 詳しい書式: http://www.ruby-lang.org/ja/man/html/Test_Unit.html LANG:ruby(linenumber) test "ActionController_AbstractRequest_acceptable_language?" do ar = ActionController::AbstractRequest ar.class_eval <<-EOS def self.acceptable_languages=(v) @acceptable_languages = v end EOS ar.acceptable_languages= :ja, :en, :fr assert ar.acceptable_language? :ja assert ar.acceptable_language? :en assert ar.acceptable_language? :fr assert ! ar.acceptable_language?(:de) assert ! ar.acceptable_language?(:es) assert ar.acceptable_language? "fr" assert ! ar.acceptable_language?("de") end 問題なし。 LANG:ruby(linenumber) test "ActionController_AbstractRequest_accepts_languages!" do ar= ActionController::AbstractRequest ar.class_eval <<-EOS def self.acceptable_languages=(v) @acceptable_languages = v end attr :cookies, true attr :env, true EOS rec = ar.new ar.acceptable_languages= :ja, :en, :fr rec.cookies = {} rec.env = {} # server default assert_equal [ :ja, :en, :fr ], rec.accepts_languages! # ignore invalid specifications rec.cookies = { 'rails_language' => ['xx'] } rec.env = { 'HTTP_ACCEPT_LANGUAGE' => 'es, de' } assert_equal [ :ja, :en, :fr ], rec.accepts_languages! # Accept-Language rec.env = { 'HTTP_ACCEPT_LANGUAGE' => 'en-us, fr, en, ja' } assert_equal [ :en, :fr, :ja ], rec.accepts_languages! rec.env = { 'HTTP_ACCEPT_LANGUAGE' => 'en-us;q=0.8, fr;q=0.1, en, ja' } assert_equal [ :en, :fr, :ja ], rec.accepts_languages! # cookie rec.cookies = { 'rails_language' => ['ja'] } assert_equal [ :ja, :en, :fr ], rec.accepts_languages! rec.cookies = { 'rails_language' => ['fr'] } assert_equal [ :fr, :en, :ja ], rec.accepts_languages! rec.env = {} assert_equal [ :fr, :ja, :en ], rec.accepts_languages! # arg assert_equal [ :ja, :fr, :en ], rec.accepts_languages!('ja') assert_equal [ :en, :fr, :ja ], rec.accepts_languages!('en') assert_equal [ :fr, :ja, :en ], rec.accepts_languages!('fr') end テストに引っかかった。 LANG:ruby class ActionController::AbstractRequest.accepts_languages!(language=nil) ... lsym= l.split(/;|\-/,2)[0].downcase.to_sym の部分、 LANG:ruby lsym= l.split(/;|\-/,2)[0].strip.downcase.to_sym とする必要有り。 LANG:ruby(linenumber) class MemoizedTestClass extend ActiveSupport::Memoizable def count @count end def calc(v) @count= ( @count || 0 ) + 1 v end memoize :calc end test "memoize" do mt= MemoizedTestClass.new mt.calc(1) mt.calc(2) mt.calc(1) mt.calc(3) mt.calc(1) assert_equal 3, mt.count mt.calc(1) mt.calc(2) mt.calc(3) assert_equal 3, mt.count mt.calc(4) assert_equal 4, mt.count assert MemoizedTestClass.memoized?(:calc) MemoizedTestClass.unmemoize :calc assert !MemoizedTestClass.memoized?(:calc) assert_equal 4, mt.count mt.calc(1) mt.calc(2) mt.calc(3) mt.calc(4) assert_equal 8, mt.count MemoizedTestClass.memoize :calc mt.calc(1) mt.calc(2) mt.calc(3) assert_equal 8, mt.count end unmemoize 後にもう一度 memoize した場合には、 以前の計算結果が残ってしまうことに注意。 LANG:ruby(linenumber) module ActiveSupport::Memoizable def unmemoize(*symbols) class_eval <<-EOS, __FILE__, __LINE__ #{memoized_ivar}= nil などと書いてたけれど、呼び出されるコンテキストが 異なるので変数のクリアはできない。 この行は削除した。 LANG:console $ rake test:plugins /usr/bin/ruby1.8 -I"(rails)/negotiation/lib" -I"(rails)/negotiation/test" "/usr/lib/ruby/1.8/rake/rake_test_loader.rb" "vendor/plugins/lanugage_negotiation/test/lanugage_negotiation_test.rb" Loaded suite /usr/lib/ruby/1.8/rake/rake_test_loader Started ... Finished in 0.038165 seconds. 3 tests, 25 assertions, 0 failures, 0 errors テスト成功。 他にももう少し plugin test でちまちま書いてく事もできるんだけど、 controller の test と合わせていっきにやってしまった方が楽そう? ** negotiation アプリケーションのテストケース [#p80291d3] コントローラ込みのファンクショナルテストについては、 プラグインのテストケースに入れるわけに行かないので、 negotiation アプリケーションの test コントローラのテストケースとして書く事になる。 まずテストに必要なアクションとビューを追加。 LANG:console $ jed app/controllers/test_controller.rb class TestController < ApplicationController caches_page :pagecached caches_action :actioncached def index end def pagecached end def actioncached end def fragmentcached end def render_action render :action => :acceptid end def render_file render :file => "test/acceptid.html" end def acceptid end end $ echo "Test <%= render :partial=>'ok' %>" > app/views/test/index.html.en.erb $ echo "テスト <%= render :partial=>'ok' %>" > app/views/test/index.html.ja.erb $ echo "Page cached" > app/views/test/pagecached.html.en.erb $ echo "ページキャッシュ" > app/views/test/pagecached.html.ja.erb $ echo "Action cached" > app/views/test/actioncached.html.en.erb $ echo "アクションキャッシュ" > app/views/test/actioncached.html.ja.erb $ echo "<% cache :param1=>:test do %>Fragment cached <%= render :partial=>'ok' %><% end %>" > app/views/test/fragmentcached.html.en.erb $ echo "<% cache do %>フラグメントキャッシュ <%= render :partial=>'ok' %><% end %>" > app/views/test/fragmentcached.html.ja.erb $ echo "File" > app/views/test/renderfile_.html.en.erb $ echo "ファイル" > app/views/test/renderfile_.html.ja.erb $ echo "html <%= params[:id] %>" > app/views/test/acceptid.html.ja.erb $ echo "text <%= params[:id] %>" > app/views/test/acceptid.text.ja.erb $ echo "English <%= @content_for_layout %>" > app/views/layouts/application.text.en.erb テストは test/functional/test_controller_test.rb に書き込む。 書き方は http://d.hatena.ne.jp/elm200/20070724/1185282738 が参考になる。 LANG:ruby(linenumber) def setup @ar = ActionController::AbstractRequest @controller = TestController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new end としておくと、 LANG:ruby(linenumber) test "server default1" do @ar.acceptable_languages = :ja, :en, :fr @request.cookies = {} @request.env = {} get :index, :format=>"html" assert_response :success assert_template 'index.html.ja' assert_equal "/test", @request.request_uri end のようなコードを書く事ができる。 キャッシュをテストするために、~ config/environments/test.rb LANG:ruby(linenumber) config.action_controller.perform_caching = true さらに、 LANG:ruby(linenumber) def assert_cached_page_exists( page ) full_path= ActionController::Base.__send__(:page_cache_path, page) assert File.exist?(full_path), "Cached file '#{full_path}' not created." end def assert_cached_page( page ) ActionController::Base.class_eval <<-EOS @@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : "" @@page_cache_directory += "/cahce_in_test_environment" EOS @controller.expire_page page yield assert_cached_page_exists page end として、development や production 環境にキャッシュを作成してしまわないようにする。 LANG:ruby(linenumber) test "page cache 1" do @ar.acceptable_languages = :fr, :ja, :en @request.cookies = {} @request.env = { } page = "/test/pagecached.html.fr" assert_cached_page( page ) do get :pagecached, :format=>"html" assert_response :success assert_template 'pagecached.html.ja' assert_equal "/test/pagecached", @request.request_uri assert_layout "layouts/application.html.ja" end end のようにテストする事ができる。 その他作ったヘルパー関数達 LANG:ruby(linenumber) def assert_content_type(type) flunk "Content-Type '#{type}' was expected but "+ "'#{@response.content_type}' found." unless @response.content_type == type end def assert_cached_fragment_exists(key, message="Fragment cache ['#{key}'] not exists.") flunk message unless @controller.fragment_exist? key end def assert_cached_fragment(key) @controller.expire_fragment key yield assert_cached_fragment_exists key end def assert_layout(v) assert_equal v, @response.template.send( :_pick_template, @response.layout).path_without_extension end def log(s) if s.is_a? String Rails.logger.warn s else Rails.logger.warn s.inspect end end テストはかなりたくさん書いたので、ここに全部は載せない。~ 一部を取り出すと、 LANG:ruby(linenumber) test "page cache 3" do @ar.acceptable_languages = :fr, :ja, :en @request.cookies = {} @request.env = { 'HTTP_ACCEPT_LANGUAGE' => 'xx' } page = "/test/pagecached.html.fr" assert_cached_page( page ) do get :pagecached, :format=>"html" assert_response :success assert_template 'pagecached.html.ja' assert_equal "/test/pagecached", @request.request_uri assert_layout "layouts/application.html.ja" assert_content_type "text/html" end end test "page cache 4" do @ar.acceptable_languages = :fr, :ja, :en @request.cookies = {} @request.env = { 'HTTP_ACCEPT_LANGUAGE' => 'en' } page = "/test/pagecached.html.en" assert_cached_page( page ) do get :pagecached, :format=>"html", :extra=>:param assert_response :success assert_template 'pagecached.html.en' assert_equal "/test/pagecached?extra=param", @request.request_uri assert_layout "layouts/application.html.en" assert_content_type "text/html" end end test "action cache 1" do @ar.acceptable_languages = :fr, :ja, :en @request.cookies = {} key = "test.host/test/actioncached?rails_language=fr" assert_cached_fragment( key ) do get :actioncached, :format=>"html" assert_response :success assert_template 'actioncached.html.ja' assert_equal "/test/actioncached", @request.request_uri assert_layout "layouts/application.html.ja" assert_content_type "text/html" end end test "action cache 2" do @ar.acceptable_languages = :fr, :ja, :en @request.cookies = {} key = "test.host/test/actioncached.ja" assert_cached_fragment( key ) do get :actioncached, :format=>"html", :rails_language=>"ja" assert_response :success assert_template 'actioncached.html.ja' assert_equal "/test/actioncached.ja", @request.request_uri assert_layout "layouts/application.html.ja" assert_content_type "text/html" end end こんな感じ。 実際にこれを走らせる。 LANG:console $ rake test:functionals rake aborted! no such file to load -- (rails)/negotiation/db/schema.rb (See full trace by running task with --trace) $ rake db:schema:dump $ rake test:functionals /usr/bin/ruby1.8 -I"(rails)/negotiation/lib" -I"(rails)/negotiation/test" "/usr/lib/ruby/1.8/rake/rake_test_loader.rb" "test/functional/test_controller_test.rb" Loaded suite /usr/lib/ruby/1.8/rake/rake_test_loader Started .......................... Finished in 0.358801 seconds. 26 tests, 96 assertions, 0 failures, 0 errors 実行は rake test:functionals で良い。~ schema.rb が無いと言われたので、rake db:schema:dump で作成した。 ミスして入力したテストで気づいた点。 LANG:ruby(linenumber) module ActionView class Template def split(file) elsif ActionController::AbstractRequest.acceptable_language?(m[3]) の部分などで、m[3] が nil になった場合、 LANG:ruby(linenumber) class ActionController::AbstractRequest def self.acceptable_language?(l) acceptable_languages.include?(l.to_sym) end end の l.to_sym でこける。 LANG:ruby(linenumber) def self.acceptable_language?(l) l.respond_to?(:to_sym) && acceptable_languages.include?(l.to_sym) end とすべき。 また、routes.rb では LANG:ruby(linenumber) map.connect ':controller/:action/:id', :format => 'html' を始めの方に書いておかないと、 LANG:ruby(linenumber) url_for(:controller=>:test, :action=>:acceptid, :index=>1, :format=>"html") が LANG:ruby(linenumber) map.connect ':controller/:action/:id.:rails_language', :format => 'html', :requirements => { :rails_language => lang_regexp } を使って変換されてしまい、 /test/acceptid/1. となるので、順番を入れ替える。 * fcgi 化 [#wd595311] ちょっとオフトピックだけれど、はまったので。 fcgi 化するには基本的には RewriteRule で dispatch.cgi となっていたところを dispatch.fcgi にするだけでよい。 LANG:console $ jed public/.htaccess RewriteRule ^(.*)$ dispatch.fcgi [QSA,L] $ wgetc --header="Accept-Language: en" http://localhost/negotiation/test エラー: ActionController::RoutingError (no route found to match to "/negotiation/test") でも、なんでかうまくいかない。~ .htaccess の SetEnv RAILS_RELATIVE_URL_ROOT /negotiation の効果が得られていないみたい。 vendor/actionpack/CHANGELOG *2.2 (November 21st, 2008)* * AbstractRequest.relative_url_root is no longer automatically configured by a HTTP header. It can now be set in your configuration environment with config.action_controller.relative_url_root [Josh Peek] どうやらこれらしい。~ なんで fcgi の時だけだめなんだろう? changelog に書かれているのだからバグという事でもないのだろうし、 コードを追うのはやめにして、素直に environments/production.rb に 設定を追加したところ、動き出した。 さすが、cgi に比べると段違いに速い。 LANG:console $ echo 'config.action_controller.relative_url_root = "/negotiation"' >> environments/production.rb $ wgetc --header="Accept-Language: en" http://localhost/negotiation/test 日本語: テスト ばっちり $ rm tmp/wget.cookie $ wgetc --header="Accept-Language: en" http://localhost/negotiation/test English: Test OK $ wgetc --header="Accept-Language: en" http://localhost/negotiation/test.ja 日本語: テスト ばっちり $ wgetc --header="Accept-Language: en" http://localhost/negotiation/test 日本語: テスト ばっちり $ wgetc http://localhost/negotiation/test/pagecached 日本語: ページキャッシュ $ ls public/test/* public/test/pagecached.html.ja $ wgetc http://localhost/negotiation/test/pagecached.en English: Page cached $ ls public/test/* public/test/pagecached.html.en public/test/pagecached.html.ja
Counter: 5092 (from 2010/06/03),
today: 3,
yesterday: 0