2012年3月29日 #ruby #rails #rspec #postgresql

PostgresSQLでrake specするとNOTICEがいっぱいでるが大変参考になった。

NOTICE: CREATE TABLE will create implicit sequence "peroperos_id_seq" for serial column "peroperos.id"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "peroperos_pkey" for table "peroperos"
~略~

の警告をシャットダウンする方法です。

# add min_messages to test env
test:
adapter: postgresql
encoding: unicode
database: homu_test
pool: 5
username: postgres
password:
min_messages: warning
view raw database.yml hosted with ❤ by GitHub
2012年3月23日 #ruby #rails #troubleshooting #rack

Rails 3.1以前のバージョンなら多分テスト実行時にこういう警告が出ることがあるかもしれません。

gems/rack-1.2.5/lib/rack/utils.rb:16: warning: regexp match /.../n against to UTF-8 string

このうるさい警告を消す方法を紹介します。

解決方法

rspecを使う場合はspec/spec_helper.rbに下記コードを最後に追記してください。

RSpec.configure do |config|
# ...
end
# quick hack to get Rack to be quiet about "warning: regexp match /.../n against to UTF-8 string" until we upgrade to Rails 3.1 with Rack 1.3
module Rack
module Utils
def escape(s)
CGI.escape(s.to_s)
end
def unescape(s)
CGI.unescape(s)
end
end
end
view raw spec_helper.rb hosted with ❤ by GitHub

原因

Rack1.3からは解決できたらしいですが、Rails 3.1以前を使うならとりあえずこの方法で回避してもいいかと。
詳しくは:https://github.com/rack/rack/issues/41

2012年1月26日 #ruby #rails #factory_girl #test

FactoryGirlで既に定義した値を使おうとする時の方法です。
例えばLessonというクラスのstart_atとend_atの属性があり、start_atを定義した後はend_atをその2時間後に定義したい場合、ブロックでパラメータを渡せばそこでstart_atにアクセスできるようになります。

# Say you have two columns: start_at, end_at
# When start_at is defined, you want end_at's value based on the start_at
# Here is the solution, passing a block to get the object of the context.
start_at Random.new.rand(14..45).days.from_now
end_at { |l| l.start_at + 2.hours }
# Full example
FactoryGirl.define do
factory :lesson do
sequence(:title) { |n| "lesson title#{n}" }
sequence(:description) { |n| "lesson description#{n}" }
capacity 6
start_at Random.new.rand(14..45).days.from_now
end_at { |l| l.start_at + 2.hours }
price [5000, 5100, 5200, 5300, 5500, 6000].sample
end
end
view raw gistfile1.rb hosted with ❤ by GitHub
2011年11月 9日 #ruby #rails #asset

ActionView/Helpers/AssetTagHelper、asset(画像、CSSなど)ホストのチューニングについての勉強メモです。

asset hostを指定

image_tag("rails.png")のhelper methodで生成するリンクはデフォルトでは同じホストのpublicフォルダを指しています。それを変更したい場合はconfig/environments/production.rbActionController::Base.asset_hostをいじります。

ActionController::Base.asset_host = "assets.example.com"
image_tag("rails.png")
# => <img alt="Rails" src="http://assets.example.com/images/rails.png?1230601161" />

asset hostを複数指定

ブラウザが一度に同一サーバには2つのコネクションしかできないそう(これは初めて知りました)で、asset同士のダウンロード完了するのを待たなければなりません。もし複数台のassetサーバがある場合はassets%d.example.comを使ってそれをコネクション数を増やせることができます。%dが指定されればRailsは0~3の4つの番号を付けて、並行して8つのコネクションができます。

ActionController::Base.asset_host = "assets%d.example.com"
image_tag("rails.png")
# => <img alt="Rails" src="http://assets.example.com/images/rails.png?1230601161" />
stylesheet_link_tag("application")
# => <link href="http://assets2.example.com/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />

カスタマイズ

もっと自分でカスタマイズしたい場合はsourceのprocパラメータを使えます。

ActionController::Base.asset_host = Proc.new { |source|
  "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com"
}
image_tag("rails.png")
# => <img alt="Rails" src="http://assets1.example.com/images/rails.png?1230601161" />
stylesheet_link_tag("application")
# => <link href="http://assets2.example.com/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />

特定のパスで始まるassetを特定のホストに指定する例:

ActionController::Base.asset_host = Proc.new { |source|
   if source.starts_with?('/images')
     "http://images.example.com"
   else
     "http://assets.example.com"
   end
 }
image_tag("rails.png")
# => <img alt="Rails" src="http://images.example.com/images/rails.png?1230601161" />
stylesheet_link_tag("application")
# => <link href="http://assets.example.com/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />

さらにrequestの第二パラメータもあります。これでHTTPSの動作も制御できます。

ActionController::Base.asset_host = Proc.new { |source, request|
  if request.ssl?
    "#{request.protocol}#{request.host_with_port}"
  else
    "#{request.protocol}assets.example.com"
  end
}

Resources

http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html

2011年11月 7日 #ruby #rails #actionview #partial

