ActiveRecordを使ってみる

ActiveRecordはRubyOnRailsのO/Rマッパーとして有名ですが、Railsに依存しているわけではなく単体で使えます。

DBを用意する

なにはともあれ、DBがないと始まらないので適当に用意します。
create database ar;
 use ar;
 create table users (
   id int unsigned not null auto_increment,
   name varchar(50),
   age int,
   primary key (id)
 );
ユーザ名と年齢を管理するテーブルってことで。

DBへの接続とマッピング

require 'rubygems'
require_gem 'activerecord'

# ARで接続
ActiveRecord::Base.establish_connection(
  :adapter    =>      'mysql',
  :host       =>      'localhost',
  :username   =>      'ユーザ名',
  :password   =>      'パスワード',
  :database   =>      'ar',
  :socket     =>      '/tmp/mysql.sock'
)

# マッピングクラスを定義
class User < ActiveRecord::Base
end

データベースへのコネクションはActiveRecord::Base.establish_connectionで作られます。特にむずかしいところはないですね。socketは必ず書く必要はないです。書かない場合は/tmp/mysql.sockを見にいくのかな。

ActiveRecord::Baseを継承することでマッピングクラスを定義することができます。クラス名がUserusersテーブルへのマッピングを定義していることになります。クラスを単数形、テーブル名を複数形にすることで自動的にマッピングされます。いわゆるCoC(Convention over Configuration)ってやつです。

データの保存

newメソッド、もしくはcreateメソッドを使用し、ARオブジェクトを作成することでDBにデータを保存(Insert)することができます。newとcreateの違いは、newは明示的にsaveメソッドを実行する必要があり、createは自動的にsaveメソッドも実行されます。

ARオブジェクトを作成

user = User.new(:name => “UK”, :age => 20) user.save # DBに格納

# ARオブジェクトを作成
user = User.create(:name => "UK", :age =>20) # この時点でDBに格納される

データの検索

データが1件だけじゃ少ないので数件ほど追加しました。

データの検索(Select)にはfindメソッドを使用します。他にもfind_allとか、find_by_sqlとかあるけどとりあえずfindのみの説明で。ちなみにfind_allは非推奨的な雰囲気。find(:all)を使えってことですね。

DEPRECATION WARNING: find_all is deprecated and will be removed from Rails 2.0 ( use find(:all, ...)) See http://www.rubyonrails.org/deprecation for details.

findメソッド

findメソッドの引数にIDを指定することで、該当するデータのオブジェクトが得られます。IDは複数指定することが可能でその場合の戻り値は配列です。:allを指定すると、 全件分のオブジェクトが得られます。こちらの場合も配列です。:firstを指定すると、先頭のオブジェクトを得られます。

p User.find(1) # ID = 1のオブジェクト
=> #"UK", "id"=>"1", "age"=>"20"}>
p User.find(1,4) # ID =1, ID = 4 のオブジェクトを格納した配列
=> [#"UK", "id"=>"1", "age"=>"20"}>,
 #"bar", "id"=>"4", "age"=>"38"}>]
p User.find([2]) # ID = 2のオブジェクトを格納した配列
=> [#"hoge", "id"=>"2", "age"=>"40"}>]
p User.find(:all) # 全件分のオブジェクトを格納した配列
=> [#"UK", "id"=>"1", "age"=>"20"}>, #"hoge", "id"=>"2", "age"=>"40"}>, #"foo", "id"=>"3", "age"=>"14"}>, #"bar", "id"=>"4", "age"=>"38"}>]
p User.find(:first) # 先頭のオブジェクト
=> #"UK", "id"=>"1", "age"=>"20"}>

各カラムのデータにアクセスする場合は以下のようにします。

p User.find(1).name # UK
p User.find(1,4)[1].age # 38
p User.find([2])[0].name # hoge
p User.find(:all)[3].name # bar
p User.find(:first).age # UK

ActiveRecord::RecordNotFound

ちなみに存在しないID(この場合だと5とか)を指定すると例外(ActiveRecord::RecordNotFound)が発生するので注意して下さい。

条件文の指定

普通に考えて、データへのアクセスにIDの指定とか不便すぎますよね。次は引数に条件文を与えてみたいと思います。SQLで言う、where文に該当します。

p User.find(:first, :conditions => {:name => "UK"})
=> #"UK", "id"=>"1", "age"=>"20"}>
p User.find(:first, :conditions => {:name =>"hoge", :age => 40})
=> #"hoge", "id"=>"2", "age"=>"40"}>
p User.find(:first, :conditions => {:name => "aaa"})
=> nil

条件文は:conditionsで指定します。1番目のfindはnameカラムのデータが”UK”に該当するものから先頭のオブジェクトを返します。ここで:firstの指定を:allに変更すると:conditionsに該当する全てのオブジェクトを配列で返します。

User.crate(:name => "UK", :age => 20) # ID = 1と同じデータを保存
p User.find(:fist, :conditions => {:name => "UK"}
=> #"UK", "id"=>"1", "age"=>"20"}>
p User.find(:all, :conditions => {:name => "UK"}
=> [#"UK", "id"=>"1", "age"=>"20"}>, #"UK", "id"=>"5", "age"=>"20"}>]

パラメータによるサニタイズ

上記のやり方だとパラメータを直接クエリに挿入します。いわゆるサニタイズと言ったものは行っていません。それだと流石にまずいと思うので以下のようにパラメータ使用します。

p User.find(:all, :conditions => ["name = ? and age = ?", "UK", 20])
=> [#"UK", "id"=>"1", "age"=>"20"}>, #"UK", "id"=>"5", "age"=>"20"}>]

このようにしてパラメータを指定することで”UK”と20はサニタイズされます。

名前付きパラメータ

パラメータを使用している場合、クエスチョンマークが増えすぎるとわかりにくくなる時があります。その場合は名前つきパラメータを使用します。クエスチョンマークの代わりにシンボルを使用し、シンボルをキーにしたハッシュを渡します。

User.find(:all, :conditions => ["name = :name and age = :age", {:name => "UK", :age => 20}])
=> [#"UK", "id"=>"1", "age"=>"20"}>, #"UK", "id"=>"5", "age"=>"20"}>]

データの上書き保存

データの上書き保存(Update)するにはfindメソッドなどでARオブジェクトを作成、操作しsaveメソッドで行ないます。

user = User.find(:first, :conditions => {:name => "hoge"})
p user.age # 40
user.age =69
user.save

User.find(:first, :conditions => {:name => "hoge"})
=> [#"hoge", "id"=>"2", "age"=>"69"}>]

データの削除

データの削除(Delete)はdeleteメソッドや、destroyメソッドを使用します。

User.delete(1) # ID = 1を削除

user = User.find(:first, :conditions => {:name => "UK"})
user.destory # name が "UK"のデータを削除

雑感

とりあえずざっとActiveRecordに触れてみましたが、DBをRubyのオブジェクトのように扱えるのは思いの外気持ちがいいですなー。ただ、プライマリーキー名が”id”固定なところやテーブル名を複数形云々のあたりが気になる人はいるのかなぁと。あと複合キーとかどうするんだーとか。そういうところを気にしないので済むのであれば、ActiveRecordはかなり良いと思います。

それにしてもコードがみづらいことこの上ないですね・・・そのうちなんとかしたいところです。