[Ruby] ActiveRecord off Rails

» Posted by on 1月 8, 2009 in Blog | 0 comments

How to use ActiveRecord without Rails.
Rails 無しで ActiveRecord を使う方法。

メモ

  • 他の O/R Mapper と違い、基本的にデータベースの定義を書く必要はない
    ActiveRecord::Base のサブクラスを定義するだけで OK
    プログラム実行後のテーブルへの初回アクセス時に、テーブルの定義を調べている
  • テーブルの定義などに命名規則がある
    この命名規則に従っていない場合少し ruby のコードを書く必要がある
  • 細かいことをしようとすると直接 SQL を各必要が出てくる
  • テーブルのデータへは ActiveRecord::Base のサブクラスを使ってアクセスする

基本的な使い方

  1. データベースへの接続情報の入った YAML 形式のファイルを作る
  2. データベース内のテーブル1つにつき、ActiveRecord::Base の sub class を1つ定義する
  3. YAML ファイルの情報を元にデータベースへの接続を確立する
  4. ActiveRecord のサブクラスのインスタンスを new で生成したり、find メソッドでレコードを検索して操作する

簡単な使い方

#!/usr/bin/env ruby

# definition of 'groups' table
#CREATE TABLE IF NOT EXISTS groups (
#  id       INT(32) UNSIGNED NOT NULL AUTO_INCREMENT,
#  name     VARCHAR(255) NOT NULL,
#  PRIMARY KEY (id)
#) ENGINE=MyISAM;

require 'rubygems'
require 'activerecord'

# automatically map to 'groups' table.
class Group < ActiveRecord::Base
end # class

spec = {
  :adapter  => 'mysql',
  :encoding => 'utf8',
  :database => 'messadb_dev', # database name
  :username => 'messauser',   # username for mysql
  :password => 'messapass',   # password for mysql
  :socket   => '/var/run/mysqld/mysqld.sock',
}
ActiveRecord::Base.establish_connection(spec)

# create a record
group = Group.new
#group.id = 2
group.name = 'christmas town'
group.save

# find a record by it's name
group = Group.find_by_name('christmas town')
puts "group.id   : #{group.id}"   # 
puts "group.name : #{group.name}" # 'christmas town'

環境例

database: mysql
dbnames:
  本番用:   messadb
  テスト用: messadb_test
  開発用:   messadb_dev
tables:
  table1: users
  table2: groups

ディレクトリ構成例

activerecord-sample/
  app/
    messa_app.rb      # アプリケーション
  config/
    database.yml      # データベースの接続の設定ファイル
  db/
    setup_dev.mysql   # データベースの初期化スクリプト
  lib/
    messa/
      db.rb           # Messa::DB::Base, ActiveRecord::Base のサブクラス
      db/
        user.rb      # Messa::DB::Base のサブクラス
        group.rb     # Messa::DB::Base のサブクラス

必要なもののインストール (Ubuntu 8.10 Intrepid Ibex)

% sudo aptitude install ruby rubygems
% sudo gem install activerecord
% sudo aptitude install libmysql-ruby

セットアップ&実行例

% cd activerecord-sample
% mysql -u root -p
mysql> source db/setup_dev.mysql
mysql> exit
% ruby app/messa_app.rb

app/messa_app.rb

ActiveRecord::Base のサブクラスの Messa::DB::User と Messa::DB::Group をロードする。

require 'messa/db'
require 'messa/db/user'
require 'messa/db/group'

YAML のデータベース設定ファイルをロードする、それを Messa::DB::Base にセットして、開発用の設定を選択する。

config_file = File.join(MESSA_CONFIG_DIR, 'database.yml')
config = YAML::load(ERB.new(IO.read(config_file)).result)
Messa::DB::Base.configurations = config
Messa::DB::Base.establish_connection(:messadb_development)
#Messa::DB::Base.establish_connection(:messadb_test)
#Messa::DB::Base.establish_connection(:messadb_production)

新規の group を作成してデータベースに保存。

group = Messa::DB::Group.new
group.id = 1
group.name = 'halloween town'
group.save

新規のユーザーを作成してデータベースに保存。

user = Messa::DB::User.new
user.id   = 1
user.group_id   = 1
user.name = 'Jack'
user.birthday  = '1901-10-31'
user.save

ユーザーを id を使って取得して表示。

jack = Messa::DB::User.find(1)
puts "jack.id         : #{jack.id}"         # 1
puts "jack.group_id   : #{jack.group_id}"   # 1
puts "jack.group.name : #{jack.group.name}" # 'halloween town'
puts "jack.name       : #{jack.name}"       # 'Jack'
puts "jack.birthday   : #{jack.birthday}"   # '19001-10-31'
puts "jack.age        : #{jack.age}"        # 107


ソースコード

Download Source Code

messa_app.rb
#/usr/bin/env ruby
# -*- coding: utf-8 -*-

$LOAD_PATH << File.expand_path(File.join('..', 'lib'), File.dirname(__FILE__))

require 'messa/db'
require 'messa/db/user'
require 'messa/db/group'
require 'date'

MESSA_ROOT_DIR   = File.expand_path(File.join('..'), File.dirname(__FILE__)) unless defined? MESSA_ROOT_DIR
MESSA_CONFIG_DIR = File.join(MESSA_ROOT_DIR, 'config')                       unless defined? MESSA_CONFIG_DIR

