Skip to content

tmtm/sequel

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5,031 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Sequel はシンプルで柔軟でパワフルな、Ruby の SQL データベースアクセスツールキットです。

  • Sequel はスレッド安全、コネクションプーリング、SQLクエリを構築するための簡潔な DSL、テーブルスキーマを提供します。

  • Sequel は、レコードを Ruby オブジェクトにマッピングし、関連レコードを操作するための包括的な ORM 層を含んでいます。

  • Sequel は次のような先進的なデータベース機能をサポートします: プリペアドステートメント、bound variables,、ストアドプロシジャ、セーブポイント、ツーフェーズコミット、トランザクション分離、マスター/スレーブ構成、データベースシャーディング

  • Sequel は現在次のアダプタを持っています: ADO, Amalgalite, CUBRID, DataObjects, DB2, DBI, Firebird, IBM_DB, Informix, JDBC, MySQL, Mysql2, ODBC, OpenBase, Oracle, PostgreSQL, SQLAnywhere, SQLite3, Swift, TinyTDS

ソースコードをチェックアウトするには:

git clone git://github.com/jeremyevans/sequel.git

コメントや提案があれば、Google グループに投稿してください。

sudo gem install sequel
require 'sequel'

DB = Sequel.sqlite # メモリーデータベース

DB.create_table :items do
  primary_key :id
  String :name
  Float :price
end

items = DB[:items] # データセットの作成

# Populate the table
items.insert(:name => 'abc', :price => rand * 100)
items.insert(:name => 'def', :price => rand * 100)
items.insert(:name => 'ghi', :price => rand * 100)

# レコード数の出力
puts "Item count: #{items.count}"

# 平均価格の出力
puts "The average price is: #{items.avg(:price)}"

Sequel はデータベースに素早くアクセスするための IRB コンソールを含んでいます(通常は bin/sequel として参照できます)。次のようにして使用できます:

sequel sqlite://test.db # カレントディレクトリの test.db

DB に格納されたデータベースオブジェクトでIRB セッションを開始します。

IRB シェル(デフォルトの振る舞い)に加え、bin/sequel はデータベースのマイグレート、スキーママイグレーションのダンプ、データベースのコピーもサポートします。詳細は bin/sequel ガイド を見てください。

Sequel はデータベースの接続と操作に手間がかからないようにデザインされています。Sequel は、アプリケーションに集中できるように、接続の維持、SQL を正しく整形、レコードの取り出しのようなすべての退屈なことを処理します。

Sequel はデータを取り出すためにデータセットの概念を使用します。データセットオブジェクトは SQL クエリをカプセル化し、連鎖性をサポートし、簡潔で柔軟な便利な Ruby DSL を使用してデータを取り出せるようにします。

例えば、次のワンライナーは中東の国の GDP 平均を返します:

DB[:countries].filter(:region => 'Middle East').avg(:GDP)

これは次と同等です:

SELECT avg(GDP) FROM countries WHERE region = 'Middle East'

データセットは必要な時だけレコードを取り出し、それは保存されて後で再利用されます。レコードはハッシュ(またはカスタムモデルオブジェクト)として取り出され、Enumerable インタフェースを使用してアクセスできます:

middle_east = DB[:countries].filter(:region => 'Middle East')
middle_east.order(:name).each{|r| puts r[:name]}

Sequel は map メソッドのような、データセットからデータを引き出すための便利なメソッドも提供します:

middle_east.map(:name) #=> ['Egypt', 'Turkey', 'Israel', ...]

また、 to_hash でハッシュとして結果を得ることができます。一つのカラムはキー、他は値として:

middle_east.to_hash(:name, :area) #=> {'Israel' => 20000, 'Turkey' => 120000, ...}

データベースに接続するには、単純に Sequel.connect に URL を与えます:

require 'sequel'
DB = Sequel.connect('sqlite://blog.db')

接続 URL はユーザー名、パスワード、ポートのようなものも含みます:

DB = Sequel.connect('postgres://user:password@host:port/database_name')

コネクションプールの大きさや SQL クエリを記録するためのロガーのような、オプションのパラメータを記述することもできます:

