2011年8月23日火曜日

capybara-webkitを動かす 2011/08/23時点版

■概要

こちらの素晴らしい記事では、capybara-webkitを利用して、headlessでjsが動く環境を紹介されています。

capybara-webkitのこれまでリリースされているバージョン(~0.5.0)では、capybara1.0.0系への依存が解決できない為、抗う方法をご紹介されています。が、0.6.0からは上手く依存関係を解決されるようになりました!

■手順

こちらと同様ですが、Xvfbとqtの当たらしいバージョン(qt47)をインストールしておきます。
qt47にする理由はphantomjsも動かしたいからです ^o^

$ sudo yum -y install firefox ★ seleniumで動かす必要があれば
$ sudo yum -y install xorg-x11-server-Xvfb xorg-x11-fonts*
$ sudo vim /etc/yum.repos.d/atrpms.repo

[atrpms]
name= CentOS-$releasever - ATrpms
baseurl=http://dl.atrpms.net/el$releasever-$basearch/atrpms/testing/
gpgcheck=1
gpgkey=http://ATrpms.net/RPM-GPG-KEY.atrpms
enabled=0

$ sudo rpm --import http://packages.atrpms.net/RPM-GPG-KEY.atrpms
$ sudo yum -y install sqlite --enablerepo=atrpms ★依存関係からインストールする必要がある?
$ sudo yum -y install qt47-devel qt47-webkit qt47-webkit-devel --enablerepo=atrpms
$ sudo ln -s /usr/bin/qmake-qt47 /usr/bin/qmake (コンパイル時に必要とされるので予め作成)

でrailsプロジェクト側で

$ vim Gemfile
group :development, :test do
  ...
  gem 'capybara', '1.0.1'
  gem 'capybara-webkit', '0.6.0'
  gem 'headless', '0.1.0'
  ...
end
$ bundle install
$ vim spec/spec_helper.rb
...
require "capybara/rails"
require "capybara/rspec"
...
RSpec.configure do |config|
...
end

Capybara.javascript_driver = :webkit
$ vim spec/support/headless.rb
if %w(yes y on).include?(ENV['HEADLESS'])
  require 'headless'

  headless = Headless.new
  headless.start

  at_exit do
    headless.destroy
  end
end
$ vim spec/requests/index_spec.rb
# coding: utf-8

require 'spec_helper'

describe "Index" do
  describe "GET /" do
    it "/index.html", :js => true do
      visit "/"
      click_link "About your application’s environment"
      page.should have_content("No route matches")
    end
  end
end

$ HEADLESS=on bundle exec rspec spec/requests/index_spec.rb
Index
  GET /
    /index.html

Finished in 4.48 seconds
1 example, 0 failures
という訳でいい感じに実行できました~

2011年8月20日土曜日

javaの32bitと64bitの使い分けにはご注意を

最近では64bit OS環境が広く利用されている事と思います。
一般的に64bit環境での32bitコマンドの実行は問題が無いと思いますが、java関係で二つ程問題が発生したので記録しておきます。

■Tomcatが起動出来なくなった。

64bit => 32bit javaに変更した所、Tomcatが起動しなくなりました。調べてみるとTomcat本体の問題では無かったのですが、jsvcを利用して起動している為、問題が発生しているようです(TOMCAT_HOME/bin/startup.sh等では問題なく起動しました)。

一部推測混じりですが

・jsvcを利用するにはコンパイルが必要
・コンパイル時には、configureを実行
・configureのログをよく見てみると、linux自体を"x86_64"と判定
・よってjavaは64bit版を想定してコンパイルしてしまう(推測)

という理由で上手く行かなくなったのではと思われます。

結論:jsvcを利用する場合は、32/64bitをOSとあわせる必要があります。

■rjb(ruby java bridge)が動かなくなった。

こちらも64bit => 32bit javaに変更した所、動かなくなりました。ruby側からJava VMを作成できなくなるようです。

但し上記問題を解決しても、javaのマイナーバージョン違いで急に動かなくなる場合を確認しています(jdk6u22 => jdk6u23)。ログを見た限りではGC発生タイミングでseg faultしてしまうようです。

リリースノートを見ている限りでは、hotspot vmのバージョンが上がったから?等推測してますが根本原因は判明しませんでした。

