ActiveRecordの内部実装を垣間見る その1

ARの実装とRuby処理系のTimeに関する実装でハマる - Lazy Technology
にも書いたけど、ARはDBMSのカラム情報に基づいて、格納された値を自動的にキャスト(変換)する。

ar = AR.new
ar.id = "hoge"
=> "hoge"
ar.id 
=> 0

キャストするのは格納時ではなく出力時。
オリジナルの値が欲しい場合はカラム名_before_type_castメソッドを呼ぶ。

a.id_before_type_cast
=> "hoge"


キャストのロジックを実装しているのはColumn#type_castメソッド。以下に定義されている。
activerecord\lib\active_record\connection_adapters\abstract\schema_definitions.rb

      # Casts value (which is a String) to an appropriate instance.
      def type_cast(value)
        return nil if value.nil?
        case type
          when :string    then value
          when :text      then value
          when :integer   then value.to_i rescue value ? 1 : 0
          when :float     then value.to_f
          when :decimal   then self.class.value_to_decimal(value)
          when :datetime  then self.class.string_to_time(value)
          when :timestamp then self.class.string_to_time(value)
          when :time      then self.class.string_to_dummy_time(value)
          when :date      then self.class.string_to_date(value)
          when :binary    then self.class.binary_to_string(value)
          when :boolean   then self.class.value_to_boolean(value)
          else value
        end
      end

ARがよく分からないバギーな動作をした時にこの辺を知っていると役立つ…のかな。

2007/5/21 追記

activerecord\lib\active_record\connection_adapters\abstract\schema_definitions.rb
に定義されているのはデフォルトのキャストロジックで、DBMSによっては個別のアダプタクラスでオーバーライドしている事もある。
activerecord\lib\active_record\connection_adapters\mysql_adapter.rb
を見る限り、MySQLでは特にオーバーライドしていない。