Kinopyo Blog

プログラマとしてRuby, Rails, iPhone, iPad,Macなどなどと向き合う日々のログポース

Posts Tagged ‘rails’

Rails Append class if condition is true in Haml

2011年11月22日
Append class if condition is true in Haml (with Rails) - Stack Overflow http://t.co/7pyL7pNn

HAMLである条件がtrueの時だけあるclassをタグに追加したい場合


.post{:class => ("gray" unless post.published?)}

Rails asset hostsについて

2011年11月09日

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

Gistのmarkdownはembedする時にうまくスタイルをレンダリングしてくれないようです。。。

orz….https://gist.github.com/1350279で見たほうがいいかも知れません。

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

```ruby
ActionController::Base.asset_host = "assets.example.com"
```

```erb
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つのコネクションができます。

```ruby
ActionController::Base.asset_host = "assets%d.example.com"
```

```erb
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パラメータを使えます。

```ruby
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を特定のホストに指定する例:
```ruby
ActionController::Base.asset_host = Proc.new { |source|
   if source.starts_with?('/images')
     "http://images.example.com"
   else
     "http://assets.example.com"
   end
 }
```

```erb
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の動作も制御できます。

```ruby
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
view raw gistfile1.md This Gist brought to you by GitHub.

Rails オプション(省略可)local variablesをpartial viewに渡す方法

2011年11月07日

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

<%= render 'article', :show_author => false %>
<%= render 'article' %>

調べてみたら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

# http://api.rubyonrails.org/classes/ActionView/Base.html

# If you need to find out whether a certain local variable
# has been assigned a value in a particular render call,
# you need to use the following pattern:

<% unless local_assigns.has_key? :show_author %>
  <% show_author = true %>
<% end %>

<% if show_author %>
  Author: <%= @article.author %>
<% end %>

# Testing using defined? headline will not work. This is an implementation restriction.

view raw solution.erb This Gist brought to you by GitHub.

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

# this will work
<% unless defined?(:show_author) %>
  <% show_author = true %>
<% end %>

# NOT work
<% show_author = true unless defined?(:show_author) %>

# this will work
<% show_author = true unless local_assigns.has_key? :show_author %>

Resources

http://stackoverflow.com/questions/238615/defined-method-in-ruby-and-rails

http://api.rubyonrails.org/classes/ActionView/Base.html

Rspec Capybara_webkitを使ってjavascriptをテストする

2011年11月06日

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.

https://github.com/jnicklas/capybara

のように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の記事によりますとrspecのテストではデータベーストランザクションを使うが、それが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

Rails OmniauthでTwitter認証ログイン後のcallback urlをカスタマイズ

2011年11月05日

目的

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

方法

# config/application.rb

config.middleware.use 'StoreRedirectTo'

# and make sure you autoload the contents of /lib


# lib/store_redirect_to.rb

class StoreRedirectTo
  def initialize(app)
    @app = app
  end

  def call(env)
    request = Rack::Request.new(env)
    path = request.path
    signin = path.match(%r(^/auth/[^/]+$)) && path != '/auth/failure'
    signout = path == '/users/signout'
    if signin || signout
      redirect = request.params['redirect_to'] || request.referer
      request.session['redirect_to'] = redirect if redirect
    end

    @app.call(env)
  end
end
# after successful sign-in or sign-out
redirect_to session[:redirect_to] || root_path
session[:redirect_to] = nil

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

解説

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

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

Rails libディレクトリのファイルをautoload

# application.rb

# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/extras)
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]

サブディスプレイのautoloadが必要ない場合は一行目だけでOK.

Railsでcustom log(file)の作り方

2011年10月12日

Overview

rails serverで起動する時に盛りだくさんの情報がログに出力されるので、別ファイルで自分がデバッグしたい情報だけをそこに出力する方法です。この方法でtail -f log/custom.logで監視できます

ソースコード

# lib/custom_logger.rb
class CustomLogger < Logger
  def format_message(severity, timestamp, progname, msg)
    "#{timestamp.to_formatted_s(:db)} #{severity} #{msg}\n"
  end
end

logfile = File.open("#{Rails.root}/log/custom.log", 'a') # create log file
logfile.sync = true # automatically flushes data to file
CUSTOM_LOGGER = CustomLogger.new(logfile) # constant accessible anywhere
# in development.rb
require "custom_logger"
# in controller files
CUSTOM_LOGGER.info("info from custom logger")
CUSTOM_LOGGER.debug("debug from custom logger")
CUSTOM_LOGGER.error("error from custom logger")

参考

http://robaldred.co.uk/2009/01/custom-log-files-for-your-ruby-on-rails-applications/

http://ianma.wordpress.com/2009/04/08/custom-logging-in-ruby-on-rails/

Rails3.1rcをHerokuで動かす

2011年06月17日

この前Herokuのドキュメントを見ましたが、まだRails3.0.7しか正式にサポートされているようです。でも実はRails 3.1rcもherokuで実行できますよ。

### in Terminal
# create rails app
rails new test_app

############ start: edit Gemfile ##############
#wrap sqlite3 in
group :test, :development do
gem 'sqlite3'
end

# add thin server
gem 'thin'

#add the rubyracer for heroku
group :production do
  gem 'therubyracer-heroku', '0.8.1.pre3'
  gem 'pg'
end

############ end: edit Gemfile ##############

### in Terminal
# bundle install
bundle

# git initialize
git init .

# add files
git commit -am "initial commit"

#create app on heroku
heroku create --stack cedar

# push app to heroku
git push heroku master

# test it
heroku open

view raw Gemfile This Gist brought to you by GitHub.

上記snippetはgistで保管しJavaScriptで表示しています。RSSでご覧の方は見れないと思います。お手数ですが、直接アクセスしてください。

Rails3.1.0 beta1のインストールと、undefined method ‘context’ for #Sprockets::Environment:のトラブル

2011年05月19日

Rails3.1.0 beta1をインストールするときにあったトラブルです。

インストール

// 3.1.0 betaに更新
sudo gem install rails --pre

// バージョン確認
rails -v

// ダミープロジェクト作成
rails new railsfoo

// 必要なGemをインストール
cd railsfoo
bundle install

// サーバ起動
rails s

トラブル

ここまで順調でしたが、http://127.0.0.1:3000にアクセスしたときにundefined method `context’ for #のトラブルに会いました。


