ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

「RailsによるアジャイルWebアプリケーション開発」17章まで読了

Active Record その1
Railsのキモって感じな機能ですね。

Rails外で動くActive Record

sqlite3でこんな感じ。depot/exam/ar1.rbに置いているので、相対パスで../になってます。

require 'rubygems'
require 'activerecord'

ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "../db/development.sqlite3")

class Order < ActiveRecord::Base
end

order = Order.find(1)
order.name = "Dave Thomas"
order.save

列一覧と列の詳細情報の取得

本書中ではscript/console使ってますが、ここでは上のプログラムをコピーして作ってみます。

require 'rubygems'
require 'activerecord'

ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "../db/development.sqlite3")

class Order < ActiveRecord::Base
end

p Order.column_names

p Order.columns_hash["payment_type_id"]

結果はこう。

>ruby ar2.rb
ruby ar2.rb
["id", "name", "address", "email", "created_at", "updated_at", "payment_type_id"]
#<ActiveRecord::ConnectionAdapters::SQLiteColumn:0x301f038 @precision=nil, @primary=false, @name="payment_type_id", @default=nil, @limit=nil, @null=true, @scale=nil, @sql_type="integer", @type=:integer>

>

自由課題やったせいで若干違うのですが、目的は達成できたようです。
この手のメタ情報を扱うことが、フレームワークとしては重要なんですよね。

_before_type_cast

列の値を読み込むとき、Railsは適切なRubyの型にキャストするそうです。
で、それを抑制するのが、_before_type_cast。
これを読み込む属性の名前の後ろに付けると良いようです。

サンプルはこれ。

require 'rubygems'
require 'activerecord'

ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "../db/development.sqlite3")

class Order < ActiveRecord::Base
end

o = Order.find(1)
casted_updated_at = o.updated_at
raw_updated_at =  o.updated_at_before_type_cast

print casted_updated_at.class.to_s, " / ", casted_updated_at, "\n"
print raw_updated_at.class.to_s, " / ", raw_updated_at, "\n"

結果はこう。

>ruby ar3.rb
ruby ar3.rb
Time / Wed Jun 11 23:50:28 +0900 2008
String / 2008-06-11 23:50:28

>

_before_type_cast指定なしの(キャストされる)方は、Time型になったのに対し、
指定ありの(キャストされない)方は、String型となっています。
どうでもいいが、Time型ってミリ秒以下は扱えないのね。。

ISBNは一意じゃない

なんといってもISBNは一意な値です。

とか書いてありますが、違います。ISBNは一意じゃないんです。
しつこいようだが、悔しかったので繰り返し書いてみる。
http://d.hatena.ne.jp/taka_2/20080611#p1

newとcreateの違い

create = new(オブジェクトのインスタンス化) + save(データベースへの永続化)
って感じです。

動的ファインダ

メソッドがなくても、自動的に作ってくれる(?)なんだかすごい機能。
最初の1件だけ返すバージョンと、条件に合致する行全てを返すバージョンの二通りあります。

find_by_[カラム名] = find(:first, :conditions => ...)
find_all_by_[カラム名] = find(:all, :conditions => ...)

データの再読み込み

ユニットテスト以外の場所で使うことはめったにありません。

だそうですが、コードを書いて検証してみる。

require 'rubygems'
require 'activerecord'

ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "../db/development.sqlite3")

class Order < ActiveRecord::Base
end

# ID1のnameは'Dave Thomas'
o = Order.find(1)
p o.name

# ID1のnameを'taka_2'に書き換える
o2 = Order.find(1)
p o2.name
o2.name = 'taka_2'
o2.save

# 書き換え後のnameをそれぞれ表示
p o.name
p o2.name

# oのnameが古いままなので、再読み込み
o.reload

# どっちも新しくなった
p o.name
p o2.name

結果はこう。

"Dave Thomas"
"Dave Thomas"
"Dave Thomas"
"taka_2"
"taka_2"
"taka_2"

まあ、そのまんまですが。
更新ボタンとかある画面で使えそう。でも、showアクションをもう一度呼べば同じか。。

saveとsave!, createとcreate!の違い

save, create: 失敗時例外が発生しない
save!, create!: 失敗時例外が発生する
!が付いているものの、特に破壊的というわけではなさそうですね。