例えば省略可能なフラグみたいなローカル変数をpartial viewに渡した時、そのデフォルト動作をpartial viewでハンドリングしたいですね。

調べてみたらdefined?local_assigns.has_key?が見つかりましたが、前者のほうはオススメできなさそうです。

Testing using defined? headline will not work. This is an implementation restriction.
http://api.rubyonrails.org/classes/ActionView/Base.html

実際やってみて、defined?でも動けるパターンはありますが、if/unless文で一行にしたらバグりました。なのでやはり* local_assigns.has_key?*を使いましょう。

Resources

http://stackoverflow.com/questions/238615/defined-method-in-ruby-and-rails
http://api.rubyonrails.org/classes/ActionView/Base.html

2011年11月 6日 #ruby #rails #rspec #capybara #webkit

Goal

ゴールと言うか目標はrspecのrequest(integration) testでjavascriptのテストをしたいです。が、いろいろハマってその問題と解決策を時間軸でメモしておきます。

Capybaraについてはある程度知ってる前提です。

javascriptのテストを書く

:js => trueでrspecのintegration testでjavascriptを有効に設定できます。
例:

# in your view erb file
<%= link_to_function "test js", '$(this).html("js works")' %>
# in test spec
it "supports js", :js => true do
visit tasks_path
click_link "test js"
page.should have_content("js works")
end

ただデフォルトではjavascriptをサポートしないので、このspecは失敗します。解決の鍵はcapybara driverというものです。

capybara_webkit driver

なぜcapybara_webkit driver使う

Capybara ... currently comes with Rack::Test and Selenium support built in.

By default, Capybara uses the :rack_test driver, which is fast but does not support JavaScript. You can set up a different default driver for your features.

The capybara-webkit driver is for true headless testing. It uses QtWebKit to start a rendering engine process. It can execute JavaScript as well. It is significantly faster than drivers like Selenium since it does not load an entire browser.

のようにcapybaraは Rack::TestSeleniumのdriverがbuilt-inされて、デフォルトdriverは前者の:rack_testでjavascriptをサポートしないです。Seleniumはサポートしますが、それよりも速いのがcapybara-webkitなのでそれを採用しました。

capybara_webkit driverをインストール

Gemfileにcapybara_webkitを追記、bundle installします。webkitはQTが必要なので、もしそれがないとbundle installで失敗(エラー)します。後はspec_helper.rbにdefault driverをwebkitに設定します。

# webkit-dependency
# if you don't have homebrew installed
/usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)"
# if you don't have QT installed
brew install qt
# Gemfile, add capybara-webkit
group :test, :development do
...
gem "capybara-webkit"
end
# in Terminal
bundle install
# set default capybara driver to webkit.
# I put this into spec_helper.rb
Capybara.javascript_driver = :webkit
# In RSpec, use the :js => true flag
it "supports js", :js => true do
...
end

これで最初のサンプルspecは通るはずです。

データベースと絡んだテスト

上記のままだとデータベースと絡んだテストはまた失敗します。
例:

it "displays tasks", :js => true do
Task.create!(:name => "foo")
visit tasks_path
click_link 'some link'
page.should have_content("some text")
end

自分で確認してみたら、テスト時に作成したデータはjavascriptのテストで使えないっぽいです。Railscastsの記事によりますとspecのテストではデータベーストランザクションを使うが、それがselenium或いはwebkitでは使えないそうです。そのためにdatabase_cleanerというgemを使います。

database_cleanerを使う

# in Gemfiile
group :test, :development do
...
gem "database_cleaner"
end
# in Terminal
bundle install
# in spec_helper.rb
RSpec.configure do |config|
# ...
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end

config.use_transactional_fixturesをfalseにした時点でもうテスト自体は通るはずですが、データはずっとそのまま残ってしまうんです。database_cleanerは名の通りデータベースをテスト前後で綺麗な状態に保つことができます。

これでもう環境面の設定は完了です!

Resources

目的

OmniauthでTwitterに認証ログイン後、ログイン前と同じページにリダイレクトしたい

方法

libディレクトリのautoloadはRails libディレクトリのファイルをautoloadを参考してください。

解説

redirect_toパラメータ或いはrefererをログイン直前のリクエストから取ってセッションに保存するRack middwareの手法です。そしてsessions_controllerではセッション内に保存されたurlにリダイレクトし、セッションクリアしています。

もちろんTwitter以外のproviderでの認証&ログイン処理にも対応できます。

2011年10月21日 #ruby #sinatra

目的

Sinatraで静的なhtmlファイルをrenderする方法です。

現状

Sinatraでは色々なビューテンプレートをレンダリングできます。Haml、Erb、Sass、Markdown、CoffeeScript…が対応されていますが、HTMLは対応してないようです。html :indexで書いてもダメですね。

Sinatraのビューテンプレート

解決方法

File.readでファイルとして読み込むことですね。Sinatraはデフォルトの設定だとpublicフォルダ内のものをassetsとするそうです。

でちょっとリファクタリングしてhtml :indexのシンタックスでいけるようにしました。

参考

http://stackoverflow.com/questions/2437390/serving-static-files-with-sinatra