config_file = File.join(MESSA_CONFIG_DIR, 'database.yml')
config = YAML::load(ERB.new(IO.read(config_file)).result)
Messa::DB::Base.configurations = config
Messa::DB::Base.establish_connection(:messadb_development)
#Messa::DB::Base.establish_connection(:messadb_test)
#Messa::DB::Base.establish_connection(:messadb_production)

Messa::DB::Group.delete_all
Messa::DB::User.delete_all

group = Messa::DB::Group.new
group.id = 1
group.name = 'halloween town'
group.save

user = Messa::DB::User.new
user.id   = 1
user.group_id   = 1
user.name = 'Jack'
user.birthday  = '1901-10-31'
user.save

user = Messa::DB::User.new
#user.id   = 2
user.group_id   = 1
user.name = 'Sarry'
user.birthday  = '1997-10-31'
user.save

puts "today : #{Date.today}" # 2009-01-06

jack = Messa::DB::User.find(1)
puts "jack.id         : #{jack.id}"         # 1
puts "jack.group_id   : #{jack.group_id}"   # 1
puts "jack.group.name : #{jack.group.name}" # 'halloween town'
puts "jack.name       : #{jack.name}"       # 'Jack'
puts "jack.birthday   : #{jack.birthday}"   # '19001-10-31'
puts "jack.age        : #{jack.age}"        # 107

sarry = Messa::DB::User.find(:first, :conditions => {:name => 'Sarry'})
puts "sarry.id         : #{sarry.id}"        # 
puts "sarry.group_id   : #{sarry.group_id}"  # 1
puts "sarry.group.name : #{jack.group.name}" # 'halloween town'
puts "sarry.name       : #{sarry.name}"      # 'Sarry'
puts "sarry.birthday   : #{sarry.birthday}"  # '1997-10-31'
puts "sarry.age        : #{sarry.age}"       # 11

__END__

today : 2009-01-06
jack.id         : 1
jack.group_id   : 1
jack.group.name : halloween town
jack.name       : Jack
jack.birthday   : 1901-10-31
jack.age        : 107
sarry.id         : 10
sarry.group_id   : 1
sarry.group.name : halloween town
sarry.name       : Sarry
sarry.birthday   : 1997-10-31
sarry.age        : 11
database.yml
messadb_development:
  adapter: mysql
  encoding: utf8
  database: messadb_dev
  username: messauser
  password: messapass
  socket: /var/run/mysqld/mysqld.sock

messadb_test:
  adapter: mysql
  encoding: utf8
  database: messadb_test
  username: messauser
  password: messapass
  socket: /var/run/mysqld/mysqld.sock

messadb_production:
  adapter: mysql
  encoding: utf8
  database: messadb
  username: messauser
  password: messapass
  socket: /var/run/mysqld/mysqld.sock
setup_dev.mysql
-- messadb

-- % mysql -u root -p
-- mysql> create database messadb;
-- mysql> source create_tables.mysql
-- mysql> exit

DROP DATABASE messadb_dev;

CREATE DATABASE IF NOT EXISTS messadb_dev DEFAULT CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON messadb_dev.* TO messauser@localhost IDENTIFIED BY 'messapass';

use messadb_dev;

CREATE TABLE IF NOT EXISTS users (
  id       INT(32) UNSIGNED NOT NULL AUTO_INCREMENT,
  group_id INT(32) UNSIGNED NOT NULL,
  name     VARCHAR(255) NOT NULL,
  birthday DATE         NULL,
  added             TIMESTAMP    NOT NULL DEFAULT 0,
  updated           TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
) ENGINE=MyISAM;

CREATE TABLE IF NOT EXISTS groups (
  id       INT(32) UNSIGNED NOT NULL AUTO_INCREMENT,
  name     VARCHAR(255) NOT NULL,
  added             TIMESTAMP    NOT NULL DEFAULT 0,
  updated           TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
) ENGINE=MyISAM;
db.rb
# -*- coding: utf-8 -*-

require 'rubygems'
require 'activerecord'
require 'erb'
require 'yaml'

module Messa
  module DB
    class Base < ActiveRecord::Base
    end # class
  end # module
end # module
user.rb
# -*- coding: utf-8 -*-

require 'messa/db'
require 'date'

module Messa
  module DB
    class User < Messa::DB::Base
      set_table_name 'users'
      #has_one :group, {:class_name => 'Messa::DB::Group', :foreign_key => 'id'} # this works
      has_one :group, {:class_name => 'Messa::DB::Group', :foreign_key => 'id', :primary_key => 'group_id'}

      def age
        birthday = read_attribute(:birthday)
        return nil if birthday.nil?

        today = Date.today
        age = today.year - birthday.year
        if (today.month < birthday.month) or (birthday.month == today.month and today.day < birthday.day)
          age -= 1
        end
        return age
      end

      def age=(rhs)
        raise "Can't set value to age."
      end
    end # class
  end # module
end # module
group.rb
# -*- coding: utf-8 -*-

require 'messa/db'

module Messa
  module DB
    class Group < Messa::DB::Base
      set_table_name 'groups'
    end # class
  end # module
end # module