どうやらこのSprocketsというのがポイントのようです。

ぐぐってみたらgithubでこのようなコメントがありました。

Just to save someone else the 2 minutes waiting for a bundle update sprockets that will fail, beta.2 is required by rails 3.1.0beta1. You want gem ‘sprockets’, ’2.0.0.beta.2′ in your Gemfile.

https://github.com/rails/rails/issues/453

のようでGemfileに下記を追記し再度bundle update。

gem 'sprockets', '2.0.0.beta.2'

解決〜

Error installing rails bundler requires RubyGems version >= 1.3.6

2011年03月07日

sudo gem install railsでこんなエラーが出ちゃいました。

Error installing rails bundler requires RubyGems version >= 1.3.6

解決策は

sudo gem update --system

pdating RubyGems
Updating rubygems-update
Successfully installed rubygems-update-1.6.1
Updating RubyGems to 1.6.1
Installing RubyGems 1.6.1
RubyGems 1.6.1 installed

=== 1.6.1 / 2011-03-03

Bug Fixes:

* Installation no longer fails when a dependency from a version that won't be
  installed is unsatisfied.
* README.rdoc now shows how to file tickets and get help.  Pull Request #40 by
  Aaron Patterson.
* Gem files are cached correctly again.  Patch #29051 by Mamoru Tasaka.
* Tests now pass with non-022 umask.  Patch #29050 by Mamoru Tasaka.

------------------------------------------------------------------------------

RubyGems installed the following executables:
	/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/gem

を実行した後にsudo gem install railsでrailsをインストールすればOKです。