DB = Sequel.connect("postgres://user:password@host:port/database_name",
  :max_connections => 10, :logger => Logger.new('log/db.log'))

接続にブロックを記述できます。ブロックが終了するとデータベースから切断されます:

Sequel.connect('postgres://user:password@host:port/database_name'){|db| db[:posts].delete}

Sequel のドキュメント全体にわたって、生成された Sequel::Database インスタンスを参照するために使用される DB 定数を見ることでしょう。これは app でひとつの Sequel::Database インスタンスを推奨することを反映しています。Sequel の慣習は DB 定数にインスタンスを格納することです。これは慣習で必須ではありませんが、推奨します。

注意: Sequel を使用するいくつかのフレームワークは、あなたのために Sequel::Database インスタンスを作成し、それにアクセスする方法をあなたは知らないかもしれません。多くの場合、<tt>Sequel::Model.db<tt> を通して Sequel::Database インスタンスにアクセスできます。

Database#run を使用して自由に SQL コードを実行できます:

DB.run("create table t (a text, b text)")
DB.run("insert into t values ('a', 'b')")

生 SQL を元にデータセットを生成することもできます:

dataset = DB['select id from items']
dataset.count # 結果セットのレコード数を返します
dataset.map(:id) # 結果セット中の全 id カラムの値から成る配列を返します

データセットを通して生 SQL でレコードを取り出すこともできます:

DB['select * from items'].each do |row|
  p row
end

さらに SQL 文字列中でプレースホルダを使用できます:

name = 'Jim'
DB['select * from items where name = ?', name].each do |row|
  p row
end

データセットはレコードを取り出して操作する主要な方法です。これは通常は Database#from または Database#[] メソッド経由で生成されます:

posts = DB.from(:posts)
posts = DB[:posts] # same

データセットはあなたが伝えた時だけレコードを取り出します。レコードのフィルタ、順序の変更、テーブルの結合等の操作が可能です。

all メソッドを使用して全レコードを取り出すことができます:

posts.all
# SELECT * FROM posts

all メソッドはハッシュの配列を返します。各ハッシュはレコードに対応しています。

each を使用して1レコードずつ繰り返すこともできます:

posts.each{|row| p row}

さらに高度な方法を行えます:

names_and_dates = posts.map([:name, :date])
old_posts, recent_posts = posts.partition{|r| r[:date] < Date.today - 7}

データセットの最初のレコードを取り出すこともできます:

posts.first
# SELECT * FROM posts LIMIT 1

特定の値を持つ1レコードを取り出すこともできます:

posts[:id => 1]
# SELECT * FROM posts WHERE id = 1 LIMIT 1

データセットに順序がある場合、最後のレコードを問い合わせることができます:

posts.order(:stamp).last
# SELECT * FROM posts ORDER BY stamp DESC LIMIT 1

レコードをフィルタする簡単な方法は、適合する値のハッシュを where に与えることです:

my_posts = posts.where(:category => 'ruby', :author => 'david')
# WHERE category = 'ruby' AND author = 'david'

範囲も指定できます:

my_posts = posts.where(:stamp => (Date.today - 14)..(Date.today - 7))
# WHERE stamp >= '2010-06-30' AND stamp <= '2010-07-07'

値の配列も指定できます:

my_posts = posts.where(:category => ['ruby', 'postgres', 'linux'])
# WHERE category IN ('ruby', 'postgres', 'linux')

Sequel は式も受け付けます:

my_posts = posts.where{stamp > Date.today << 1}
# WHERE stamp > '2010-06-14'

アダプタによっては正規表現も指定することができます:

my_posts = posts.where(:category => /ruby/i)
# WHERE category ~* 'ruby'

exclude でフィルタを反転できます:

my_posts = posts.exclude(:category => ['ruby', 'postgres', 'linux'])
# WHERE category NOT IN ('ruby', 'postgres', 'linux')

文字列でカスタム WHERE 節も指定できます:

posts.where('stamp IS NOT NULL')
# WHERE stamp IS NOT NULL

