ActiveRecord を詳しく(away)
从;’w’)
ActiveRecord
- Rails に含まれるフレームワークの1つ
-
- Assocations
Introduction
基本
- CRUD operations
- Assocations
マイナー
未来
- Acts As View
- Finder Query
CRUD : ActiveRecord Pattern
Associations
- has_one
- has_many
- belongs_to
- has_and_belongs_to_many
With Scope
データ検索と作成時に指定条件を付与
With Scope : find / create
条件のデフォルト値として利用
Member.with_scope(:find => {:conditions => "grade = 1"}) do
Member.find(:all)
# => 'SELECT * FROM members WHERE grade = 1'
end
Member.with_scope(:create => {:grade => 1}) do
Member.create(:name=>"舞波")
# => 'INSERT INTO members ("name", "grade") VALUES ("舞波", 1)'
end
With Scope : conflict
条件が衝突する場合
Member.with_scope(:find => {:conditions => "grade = 1"}) do
Member.find(:all, :conditions => "grade = 2")
# => 'SELECT * FROM members WHERE grade = 1 AND grade = 2'
end
Member.with_scope(:create => {:grade => 1}) do
Member.create(:name => "舞波", :grade => 2)
# => 'INSERT INTO members ("name", "grade") VALUES ("舞波", 1)'
end
With Scope : conflict
条件が衝突する場合
Member.with_scope(:find => {:conditions => "grade = 1"}) do
Member.find(:all, :conditions => "grade = 2")
# => 'SELECT * FROM members WHERE grade = 1 AND grade = 2'
end
Member.with_scope(:create => {:grade => 1}) do
Member.create(:name => "舞波", :grade => 2)
# => 'INSERT INTO members ("name", "grade") VALUES ("舞波", 1)'
end
↓不正アクセス防止に利用
with_scope : 想定した条件
CRUD の引数 : ユーザの入力
With Scope : Validation on GMail System
アクセス権限のチェック
def list
@mails = Mail.find(:all, :conditions => ["owner_id = ?", owner_id])
end
def show
@mail = Mail.find(params[:id], :conditions => ["owner_id = ?", owner_id])
end
def create
params[:mail][:owner_id] = owner_id
@mail = Mail.create(params[:mail])
end
With Scope : using with_scope
def mine
{ :find => {:conditions => ["owner_id = ?", owner_id]},
:create => {:owner_id => owner_id } }
end
def list
Mail.with_scope(mine){ @mails = Mail.find(:all) }
end
def show
Mail.with_scope(mine){ @mail = Mail.find(params[:id]) }
end
def create
Mail.with_scope(mine){ @mail = Mail.create(params[:mail]) }
end
With Scope : DRY
DRY (image)
Mail.with_scope(mine) do
def list
@mails = Mail.find(:all)
end
def show
@mail = Mail.find(params[:id])
end
def create
@mail = Mail.create(params[:mail])
end
end
Scoped Access
コントローラ全体に with_scope をしかけるプラグイン
class MailController < ApplicationController
scoped_access Mail, :mine
def list
@mails = Mail.find(:all)
end
def show
@mail = Mail.find(params[:id])
end
def create
@mail = Mail.create(params[:mail])
end
end
Scoped Access
- nested with_scope
- with_exclusive_scope
Acts As View
・モデルをDBのViewのように
・STIは微妙
Acts As View : scoped_methods
class ActiveRecord::Base
def self.scoped_methods
@scoped_methods ||= []
end
def self.current_scoped_methods
scoped_methods.last
end
end
Acts As View : default_scope
class ActiveRecord::Base
def self.scoped_methods
@scoped_methods ||= []
end
end
↓ default_scope の導入
class ActiveRecord::Base
@default_scope = {}
cattr_accessor :default_scope
def self.scoped_methods
@scoped_methods ||= [@default_scope]
end
end
Acts As View : Coding
module ActsAsView
module ClassMethods
def acts_as_view(options)
scoping = construct_scoping_from_hash(options)
scoped_methods << scoping
end
end
end
class Member < ActiveRecord::Base
end
class ActiveMember < ActiveRecord::Base
acts_as_view :deleted=>false
end
Acts As View : STI
module STI
module ClassMethods
def sti
acts_as_view :type=>name
end
end
end
class Person < ActiveRecord::Base
end
class Customer < Person
sti
end
Finder Query
生成したSQLを文字列で受け取りたい
- 実行したSQLをログに取ったり
- どういうSQLが作られるか確認したり
- サブクエリの実現
Finder Query : Coding
AR.xxx(*args) # => AR.connection.execute(AR.xxx_query(*args))
AR.find_query(*args) # => "SELECT ..."
AR.find(*args) # => AR.connection.execute(AR.find_query(*args))
AR.create_query(*args) # => "INSERT ..."
AR.create(*args) # => AR.connection.execute(AR.create_query(*args))
Finder Query : Subquery
Aclog.find_by_sql <<-SQL
SELECT src.path, path_masters.name, cnt
FROM (
SELECT path, COUNT(*) AS cnt
FROM entries
GROUP BY path
ORDER BY cnt DESC
LIMIT 10
) AS src
LEFT JOIN path_masters USING (path)
SQL
Finder Query : Subquery with FinderQuery
Aclog.find(:all, :include => :path_master,
:from => Aclog.count_query(:select=>:path, :group=>:path,
:order=>"count_path DESC", :limit=>10))