Rails 1.0 -> 1.1 の ActiveSupport 変更点まとめ

Ruby on Rails 1.0 までは AWDwR 本を読んで使い方が解ってる人も多いと思うのですが、1.1 以降でどんな詳細な機能が加わったのかはあまり知られてません。
そのために ActiveSupport の CHANGELOG を淡々と読んでまとめてみました。間違ってる可能性もあるのでそういう箇所があったらご指摘下さい。

Add CachingTools::HashCaching to simplify the creation of nested, autofilling hashes. [Nicholas Seckar]

CachingTools::HashCaching という Hash を利用したシンプルなキャッシュ機能のモジュールが加わりました。このモジュールを利用したいクラスで extend して

  hash_cache :methodname

とすると、そのメソッドが呼び出された時に結果が自動で methodname + cache *1という Hash のインスタンス変数_に格納されます。そのため一度

obj.methodname('foo', 'bar')

とメソッドを呼び出した場合、

obj.methodname_cache['foo']['bar']

に自動で結果が格納されています。

Added option to String#camelize to generate lower-cased camel case by passing in :lower, like "super_man".camelidze(:lower) # => "superMan" [DHH]

先頭は cemel 化せずに それ以降を camel 化するための引数 :lower が追加されました。たぶん RJS Template内部でよく使うために追加なのでしょう。

>> 'foo_bar'.camelize(:lower)
=> "fooBar"

Added Hash#diff to show the difference between two hashes [Chris McGrath]

hash の構造の差異を hash で返してくれます。

>> {:a => 'b', :c => 'd'}.diff({:a => 'b', :c => 'e'})
=> {:c=>"d"}

Enhance Inflector.underscore to convert '-' into '_' (as the inverse of Inflector.dasherize) [Jamis Buck]

  • ã‚’ _ に 変換します。
>> 'foo-bar'.underscore
=> "foo_bar"

Added Hash#to_xml and Array#to_xml that makes it much easier to produce XML from basic structures [DHH].

Hash を XML として出力してくれます。内部の型も要素としてそれっぽく出力してくれるようです。

>> puts({ :name => "David", :street_name => "Paulina", :age => 26, :moved_on => Date.new(2005, 11, 15) }.to_xml)
<?xml version="1.0" encoding="UTF-8"?>
<hash>
  <street-name>Paulina</street-name>
  <age type="integer">26</age>
  <name>David</name>
  <moved-on type="date">2005-11-15</moved-on>
</hash>

また引数にオプションとしていろいろ指定できます。
http://api.rails2u.com/docs/activerecord/classes/ActiveRecord/Base.html#M000449

Add Enumerable#group_by for grouping collections based on the result of some block. Useful, for example, for grouping records by date.

引数に渡した block の結果でコレクションしてくれます。サンプルでは ActiveRecord オブジェクトの day フィールドの値でコレクションしてます。

latest_transcripts.group_by(&:day).each do |day, transcripts|
  p "#{day} -> #{transcripts.map(&:class) * ', '}"
end

次の例では :result が true か false でコレクションしてます。

>> ([
?>   {
?>   :name => 'foo',
?>   :result => true,
?>   },
?>   {
?>   :name => 'bar',
?>   :result => false,
?>   },
?>   {
?>   :name => 'baz',
?>   :result => false,
?>   },
?>   {
?>   :name => 'hoge',
?>   :result => false,
?>   },
?> ]).group_by {|h| h[:result]}
=> {false=>[{:name=>"bar", :result=>false}, {:name=>"baz", :result=>false}, {:name=>"hoge", :result=>false}], true=>[{:name=>"foo", :result=>true}]}

Add Array#in_groups_of, for iterating over an array in groups of a certain size.

arrya を引数の数値ごとに区切ってグループ化してくれます。テーブルレイアウトなんかで便利そうです。

>> %w(1 2 3 4 5 6 7).in_groups_of(3) {|g| p g}
["1", "2", "3"]
["4", "5", "6"]
["7", nil, nil]

はてなテーブル記法なんかを作りたい場合はこんな感じに。

>> ('a'..'z').to_a.in_groups_of(5) {|g| puts g.unshift('').push('').join('|')}
|a|b|c|d|e|
|f|g|h|i|j|
|k|l|m|n|o|
|p|q|r|s|t|
|u|v|w|x|y|
|z|||||

Added Kernel#daemonize to turn the current process into a daemon that can be killed with a TERM signal [DHH]

Kernel#daemonize を呼び出すだけで簡易 daemon化ができます。バックグラウンドジョブとして使うユーティリティを作るときとかに使えそうですが普通 Daemons とかつかうなぁとか思ったり思わなかったり。
また win32 な環境ではうまく動きません。

Added Time#beginning_of_quarter #3607 [cohen.jeff@gmail.com]

時刻の四半期頭を求めるメソッド Time#beginning が追加されました。

>> Time.now
=> Mon May 22 15:01:50 JST 2006
>> Time.now.beginning_of_quarter
=> Sat Apr 01 00:00:00 JST 2006