文字列でパラメータを使用できます:

author_name = 'JKR'
posts.where('(stamp < ?) AND (author != ?)', Date.today - 3, author_name)
# WHERE (stamp < '2010-07-11') AND (author != 'JKR')

データセットはサブクエリとして使用することもできます:

DB[:items].where('price > ?', DB[:items].select{avg(price) + 100})
# WHERE price > (SELECT avg(price) + 100 FROM items)

フィルタ後、任意の取り出しメソッドを使用して適合したレコードを取り出すことができます:

my_posts.each{|row| p row}

詳細は Dataset Filtering を見てください。

セキュリティを念頭においてアプリケーションを設計することはベストプラクティスです。Sequel 使用時に注意すべきセキュリティ事項の詳細については Security Guide を読んでください。

count を使用して簡単にレコードを数えることができます:

posts.where(:category.like('%ruby%')).count
# SELECT COUNT(*) FROM posts WHERE category LIKE '%ruby%'

最大/最小値は maxmin で問い合わせできます:

max = DB[:history].max(:value)
# SELECT max(value) FROM history

min = DB[:history].min(:value)
# SELECT min(value) FROM history

合計や平均も sumavg で計算できます:

sum = DB[:items].sum(:price)
# SELECT sum(price) FROM items
avg = DB[:items].avg(:price)
# SELECT avg(price) FROM items

データセットを並び替えるには単純に order を使用します:

posts.order(:stamp)
# ORDER BY stamp
posts.order(:stamp, :name)
# ORDER BY stamp, name

order の連結は where と同じようには働きません:

posts.order(:stamp).order(:name)
# ORDER BY name

order_append メソッドを連結してこれを行えます:

posts.order(:stamp).order_append(:name)
# ORDER BY stamp, name

order_prepend メソッドも使用できます:

posts.order(:stamp).order_prepend(:name)
# ORDER BY name, stamp

降順を指定することもできます:

posts.reverse_order(:stamp)
# ORDER BY stamp DESC
posts.order(Sequel.desc(:stamp))
# ORDER BY stamp DESC

上の例の Sequel.desc(:stamp) の使用に着目してください。Sequel の DSL の多くはこのスタイル(SQL 式オブジェクトを返す Sequel モジュールのメソッド呼び出し)を使用します。Sequel は core_extensions extension) も提供します。これは Sequel の DSL をよりよく Ruby 言語に統合します。次のように書けます:

:stamp.desc

次の代わりに:

Sequel.desc(:stamp)

select を使用して簡単に返すカラムを選択できます:

posts.select(:stamp)
# SELECT stamp FROM posts
posts.select(:stamp, :name)
# SELECT stamp, name FROM posts

select の連結は where ではなく order のように働きます:

posts.select(:stamp).select(:name)
# SELECT name FROM posts

あなたの予想通り、order_append と同様の selectselect_append です:

posts.select(:stamp).select_append(:name)
# SELECT stamp, name FROM posts

テーブルからのレコードの削除は delete で行います:

posts.where('stamp < ?', Date.today - 3).delete
# DELETE FROM posts WHERE stamp < '2010-07-11'

削除はとても注意してください。delete はデータセット内の全ての行に影響します。まず where それから delete を呼んでください:

# DO THIS:
posts.where('stamp < ?', Date.today - 7).delete
# NOT THIS:
posts.delete.where('stamp < ?', Date.today - 7)

テーブルへのレコードの挿入は insert で行います:

posts.insert(:category => 'ruby', :author => 'david')
# INSERT INTO posts (category, author) VALUES ('ruby', 'david')

テーブル内のレコードの更新は update で行います:

posts.where('stamp < ?', Date.today - 7).update(:state => 'archived')
# UPDATE posts SET state = 'archived' WHERE stamp < '2010-07-07'

設定する値の選択にテーブルのカラムを参照できます:

posts.where{|o| o.stamp < Date.today - 7}.update(:backup_number => Sequel.+(:backup_number, 1))
# UPDATE posts SET backup_number = backup_number + 1 WHERE stamp < '2010-07-07'