結論:rjbを利用する場合は、32/64bitをOSとあわせる必要があります。またマイナーバージョン含め実績のあるjava/rubyのバージョン組み合わせ・設定を大事にする必要があります。

2011年8月16日火曜日

(小ネタ)railsのセッションを復元する(rails2.3系で確認)

必要に迫られたので... メモ残しておきます。

$ irb
> require 'rubygems'
> require 'active_support'
> v = ActiveSupport::MessageVerifier.new('Railsのsecret', 'SHA1')
> v.verify('session文字列')

※1 Railsのsecretは、initializer/session_store.rb
※2 session文字列は、cookieから取得

ちなみに得られる結果はrack.sessionと同じです。

2011年8月13日土曜日

ruby1.9時代にrcovは使ってはいけない。simplecovを使おう!

■概要

rubyにおけるテスト網羅率の定番ツールといえばrcovですが、どうもテストの通っている箇所の色付けがおかしいのと網羅率に誤差があると感じてました。

よくよくgithubのrcovのページを見てみると、

NOTE: This fork does not work on Ruby 1.9.x. For coverage on Ruby 1.9 look at SimpleCov. Even if you get results on 1.9 they will probably be inaccurate. Ruby 1.9 has call detection built in for faster, more accurate results.

なんて書いてあります。という訳でSimpleCovを試してみました。

なおこちらで簡単にセットアップできるシェルスクリプト置いてます!

■説明

まずはいつものGemfileに

gem 'simplecov', :require => false

と記載して

$ bundle install

次にspec/spec_helper.rbに下記コードを追加しましょう。
この時大事なのは実行の早い段階(railsが読み込まれる前!)で下記を実行する必要がある事です。

require 'simplecov'
SimpleCov.start 'rails'



$ rake spec

すると、coverageディレクトリ以下にhtmlファイルが出力されます(画像はsimplecovのページより)


このままでも良いのですが、rcovのフォーマットと異なるので、CIに統合するには不便です。そんな時はsimplecov_rcovを利用すると上手く行きます。

group :test do
  gem 'simplecov', :require => false
  gem 'simplecov-rcov', :require => false
end

$ bundle install

spec/spec_helper.rbには、SimpleCovの出力フォーマットを変更するコードを書きます。

require 'simplecov'
require 'simplecov-rcov'
SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter

※くれぐれも早い段階で読み込んで下さい!

すると見慣れたあのHTMLがcoverage/rcov以下に出力されます。
coverage/rcovをCI(というかjenkins)のrcovレポートの場所として指定してあげると良好な結果が得られます!

ちなみにsimplecov-rcovのページで下記の様な記載があり、網羅率測定有無を切り替える方法があります。
if( ENV['COVERAGE'] == 'on' )
  require 'simplecov'
  require 'simplecov-rcov'
  SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
  ...
end

$ COVERAGE=on rake spec
また複数の出力フォーマットで出力する例もありますので是非見てみて下さい。

2011年8月12日金曜日

rails3とnginxでx-sendfile的な事をしてみる

■概要

rails3.0.9・nginxの組み合わせで、apacheでのX-Sendfile相当の事が実現したく検証しましたが、これまた少しはまりました。

■答えまでの道のり

config/environments/production.rb を見ると

# Specifies the header that your server uses for sending files
  config.action_dispatch.x_sendfile_header = "X-Sendfile"

  # For nginx:
  # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'

という記載があるので、上をコメントアウトし、下をコメント外せば、OKと思ったのですが甘かったです。。上手く行かずエラーが出ます。

nginxのドキュメントを見ても、internalに対応する内部向けurlが、必要なのは分かりますが、いまいち良くわかりません。

ではrailsのsend_fileメソッド(actionpack-3.0.x/lib/action_controller/metal/streaming.rb)のソースを確認しても、pathには絶対パスを渡さないと上手く行かない事が予想されます(冒頭でファイルシステムとしての存在チェックをしているので)。しかしよく見ると

# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
# via the Rack::Sendfile middleware. The header to use is set via

