Railsに設定しているtimezoneでDateTime.parseしたい

日本向けアプリを作る場合、Railsのconfig/application.rbのconfig.timezoneは’Asia/Tokyo’にするわけですが、DateTime.parseの結果にはtimezoneが反映されないという問題。 結論として、DateTime.parseじゃなくてTime.to_datetimeを使えという話。 rails cで叩いてみるとこんな感じ。

# Rails 3.2.6
irb(main):001:0> DateTime.parse('2012-07-31')
=> Tue, 31 Jul 2012 00:00:00 +0000

irb(main):002:0> Time.parse('2012-07-31').to_datetime
=> Tue, 31 Jul 2012 00:00:00 +0900

Tags: ruby rails

rubyで㈱などの機種依存文字をUTF-8に変換する

"㈱"や"㈲"などの文字をCSVなどからShift_JISで取り込み、rubyでUTF-8に変換するときにUndefinedConversionでハマった。

"㈱".encode('UTF-8', 'Shift_JIS')
> "\x87\x8A" from Shift_JIS to UTF-8
> /User/sanojimaru/workspace/example/lib/string.rb:849:in `encode': "\x87\x8A" from Shift_JIS to UTF-8 (Encoding::UndefinedConversionError)

Shift_JISは一部の機種依存文字に対応していないので、CP932(Windows-31J)を使えば良い。

"㈱".encode('UTF-8', 'CP932')

Tags: ruby

ActiveRecordはテーブルに存在しないフィールドも扱える

例えばこういうテーブルがあったとする。

CREATE TABLE hoges (
  id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  created_at DATETIME DEFAULT NULL,
  updated_at DATETIME DEFAULT NULL
);
INSERT INTO hoges VALUES 
  (null, 'test1', '2010-12-15 00:00:00', '2010-12-15 00:00:00'),
  (null, 'test2', '2011-05-04 00:00:00', '2011-05-04 00:00:00'),
  (null, 'test3', '2012-03-28 00:00:00', '2012-03-28 00:00:00');

ActiveRecordで対応するモデルは多分こうなる。

class Hoge < ActiveRecord::Base; end

ここで、Hogeテーブルのとあるレコードが作られた年を取得したいとき、

hoges = Hoge.select('YEAR(created_at) AS year').all
hoges.each_with_index do |h, i|
  puts "#{i} : #{h.year}"
end

こうすると、

1 : 2010
2 : 2011
3 : 1012

こうなるのでselectのaliasもちゃんとプロパティに追加されていることになる。 しかし、こうするとh.nameh.created_atなどのもともとあるフィールドが無くなってしまうのでこのようにするといい。

# こうする
hoges = Hoge.select([:name, 'YEAR(created_at) AS year']).all
hoges.each_with_index do |h, i|
  puts "#{i} : #{h.name} :  #{h.year}"
end

# これでもよい
hoges = Hoge.select(['*', 'YEAR(created_at) AS year']).all
hoges.each_with_index do |h, i|
  puts "#{i} : #{h.year}"
end

ActiveRecordはとっても便利。

Macbookにrbenv+ruby-build+powでモダンなrails開発環境

モダンなRails開発環境構築のtipsとして、以下のものをインストールする。

  • rbenv
  • ruby-build
  • pow
  • ruby
  • rails

rbenv + ruby-build

rbenvとは、複数バージョンのrubyを共存させるためのツール。デファクトスタンダードはRVMだが、rbenvはそれより遥かに小さくてシンプルだ。開発者はRailsの開発者であるDHHも所属する37signalsのsstephenson。

sstephenson/rbenv

まず、githubから$HOME/.rbenvにcheckout。

$ cd
$ git clone git://github.com/sstephenson/rbenv.git .rbenv

bashなら$HOME/.bash_profile、zshなら$HOME/.zshenvに下の2行を追加。

export PATH="$HOME/.rbenv/bin:$PATH"
eval "$(rbenv init -)"

shellを再起動するか、以下のコマンドを実行すればインストール完了。

$ exec $SHELL

ruby-build

任意のrubyのバージョンをビルド+インストールしてくれるツール。上記のrbenvと組み合わせて使うことが出来る。

sstephenson/ruby-build

同じように、githubからチェックアウトし、インストーラーを実行。

$ git clone git://github.com/sstephenson/ruby-build.git
$ cd ruby-build
$ ./install.sh

これを行うと、ruby-build本体は/usr/localにインストールされるのでgithubからcheckoutしたディレクトリは消してよい。

もし、rbenvのプラグインとしてのみインストールしたい場合、以下のようになる。

$ mkdir -p ~/.rbenv/plugins
$ cd ~/.rbenv/plugins
$ git clone git://github.com/sstephenson/ruby-build.git

ruby

rbenvとruby-buildのインストールが終わると、それらの管理下でrubyをインストールすることができる。例えば以下のコマンドでは、ruby1.9.3-p0をインストールする。

$ rbenv install 1.9.3-p0

インストールすることができるrubyの一覧を見るには以下のようにすればよい。

$ ruby-build --definitions
1.8.6-p420
1.8.7-p249
1.8.7-p334
1.8.7-p352
1.9.1-p378
1.9.2-p180
1.9.2-p290
1.9.3-dev
1.9.3-p0
1.9.3-preview1
1.9.3-rc1
2.0.0-dev
jruby-1.6.3
jruby-1.6.4
jruby-1.6.5
jruby-1.7.0-dev
maglev-1.0.0
rbx-1.2.4
rbx-2.0.0-dev
ree-1.8.6-2009.06
ree-1.8.7-2010.02
ree-1.8.7-2011.03

インストールしたrubyを使用可能にするには、以下のコマンドでリンクを貼り直す必要がある。これは、新しくgemをインストールした時などにも必要だ。

$ rbenv rehash

ここでインストールしたrubyをデフォルトに設定するには、以下のようにする。

$ rbenv global 1.9.3-p0
$ echo 'export RBENV_VERSION=1.9.3-p0' >> $HOME/.zshenv

一応、rubyのバージョンを確認しておこう。

$ ruby -v
ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-darwin11.2.0]

pow

powとは、DHHが開発したOSX用のrackサーバーで、たとえばhttp://example.dev/のようなバーチャルホスト設定を自動で行なってくれ、webrickの起動も必要ないという便利なもの。

インストールは以下のコマンドを実行するだけ。

$ curl get.pow.cx | sh

例えば、$HOME/workspace/RailsAppというrails applicationをpowで動かす場合、$HOME/.powにシンボリックリンクを置けば、ブラウザからhttp://railsapp.dev/というURLでこのアプリケーションを実行することができる。

$ cd $HOME/.pow
$ ln -s $HOME/workspace/RailsApp

powで動作しているRailsAppを再起動する場合、$HOME/workspace/RailsApp/tmp/restart.txtにtouchすればよい。これは、Passengerなどと同じ挙動だ。

$ touch $HOME/workspace/RailsApp/tmp/restart.txt

rails

最後にRailsをインストールする。

$ gem install rails
$ rbenv rehash

Rails applicationを作るにはこうする。

$ rails new SampleApp

ぼくの場合、Test::Unitは使わずRspecとMySQLを有効にしたいのでこうしている。

$ rails new SampleApp -T -d mysql

以上で、わりとモダンなrails開発環境が構築できた。 それでは、良きRailsライフを。

CentOSやUbuntuでrbenv+unicorn+rails

追記: 20120405

起動スクリプトのオプションが間違っていたので修正。

--cd $app_root && $rbenv_bin/bundle exec $rbenv_bin/unicorn_rails -c $app_root/config/unicorn.conf.rb -e production $app_root/config.ru
++cd $app_root && $rbenv_bin/bundle exec $rbenv_bin/unicorn_rails -c $app_root/config/unicorn.conf.rb -E production $app_root/config.ru

以下本文

複数バージョンのrubyを共存させるツールとして、過去にはRVMを使っていたが少し前にrbenvに変えた。Rails3世代になり、RVMのgemsetはbundlerで代替可能になったので、RVMの豊富な機能が少し邪魔に感じたので。同時に、サーバーサイドでも管理が簡単なのでrbenvを使うようにした。あまり推奨はされてないようだけど。

rbenvの通常インストール方法はsstephenson/rbenv - GitHubにあるのでそちらを参照するとよい。

また、rbenvで使うrubyインストーラーであるsstephenson/ruby-build - GitHubも一緒に使うとすごく便利。

以下、サーバーで行った作業のログをメモ。

# rbenvインストール
$ git clone git://github.com/sstephenson/rbenv.git /usr/share/rbenv

# profile.dにrbenvの設定を追加
$ vi /etc/profile.d/rbenv.sh
# ここから
export PATH="/usr/share/rbenv/bin:$PATH"
export RBENV_VERSION=ree-1.8.7-2011.12
export RBENV_DIR=/usr/share/rbenv
export RBENV_ROOT=/usr/share/rbenv
eval "$(rbenv init -)"
# ここまで

# /etc/profile.d/rbenv.shに実行権限を与える
$ chmod 755 /etc/profile.d/rbenv.sh

# 一旦上記設定を読み込む
$ source /etc/profile.d/rbenv.sh

# ruby-buildインストール
$ git clone git://github.com/sstephenson/ruby-build.git
$ cd ruby-build
$ ./install.sh

# 今回はREE1.8.7をインストール
$ rbenv install ree-1.8.7-2011.12

# REE1.8.7をデフォルトで使用する
$ rbenv global ree-1.8.7-2011.12

と、ここまででrbenvのセットアップは完了。動作確認のために、一度シェルを再起動した上でrubyのバージョンを確認してみる。

$ ruby -v
ruby 1.8.7 (2011-12-28 MBARI 8/0x8770 on patchlevel 357) [i686-linux], MBARI 0x8770, Ruby Enterprise Edition 2011.12

正しくREEが使われることが確認できた。あとは、gemでrailsとunicorn_railsをインストールすればよい。

rbenv上のREEでunicornを起動するには、以下のようなスクリプトを作ればよい。

app_root=/var/www/rails_application
rbenv_bin=/usr/share/rbenv/versions/ree-1.8.7-2011.12/bin
cd $app_root && $rbenv_bin/bundle exec $rbenv_bin/unicorn_rails -c $app_root/config/unicorn.conf.rb -e production $app_root/config.ru

普段はUpstartでrespawnや自動起動などをしているが、その場合 /etc/init/以下に次のような設定ファイルを作ればいい。これは、アプリケーションごとに1つのファイルになるので、例えばアプリケーション名がRailsAppならばRailsApp.confなどとする。

#!/bin/sh
description "Rails Application"
start on runlevel [2345]

script
  app_root=/var/www/rails_application
  rbenv_bin=/usr/share/rbenv/versions/ree-1.8.7-2011.12/bin
  cd $app_root && $rbenv_bin/bundle exec $rbenv_bin/unicorn_rails -c 
  $app_root/config/unicorn.conf.rb -e production $app_root/config.ru >> /tmp/upstart.log 2>&1
end script

respawn

このアプリケーションの起動や終了はLinuxの場合ディストリビューションによって変わるが、大体の場合以下のようなコマンドで実行できるので自分の環境に最適なものを選択する。

# 起動
$ sudo service railsapp start
# または
$ sudo initctl start railsapp

# 終了
$ sudo service railsapp stop
# または
$ sudo initctl stop railsapp

UpstartはUbuntu10.04、CentOS6、Amazon Linuxなどで使える。従来の/etc/init.d/以下にあるようなスクリプトよりだいぶシンプルに書けるので積極的に使っている。他にもいろいろとメリットがあるらしいが、正直よくわからない。

rails3で書式系の独自バリデーションをまとめてみる

railsは3からしか触っていないので以前がどうだったか知りませんが、rails3では独自のvalidationを作るのも簡単でよいです。 試しに、入力の書式を検証するFormatsValidatorというクラスを書いてみました。

# RAILS_ROOT/app/validators/formats_validator.rb
class FormatsValidator < ActiveModel::EachValidator

  def validate_each(record,attribute,value)
    begin
      r = __send__ options[:type].to_s, value
    rescue
      raise "Invalid option (type) is specified."
    end

    unless r
      record.errors[attribute] << (options[:message] || I18n.t('activerecord.errors.messages.invalid'))
    end
  end

  private
  # e-mail address
  def email(value)
    # 値を評価する式
  end
end

これをRAILS_ROOT/app/validators以下に配置して、modelから以下のように呼び出す。

class User < ActiveRecord::Base
  validates :mail_addr, :presence => true,
    :length => { :maximum => 255 }, :formats => { :type => :email }
end

modelのvalidatesメソッドの引数optionsはEachValidator::initializeに渡されるので、validate_eachでoptionsを参照することができます。 もとは1validatorにつき1クラスという想定っぽいですが、正規表現1行で使えるようなvalidatorはまとめてしまいたいときは使えそうですね。

Tags: Ruby Rails

rubyのヒアドキュメントで終端をインデントする方法

rubyのヒアドキュメントって高機能で素敵なんだけど、せっかくrubyで見通しの良いコードが書けてもヒアドキュメントの空気読めない感じが全体の雰囲気を壊してしまったりするよね。

例えばこんなの:

FactoryGirl.define do
  factory :status do
    site
    response_code 200
    response_time 1.5
    sequence(:response_body) do |n| response_body <<"EOT"
<html>
  <head>
    <title>title#{n}</title>
  </head>
  <body>
    <p>test#{n}</p>
  </body>
</html>
EOT
    end
  end
end

なんか全然空気読めてない。もうPHPのペライチのお問い合わせフォームみたいな見栄えになってる。なのでいつもこうする。

FactoryGirl.define do
  factory :status do
    site
    response_code 200
    response_time 1.5
    sequence(:response_body) do |n| response_body <<"EOT"
      <html>
        <head>
          <title>title#{n}</title>
        </head>
        <body>
          <p>test#{n}</p>
        </body>
      </html>
EOT
    end
  end
end

当然ながらヒアドキュメントって行頭のスペースとかもそのまま入るので、値として許される場合だけにはなってしまうけど、この方がまだ見通しがいい。中身の<html>さんとかもう空気読みすぎ。そういうsyntaxみたいに見える。

しかし、EOTさんが無駄に引っ込んじゃってて気になって仕方ない。しかもsyntax highlightingでEOTさんが赤く輝いてたりして気が散ってしょうがない。

と思って調べたら、こんな書き方ができるらしい。

FactoryGirl.define do
  factory :status do
    site
    response_code 200
    response_time 1.5
    sequence(:response_body) do |n| response_body &lt;&lt;-"EOT"
      <html>
        <head>
          <title>title#{n}</title>
        </head>
        <body>
          <p>test#{n}</p>
        </body>
      </html>
    EOT
    end
  end
end

どうやらヒアドキュメントの開始の”«”に”-“(ハイフン)をくっつけると、終端がインデントしてても正しく読んでくれるらしい。

開始ラベルを <<-識別子' のように-’ を付けて書くことで終端 行をインデントすることができます。これ以外では、終端行に、余 分な空白やコメントさえも書くことはできません。

Ruby 1.9.3 リファレンスマニュアル

っていうかFactoryGirlもすごくいいんだけど説明が難しいから割愛する。すごい。

Rails3.1.0 RC6からstableに上げるとproductionでassets pipelineが動かない

Ruby on Rails 3.1.0が正式リリースされたので、早速3.1.0RCで作っていたアプリをアップデートしたところproduction環境でassets pipelineが上手く動かなくなったので。

現象としては、stylesheet_link_tagやjavascript_include_tagで出力されるファイルのURLが、RC6だと

<link href="/assets/application-9bfec9e77b13d0aae452fa8f1b785be2.css" media="screen" rel="stylesheet" type="text/css" />
<script src="/assets/application-773d7f39b6aab9114a3b3b034fe6ac17.js" type="text/javascript"></script>

だったのが、stableだと

<link href="/assets/application.css" media="screen" rel="stylesheet" type="text/css" />
<script src="/assets/application.js" type="text/javascript"></script>

になってしまうため、assets:precompileで予めassetsファイルを作る設定であったproduction環境において、cssファイルもjsファイルも見つからずにかわいそうなことになるわけです。 それどころかrake assets:precompileもコケる事態になってました。

これは、3.1.0RC8あたりでassets pipelineに関する変更がいくつか取り込まれた関係で設定項目が増え、デフォルトの挙動が変更になったからですね。詳細はgithubのrailsリポジトリのissuesを見てください。

肝心の修正方法ですが、config/environments/production.rbの14~17行目あたりを以下のように変えるだけでOKでした。

# Don't fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = true

# Generate digests for assets URLs
config.assets.digest = true

さて、3.1.0がリリースされて2日目ですが、早くもpjax_railsが取り込まれると噂の3.2.0が待ちどおしいです。

rvm+railsなアプリケーションをcronで動かす

rvmには、rvm環境をラップして非rvm環境から叩く為の仕組みがある。

参考サイト: - Using RVM with God - rvm で unicorn

なので、例えばrvm環境のrails runnerをcronから叩きたい時は、まずrvm環境で以下のようにスクリプトを作っておき、

rvm wrapper ruby-1.9.2-p180@rails3 rails3 rails

できあがったスクリプトを非rvm環境から普通に叩けばよい。

$HOME/.rvm/bin/rails3_rails runner "MailReceiver.receive_with_pop" -e production

$HOME/.rvm以下としてるけど、適宜読み替えてください。

ちなみに私は、rvm環境の何かを叩くためのスクリプトを該当アプリのRAILS_ROOT/script以下に作っておき、そのスクリプトをcronに登録するようにしています。

Tags: ruby rails cron rvm

rubyでsmtpを使ってメールを送信する

rubyでsmtp経由でメールを送信する。

Net::SMTP.start('smtp.example.com', 587, 'example.com', 'hoge@example.com', 'fugafuga', 'plain') {|smtp|
  smtp.send_mail "test", "hoge@example.com", "to@mail.addr"
}

相変わらず簡単ですね。 通常のsmtp認証の場合は上記でOKです。POP before SMTPの場合は、Net::POPにPOP認証だけ行って接続を切るauth_onlyというメソッドがあるので送信前にそれを実行すればよい。

Net::POP3.auth_only( 'pop.example.com', 110, 'hoge@example.com', 'fugafuga' )
Net::SMTP.start('smtp.example.com', 587, 'example.com') {|smtp|
  smtp.send_mail "test", "hoge@example.com", "to@mail.addr"
}

参考: net/smtp - Rubyリファレンス

Tags: ruby smtp