delete と同様に update はデータセットの全ての行に影響するため、まず where そして update を行なってください:

# DO THIS:
posts.where('stamp < ?', Date.today - 7).update(:state => 'archived')
# NOT THIS:
posts.update(:state => 'archived').where('stamp < ?', Date.today - 7)

Database#transaction メソッドを使用してデータベーストランザクションでコードをラップすることができます:

DB.transaction do
  posts.insert(:category => 'ruby', :author => 'david')
  posts.where('stamp < ?', Date.today - 7).update(:state => 'archived')
end

ブロックが例外を上げなければトランザクションはコミットされます。ブロックが例外を上げた場合はトランザクションはロールバックされ、例外は再送出されます。トランザクションをロールバックしたいけどブロックの外に例外を上げたくない場合は、ブロック内で Sequel::Rollback 例外を上げればよいです:

DB.transaction do
  posts.insert(:category => 'ruby', :author => 'david')
  if posts.filter('stamp < ?', Date.today - 7).update(:state => 'archived') == 0
    raise Sequel::Rollback
  end
end

Sequel はテーブルを簡単に結合できます:

order_items = DB[:items].join(:order_items, :item_id => :id).
  where(:order_id => 1234)
# SELECT * FROM items INNER JOIN order_items
# ON order_items.item_id = items.id 
# WHERE order_id = 1234

ここで重要なことは、item_id は結合されるテーブルで自動的に修飾され、id は結合される最後のテーブルで自動的に修飾されることです。

それからデータセットと同様のことができます:

order_total = order_items.sum(:price)
# SELECT sum(price) FROM items INNER JOIN order_items
# ON order_items.item_id = items.id
# WHERE order_items.order_id = 1234

Sequel はカラム名がシンボルで記述されることを期待します。さらに、戻り値のハッシュのキーは常にシンボルです。これは多くの場合、リテラル値とカラム参照を自由に混ぜられます。たとえば、次の2行は同等の SQL を提供します:

items.where(:x => 1)
# SELECT * FROM items WHERE (x = 1)
items.where(1 => :x)
# SELECT * FROM items WHERE (1 = x)"

Ruby 文字列は通常 SQL 文字列として扱われます:

items.where(:x => 'x')
# SELECT * FROM items WHERE (x = 'x')

SQL の識別子はカラム、テーブル、スキーマの名前です。識別子は二重のアンダースコアを使用した特殊な表記法 :table__column で修飾できます:

items.literal(:items__price)
# items.price

カラムを修飾する他の方法は、Sequel.qualify メソッドを使用することです:

items.literal(Sequel.qualify(:items, :price))
# items.price

テーブル識別子でカラム識別子を修飾するのをさらに一般的にして、修飾されたテーブルから SELECT するために、スキーマ識別子でテーブル識別子を修飾できます:

posts = DB[:some_schema__posts]
# SELECT * FROM some_schema.posts

三重アンダースコアを使用した特殊な表記法 :column___alias または :table__column___alias で識別子をエイリアスできます:

items.literal(:price___p)
# price AS p
items.literal(:items__price___p)
# items.price AS p

カラムをエイリアスする別の方法は、Sequel.as メソッドを使用することです:

items.literal(Sequel.as(:price, :p))
# price AS p

Sequel.as メソッドを使用すると、識別子と異なり、任意の式をエイリアスできます:

items.literal(Sequel.as(DB[:posts].select{max(id)}, :p))
# (SELECT max(id) FROM posts) AS p

モデルクラスはデータセットをラップし、クラスのインスタンスはデータセットの1レコードをラップします。

モデルクラスは Sequel::Model を継承した通常の Ruby クラスとして定義されています:

DB = Sequel.connect('sqlite://blog.db')
class Post < Sequel::Model
end

モデルクラスが生成される時、データベースからテーブルのスキーマをパースし、自動的にテーブル内のすべてのカラムにアクセスできるメソッドをセットアップします(Sequel::Model はアクティブレコードパターンを実装します)。

Sequel モデルクラスは、クラス名をアンダースコアした複数形がテーブル名であるとみなします:

