-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add null_dataset extension, for creating a dataset that never issues …
…a database query This is another feature that ActiveRecord will be adding in 4.0. Basically, it's the null object pattern for datasets, so you can create a dataset that never returns any rows (or issues other queries). This is easily implemented as an extension, where Dataset#nullify just extends the cloned dataset with a module. Note that there may be corner cases on some adapters when using non-standard dataset methods. Those can be dealt with on a case by case basis.
- Loading branch information
1 parent
9a7ae22
commit e1e3207
Showing
5 changed files
with
179 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# The null_dataset extension adds the Dataset#nullify method, which | ||
# returns a cloned dataset that will never issue a query to the | ||
# database. It implements the null object pattern for datasets. | ||
# | ||
# The most common usage is probably in a method that must return | ||
# a dataset, where the method knows the dataset shouldn't return | ||
# anything. With standard Sequel, you'd probably just add a | ||
# WHERE condition that is always false, but that still results | ||
# in a query being sent to the database, and can be overridden | ||
# using #unfiltered, the OR operator, or a UNION. | ||
# | ||
# Usage: | ||
# | ||
# ds = DB[;items].nullify.where(:a=>:b).select(:c) | ||
# ds.sql # => "SELECT c FROM items WHERE (a = b)" | ||
# ds.all # => [] # no query sent to the database | ||
# | ||
# Note that there is one case where a null dataset will sent | ||
# a query to the database. If you call #columns on a nulled | ||
# dataset and the dataset doesn't have an already cached | ||
# version of the columns, it will create a new dataset with | ||
# the same options to get the columns. | ||
|
||
module Sequel | ||
class Dataset | ||
module NullDataset | ||
# Create a new dataset from the dataset (which won't | ||
# be nulled) to get the columns if they aren't already cached. | ||
def columns | ||
@columns ||= db.dataset(@opts).columns | ||
end | ||
|
||
# Return 0 without sending a database query. | ||
def delete | ||
0 | ||
end | ||
|
||
# Return self without sending a database query, never yielding. | ||
def each | ||
self | ||
end | ||
|
||
# Return nil without sending a database query, never yielding. | ||
def fetch_rows(sql) | ||
nil | ||
end | ||
|
||
# Return nil without sending a database query. | ||
def insert(*) | ||
nil | ||
end | ||
|
||
# Return nil without sending a database query. | ||
def truncate | ||
nil | ||
end | ||
|
||
# Return 0 without sending a database query. | ||
def update(v={}) | ||
0 | ||
end | ||
|
||
protected | ||
|
||
# Return nil without sending a database query. | ||
def _import(columns, values, opts) | ||
nil | ||
end | ||
|
||
private | ||
|
||
# Just in case these are called directly by some internal code, | ||
# make them noops. There's nothing we can do if the db | ||
# is accessed directly to make a change, though. | ||
(%w'_ddl _dui _insert' << '').each do |m| | ||
class_eval("private; def execute#{m}(sql, opts={}) end", __FILE__, __LINE__) | ||
end | ||
end | ||
|
||
# Return a cloned nullified dataset. | ||
def nullify | ||
clone.nullify! | ||
end | ||
|
||
# Nullify the current dataset | ||
def nullify! | ||
extend NullDataset | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper") | ||
|
||
describe "null_dataset extension" do | ||
before do | ||
@db = Sequel::mock(:fetch=>{:id=>1}, :autoid=>1, :numrows=>1, :columns=>[:id]) | ||
@ds = @db[:table].nullify | ||
@i = 0 | ||
@pr = proc{|*a| @i += 1} | ||
end | ||
after do | ||
@db.sqls.should == [] unless @skip_check | ||
end | ||
|
||
it "should make each be a noop" do | ||
@ds.each(&@pr).should equal(@ds) | ||
@i.should == 0 | ||
end | ||
|
||
it "should make fetch_rows be a noop" do | ||
@ds.fetch_rows("SELECT 1", &@pr).should == nil | ||
@i.should == 0 | ||
end | ||
|
||
it "should make insert be a noop" do | ||
@ds.insert(1).should == nil | ||
end | ||
|
||
it "should make update be a noop" do | ||
@ds.update(:a=>1).should == 0 | ||
end | ||
|
||
it "should make delete be a noop" do | ||
@ds.delete.should == 0 | ||
end | ||
|
||
it "should make truncate be a noop" do | ||
@ds.truncate.should == nil | ||
end | ||
|
||
it "should make execute_* be a noop" do | ||
@ds.send(:execute_ddl,'FOO').should == nil | ||
@ds.send(:execute_insert,'FOO').should == nil | ||
@ds.send(:execute_dui,'FOO').should == nil | ||
@ds.send(:execute,'FOO').should == nil | ||
end | ||
|
||
it "should have working columns" do | ||
@skip_check = true | ||
@ds.columns.should == [:id] | ||
@db.sqls.should == ['SELECT * FROM table LIMIT 1'] | ||
end | ||
|
||
it "should have count return 0" do | ||
@ds.count.should == 0 | ||
end | ||
|
||
it "should have empty return true" do | ||
@ds.empty?.should == true | ||
end | ||
|
||
it "should make import a noop" do | ||
@ds.import([:id], [[1], [2], [3]]).should == nil | ||
end | ||
|
||
it "should have nullify method returned modified receiver" do | ||
@skip_check = true | ||
ds = @db[:table] | ||
ds.nullify.should_not equal(ds) | ||
ds.each(&@pr) | ||
@db.sqls.should == ['SELECT * FROM table'] | ||
@i.should == 1 | ||
end | ||
|
||
it "should have null! method modify receiver" do | ||
ds = @db[:table] | ||
ds.nullify!.should equal(ds) | ||
ds.each(&@pr) | ||
@i.should == 0 | ||
end | ||
|
||
it "should work with method chaining" do | ||
@ds.where(:a=>1).select(:b).each(&@pr) | ||
@i.should == 0 | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters