ActiveRecord を詳しく

从 ’w’)

http://wota.jp/ac/

ActiveRecord を詳しく

从 ’w’)

http://wota.jp/ac/

ActiveRecord を詳しく(away)

从;’w’)

http://wota.jp/ac/

ActiveRecord

Introduction

基本

マイナー

未来

CRUD : ActiveRecord Pattern

Associations

Associations

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

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を文字列で受け取りたい

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))
End