Make String#last return the string instead of nil when it is shorter than the limit [Scott Barron].

文字列の最後の文字を求める last メソッドに長さを指定できるようになりました。

>> 'foobar'.last
=> "r"
>> 'foobar'.last 3
=> "bar"

Add 'around' methods to Logger, to make it easy to log before and after messages for a given block as requested in #3809.

logger に around_* メソッドが追加され、第一第二引数にbefore, after となるログメッセージを指定し、ブロック内部であれこれできるようです。

>> logger = Logger.new STDOUT
>> logger.around_info('---- ここから ----', '---- ここまで ----') {
?>   logger.debug 'example'
>> }
---- ここから ----
example
---- ここまで ----

Added delegation support to Module that allows multipledelegations at once (unlike Forwardable in the stdlib) [DHH].

インスタンス変数(?)まとめてメソッドをデリゲートする機能が Module にサポートされたようです。

class Account < ActiveRecord::Base
  has_one :subscription
  delegate :free?, :paying?, :to => :subscription
  delegate :overdue?, :to => "subscription.last_payment"
end

account.free?    # => account.subscription.free?
account.overdue? # => account.subscription.last_payment.overdue?
class ActiveRecord::Base
  delegate :columns, :column_names, :to=>”self.class”
end

Added reusable reloading support through the inclusion of the Relodable module that all subclasses of ActiveRecord::Base, ActiveRecord::Observer, ActiveController::Base, and ActionMailer::Base automatically gets. This means that these classes will be reloaded by the dispatcher when Dependencies.mechanism = :load. You can make your own models reloadable easily

development 時に include しておくだけで自動でクラスなどをリロードしてくれる Reloadable モジュールが追加されました。詳しくは ヽ( ・∀・)ノくまくまー(2006-03-07) で。

Add Object#instance_exec, like instance_eval but passes its arguments to the block. (Active Support will not override the Ruby 1.9 implementation of this method.) [Sam Stephenson]

instance_eval の block に引数がとれるようなメソッド、Object#instance_exec が追加されました。

>> block = Proc.new { |value| [self, value] }
=> #<Proc:0x4087347c@(irb):44>
>> 'hello'.instance_exec('goodbye', &block)
=> ["hello", "goodbye"]

Add Proc#bind(object) for changing a proc or block's self by returning a Method bound to the given object. Based on why the lucky stiff's "cloaker" method. [Sam Stephenson]

Proc#bind が追加されました。bind した object が Proc の block の中では self として参照されるようです。

>> example = Proc.new {|method| self.send(method) }
=> #<Proc:0x40877f90@(irb):59>
>> example.bind('string').call(:length)
=> 6

Add ActiveSupport::JSON and Object#to_json for converting Ruby objects to JSON strings.

Object を JSON 形式の String に変換する Object#to_json が実装されました。

>> puts({:foo => 'bar', :hoge => 'huga'}.to_json)
{"hoge": "huga", "foo": "bar"}

Add Object#with_options for DRYing up multiple calls to methods having shared options. [Sam Stephenson]

オプションとして Hash を引数で渡すような場合に共有できる with_options メソッドが追加されました。

ActionController::Routing::Routes.draw do |map|
  # Account routes
  map. do |account|
    account.home   ,:controller => 'account', '',       :action => 'dashboard'
    account.signup ,:controller => 'account', 'signup', :action => 'new'
    account.logout ,:controller => 'account', 'logout', :action => 'logout'
  end
end

を次のように書くことができるようになりました。

ActionController::Routing::Routes.draw do |map|
  # Account routes
  map.with_options(:controller => 'account') do |account|
    account.home   '',       :action => 'dashboard'
    account.signup 'signup', :action => 'new'
    account.logout 'logout', :action => 'logout'
  end
end

Add Symbol#to_proc, which allows for, e.g. [:foo, :bar].map(&:to_s). [Marcel Molina Jr.]

Symbol#to_proc が追加されました。知っておくとかなり便利です。参考: ヽ( ・∀・)ノくまくまー(2006-03-09)

Added the following methods [Marcel Molina Jr., Sam Stephenson]:

Object#copy_instance_variables_from(object) to copy instance variables from one object to another

別のオブジェクトからインスタンス変数をコピーします。

Object#extended_by to get an instance's included/extended modules

インスタンスが include, extend した Module 一覧を取得します。

>> 'string'.extended_by
=> [ActiveSupport::CoreExtensions::String::Iterators, ActiveSupport::CoreExtensions::String::StartsEndsWith, ActiveSupport::CoreExtensions::String::Inflections, ActiveSupport::CoreExtensions::String::Conversions, ActiveSupport::CoreExtensions::String::Access, Enumerable, Comparable]
Object#extend_with_included_modules_from(object) to extend an instance with the modules from another instance

別のオブジェクトからインスタンスメソッドをコピーします。

*1:_cache という名前は変更可能