Post.table_name #=> :posts

テーブル名を明示的に設定できます(使用しているデータセットでも):

class Post < Sequel::Model(:my_posts)
end
# or:
Post.set_dataset :my_posts

シンボルを引数として set_dataset を呼び出すと、同じ名前のテーブルを参照しているとみなします。データセットを引数として呼び出すと、そのモデルに対するすべての検索のデフォルトとして設定できます:

Post.set_dataset DB[:my_posts].where(:category => 'ruby')
Post.set_dataset DB[:my_posts].select(:id, :name).order(:date)

モデルインスタンスはプライマリキーによって識別されます。多くの場合、Sequel はプライマリキーを決定するためにデータベースに問い合わせしますが、できない場合は :id をデフォルトとして使用します。Model.[] メソッドでプライマリキーを指定してレコードを取り出すことができます:

post = Post[123]

pk メソッドでレコードのプライマリキー値を取り出すことができます:

post.pk #=> 123

Sequel モデルはプライマリキーとして任意のカラムを使用できます。複数カラムから複合キーも作成できます:

class Post < Sequel::Model
  set_primary_key [:category, :title]
end

post = Post['ruby', 'hello world']
post.pk #=> ['ruby', 'hello world']

no_primary_key でプライマリキーを持たないモデルクラスも定義できます。しかし、簡単にレコードを更新したり削除する機能は失われます:

Post.no_primary_key

モデルインスタンスは条件を指定して取り出すこともできます:

post = Post[:title => 'hello world']
post = Post.first{num_comments < 10}

モデルクラスは多くのメソッドを下位のデータセットに転送します。これはほとんどの Dataset API を使用して、モデルインスタンスを返すカスタマイズされたクエリを生成できることを意味します。例えば:

Post.where(:category => 'ruby').each{|post| p post}

データセット内のレコードの操作もできます:

Post.where{num_comments < 7}.delete
Post.where(Sequel.like(:title, /ruby/)).update(:category => 'ruby')

モデルインスタンスはその値をカラムシンボルキーのハッシュとして格納します。ハッシュには values メソッドで直接アクセスできます:

post.values #=> {:id => 123, :category => 'ruby', :title => 'hello world'}

レコード値はオブジェクトの属性として読み込むことができます。属性名は、モデルのデータセット内の正当なカラム名とみなされます:

post.id #=> 123
post.title #=> 'hello world'

レコードの属性名がモデルのデータセット内の正当なカラムでない場合(計算された値のカラムを select_append で追加した場合等)、Model#[] で値にアクセスできます:

post[:id] #=> 123
post[:title] #=> 'hello world'

属性セッターまたは []= メソッドを使用してレコードの値を変更することもできます:

post.title = 'hey there'
post[:title] = 'hey there'

それはオブジェクトの値を変更するだけで、データベース内の行の更新は行いません。データベース行を更新するには、save メソッドを呼び出します:

post.save

mass-assignment メソッドのひとつを利用して、ひとつのメソッド呼び出しで複数カラムに値を設定することも可能です。詳細は mass assignment guide を見てください。たとえば、set はモデルのカラム値を保存せずに更新します:

post.set(:title=>'hey there', :updated_by=>'foo')

そして update はモデルのカラム値を更新し、それからデータベースに変更を保存します:

post.update(:title => 'hey there', :updated_by=>'foo')

新しいレコードは Model.create を呼び出すことで生成できます:

post = Post.create(:title => 'hello world')

他の方法は、新しいインスタンスを構成してあとで保存することです:

post = Post.new
post.title = 'hello world'
post.save

Model.newModel.create にブロックを与えることもできます:

post = Post.new do |p|
  p.title = 'hello world'
end

post = Post.create{|p| p.title = 'hello world'}

レコードの生成、更新、削除時にフックメソッドでカスタムコードを実行することができます。before_createafter_create フックメソッドはレコード生成をラップします。before_updateafter_update フックメソッドはレコード更新をラップします。before_saveafter_save フックメソッドはレコードの生成と更新をラップします。before_destroyafter_destroy フックメソッドは破壊をラップします。before_validationafter_validation フックメソッドはバリデーションをラップします。例:

