Rails 2.1 リリース

Rails 2.1の安定板がリリースされました。新しい機能はこんな感じとのこと。

    • タイムゾーンのサポート
    • Dirty tracking(なんだか面白そう)
    • Gemの依存関係(前々回のRails勉強会でやってたやつ)
    • Named scope
    • マイグレーション番号がタイムスタンプに(UTC-based migrations)
    • キャッシュの改善(Better caching)

ぱっとみで「おぉ」って思ったのは2つ。

マイグレーション番号がタイムスタンプに(UTC-based migrations)

Instead of the migration file being named 001_one.rb it now has a more unique prefix that will less likely conflict with another migration somebody else happened to check-in around the same time.


そうそう。似たような場合で、別々のブランチで開発している場合とかも、マイグレーションファイル作成時にIRCで全員に確認しなきゃいけないことが多い。で、ブランチと同時にトランクにもコミットして個々のブランチにも反映してもらうみたいな。
タイムスタンプで管理できればこの問題はなくなるかなー。少なくとも、ファイル名が衝突してバージョン管理できないってことはなくなる。

Named scope

has_finderを取り込んだ。本家の説明をみると、こんな感じで使える。

class User < ActiveRecord::Base
  named_scope :active, :conditions => {:active => true}
end

# Standard usage
User.active    # same as User.find(:all, :conditions => {:active => true})

ブロック使えば、もっと便利。

class User < ActiveRecord::Base
  named_scope :registered, lambda { |time_ago| { :conditions => ['created_at > ?', time_ago] }
end

User.registered 7.days.ago # same as User.find(:all, :conditions => ['created_at > ?', 7.days.ago])


スコープそのものでも扱うことができる。

# Store named scopes
active = User.scoped(:conditions => {:active => true})
recent = User.scoped(:conditions => ['created_at > ?', 7.days.ago)

# Which can be combined
recent_active = recent.active

# And operated upon
recent_active.each { |u| ... }


うひゃーこれは便利だ。よく使用する検索って、モデルに定義してないと、DRYじゃないから、いちいちメソッド化しなきゃいけなくて面倒だった。
has_finderも知らなくて自分で簡単なやつを作って使ってました。必要なのは、大抵find, find_by_id, count なので、そこにしか対応しないっていう、今思うと、なんとも微妙。method_missing使えば他のfind_by_xxxにも対応できたな〜。

module ActiveRecord
  class Base
#   scoped_finder :except_deleted, :conditions =>"company_tags.approval_status != 0"
#
#   Above definition defines following class method in the class     
#
#   def self.count_except_deleted(*args)
#     with_scope(:find => {:conditions =>"company_tags.approval_status != 0"}) do
#       count(*args)
#     end
#   end
#   def self.find_except_deleted(*args)
#     with_scope(:find => {:conditions =>"company_tags.approval_status != 0"}) do
#       find(*args)
#     end
#   end
#   def self.find_by_id_except_deleted(*args)
#     with_scope(:find => {:conditions =>"company_tags.approval_status != 0"}) do
#       find_by_id(*args)
#     end
#   end
 
    def scoped_finder(method_name, options = {})
      %w(count find find_by_id).each do |n|
        instance_eval <<-DEFINE
          def #{n}_#{method_name}(*args)
            with_scope(:find => #{options.inspect}) do
              #{n}(*args)
            end
          end
        DEFINE
      end
    end  
  end
end