と書いてあるので、次はRack::Sendfile(rack-1.2.x/lib/rack/sendfile.rb)を見てみました。コメントには下記の用にnginx用のサンプルがあります。

  #   location ~ /files/(.*) {
  #     internal;
  #     alias /var/www/$1;
  #   }
  #
  #   location / {
  #     proxy_redirect     off;
  #
  #     proxy_set_header   Host                $host;
  #     proxy_set_header   X-Real-IP           $remote_addr;
  #     proxy_set_header   X-Forwarded-For     $proxy_add_x_forwarded_for;
  #
  #     proxy_set_header   X-Sendfile-Type     X-Accel-Redirect;  # ①
  #     proxy_set_header   X-Accel-Mapping     /files/=/var/www/; # ②
  #
  #     proxy_pass         http://127.0.0.1:8080/;
  #   }

アプリケーションの通常処理を行うlocation "/" と、ファイル転送用のlocation "~ /files(.*)"があり/var/wwwがマッピングされるのかな?と思い、真似してみましたが上手くいきません。。

もう少しRackのソースを読んでみると、上記設定の意味が良くわかります。①はvariationメソッドの戻り値になります。したがってcall内の分岐は"X-Accel-Redirect"に進みます。その後map_accel_pathメソッドを実行するのですが、ここで驚愕の事実に気付きます。

def map_accel_path(env, file)
    if mapping = env['HTTP_X_ACCEL_MAPPING']
      internal, external = mapping.split('=', 2).map{ |p| p.strip }
      file.sub(/^#{internal}/i, external)
    end
  end

②は絶対パスを、nginx用内部urlに変換する為に必要な設定ですが「右辺と左辺をドキュメントでは逆に書いている!」という事に気付きました。(internalとexternal)

よって②は左右逆転にしなければいけません。

proxy_set_header X-Accel-Mapping /var/www/=/files/;

ここまでやって上手くいきました。
/var/www/hoge.txt (send_file) => /files/hoge.txt (rackから出た時点) => /var/www/hoge.txt (nginxでの処理)

■設定方法(まとめ)

・config/environments/production.rb

config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'

のコメントをはずし、一つ上のX-Sendfileはコメントアウトします。

・プログラム

ファイル送信部分は絶対パス指定します。

send_file "/var/www/baz.txt"

・/etc/nginx/nginx.conf

下記の様に、通常処理側には、X-Sendfile-Type・X-Accel-Mapping、内部処理向けには、internal・alias設定を記載すればOKです。

location / {
  ...
  proxy_set_header X-Sendfile-Type X-Accel-Redirect;
  proxy_set_header X-Accel-Mapping /var/www/=/files/;
  ...
}

location /files/ {
  internal;
  alias /var/www;
}

rails3でのsub ディレクトリへのデプロイ

■概要

rails3.0.9・unicorn・nginxの組み合わせで、subディレクトリにアプリケーションをデプロイしようとすると、少しはまったのでメモを残しておきます。

■設定方法

http://ホスト名/foo 以下にアプリをデプロイしたい場合...

○unicorn

unicorn_railsを実行する際に--pathを渡します。

例)
$ unicorn_rails -c config/unicorn.rb -E production -D --path /foo

○nginx

passengerと同じくDocument Root直下に、subディレクトリ名でpublicへのシンボリックリンクをはればOKです。またlocation設定はsubディレクトリ毎に設定するのが良い感じでしょうね。

例)
$ ln -s /path/to/foo_root/public /var/apps/foo
$ vim /etc/nginx/nginx.conf
...
root /var/apps/;
...
location /foo {
  if (-f $request_filename) { break; }
  proxy_set_header X-Real-IP  $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $http_host;
  proxy_pass http://(foo用のunixドメインのソケット);
}

○rails3.0.9

ActionController::Base.config.relative_url_root= が、deprecatedなのでどうしようかと思いますが検証した結果、下記で上手く行きました。
通常のroutingもassetもこれで上手くいきます(config.serve_static_assets = false なので、assetsはnginxが処理します)。

・unicon_rails --pathの設定は、ENV['RAILS_RELATIVE_URL_ROOT']となってrack側に渡される
・ENV['RAILS_RELATIVE_URL_ROOT']は、ActionController::Base.config.relative_url_rootに代入されている(?)
・よってconfig.ruで下記のようにrunを囲む
map ActionController::Base.config.relative_url_root || "/" do
  run FooApp::Application
end
参考:困った時のstackoverflow http://stackoverflow.com/questions/3181746/what-is-the-replacement-for-actioncontrollerbase-relative-url-root