class Post < Sequel::Model
  def after_create
    super
    author.increase_post_count
  end

  def after_destroy
    super
    author.decrease_post_count
  end
end

自前のフックメソッドを定義する場合は、super の使用に注意してください。ほとんど全ての Sequel::Model クラスメソッドとインスタンスメソッド(フックメソッド以外)は、安全に上書きできますが、それを行う際に super を確実に呼び出す必要があります。そうしないと、何かを壊すリスクがあります。

上の例では、可能であればデータベースのトリガーを使用すべきです。フックはデータの完全性のために使用できますが、モデルインスタンスを通じてデータベースを変更する時にその完全性を強制するだけです。それらはしばしば競合状態の影響を受けます。データベースのトリガーと制約を使って、データの完全性を強制するのがベストです。

delete または destroy を呼び出して個々のレコードを削除できます。この2つのメソッドの違いは、destroybefore_destroyafter_destroy フックメソッドを呼び出すことだけです。delete は行いません:

post.delete # => bypasses hooks
post.destroy # => runs hooks

レコードはモデルのデータセットで deletedestroy を呼び出すことで大量に削除することもできます。上述したように削除されるレコード用にフィルタを記述することもできます:

Post.where(:category => 32).delete # => bypasses hooks
Post.where(:category => 32).destroy # => runs hooks

destroy が呼び出された場合は各レコードが別々に削除されることに注意してください。delete はすべての適合したレコードを一つの SQL クエリで削除します。

関連はモデルクラス間の関係を指定するために使用されます。これはデータベース内のテーブル間の関係を反映します。通常は外部キーを使用して指定されます。many_to_one, one_to_one, one_to_many, many_to_many クラスメソッドでモデルの関連を指定します:

class Post < Sequel::Model
  many_to_one :author
  one_to_many :comments
  many_to_many :tags
end

many_to_oneone_to_one はモデルオブジェクトにゲッターとセッターを生成します:

post = Post.create(:name => 'hi!')
post.author = Author[:name => 'Sharon']
post.author

one_to_manymany_to_many はゲッターメソッドと、関連にオブジェクトを追加するメソッドと、関連からオブジェクトを削除するメソッドと、関連からすべての関連オブジェクトを削除するメソッドを生成します:

post = Post.create(:name => 'hi!')
post.comments

comment = Comment.create(:text=>'hi')
post.add_comment(comment)
post.remove_comment(comment)
post.remove_all_comments

tag = Tag.create(:tag=>'interesting')
post.add_tag(tag)
post.remove_tag(tag)
post.remove_all_tags

remove_* と remove_all_* メソッドはデータベースからオブジェクトを削除しないことに注意してください。これらはレシーバーから関連オブジェクトの関連を外すだけです。

すべての関連は dataset メソッドを追加します。これらは返されるオブジェクトのフィルタまたは並び替え、またはそれらすべてを変更するのに使用されます:

# この post のすべてのコメントをデータベースから削除します
post.comments_dataset.destroy

# この post に関連したすべてのタグで subscribers がないものを、タグの名前の順に返します
post.tags_dataset.where(:subscribers=>0).order(:name).all

関連は eager:eager 関連オプションで eagerly にロードされます。Eager ローディングはオブジェクトのグループをロードする時に使用されます。これはすべてのカレントオブジェクトのすべての関連オブジェクトを1クエリでロードします。各カレントオブジェクトの関連オブジェクトを得るために別々のクエリを使用するのではありません。Eager ローディングは all ですべてのモデルオブジェクトを一度に取り出します(each で個々にするのではなく)。Eager ローディングはカスケードして関連の関連オブジェクトをロードできます。

class Person < Sequel::Model
  one_to_many :posts, :eager=>[:tags]
end

class Post < Sequel::Model
  many_to_one :person
  one_to_many :replies
  many_to_many :tags
end

class Tag < Sequel::Model
  many_to_many :posts
  many_to_many :replies
end

class Reply < Sequel::Model
  many_to_one :person
  many_to_one :post
  many_to_many :tags
end

# .eager で Eager ローディングします
Post.eager(:person).all

# eager はデータセットメソッドなので、フィルタ/並び替え/件数制限等でも働きます
Post.where{topic > 'M'}.order(:date).limit(5).eager(:person).all

person = Person.first
# :eager で Eager ローディングします(この person の post から tag を eagerly ロードします)
person.posts

# この2つは同等です
Post.eager(:person, :tags).all
Post.eager(:person).eager(:tags).all

# .eager でカスケードしています
Tag.eager(:posts=>:replies).all

# すべての関連 post のタグを全取得します (:eager があるので)
Reply.eager(:person=>:posts).all

# 深さの制限なしで(メモリ/スタック以外には) post の tag を全取得します。
# すべての people、それらの post、それらの post の tag、それらの post への reply、
# 各 reply の person、各 reply の tag、それらの tag を持つすべての post と reply をロードします。
# 合計 8 クエリを使用します。
Person.eager(:posts=>{:replies=>[:person, {:tags=>[:posts, :replies]}]}).all

eager に加え eager_graph も使用できます。これはオブジェクトとすべての関連オブジェクトを得るために1クエリを使用します。これは関連テーブルのカラムを元にフィルタまたは並び替えをしたい場合に必要となります。これはカスケーディングでも働き、API はとても似ています。複数の *_to_many 関連を eagerly ロードするために eager_graph を使用すると、結果セットが cartesian product になることに注意してください。そのためこのケースでそれを使用する時はフィルタに充分注意するべきです。

proc を使用して eagerly ロードされたデータセットを動的にカスタマイズすることもできます。この proc は eager ローディングのために使用されるデータセットに渡され、データセットの変更された複製を返すべきです:

# Eagerly load only replies containing 'foo'
Post.eager(:replies=>proc{|ds| ds.where(Sequel.like(text, '%foo%'))}).all

これは eager_graph 使用時にも働きます。この場合 proc はデータセットを引数として、現在のデータセットに graph するために呼び出されます:

Post.eager_graph(:replies=>proc{|ds| ds.where(Sequel.like(text, '%foo%'))}).all

カスケード中でも eagereager_graph 用に eager ロードを動的にカスタマイズできます。この場合は、proc をキー、カスケードされた関連を値とした1つのエントリのハッシュを値とします:

# 'foo' を含んでいる reply と、これらの reply の person と tag だけを Eagerly ロードします
Post.eager(:replies=>{proc{|ds| ds.where(Sequel.like(text, '%foo%'))}=>[:person, :tags]}).all

テーブル共通のロジックを追加する推奨方法は、データセットで dataset_module を使用してメソッドを定義することです:

class Post < Sequel::Model
  dataset_module do
    def posts_with_few_comments
      where{num_comments < 30}
    end

    def clean_posts_with_few_comments
      posts_with_few_comments.delete
    end
  end
end

これはフィルタされたデータセットからあなたのモデル API にアクセスすることができます:

Post.where(:category => 'ruby').clean_posts_with_few_comments

Sequel モデルは subset クラスメソッドも提供します。これは簡単なフィルタを引数としたデータセットメソッドを生成します:

class Post < Sequel::Model
  subset(:posts_with_few_comments){num_comments < 30}
  subset :invisible, Sequel.~(:visible)
end

あなたのモデルに validate メソッドを定義することで、データベースにモデルを保存しようとする前に save がチェックします。モデルの属性が正当でない場合は、モデルオブジェクトの errors にその属性のエラーメッセージを追加すべきです。オブジェクトが validate メソッドによって追加された何かのエラーを持っている場合、save はエラーを発生するか、false を返します。この動きは raise_on_save_failure フラグで設定できます。

class Post < Sequel::Model
  def validate
    super
    errors.add(:name, "can't be empty") if name.empty?
    errors.add(:written_on, "should be in the past") if written_on >= Time.now
  end
end

About

Sequel: The Database Toolkit for Ruby

Resources

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Ruby 99.9%
  • CSS 0.1%