Skip to content

Commit

Permalink
Make class MyModel < Sequel::Model(DB[:table]) reload safe
Browse files Browse the repository at this point in the history
The reason this didn't work before is because Dataset did not
implement an appropriate hash or == method.  This commit does
that.  Both == and hash check the database, options, and SQL
for the dataset.  I was originally planning on having them
check the row_proc, but that wouldn't work correctly with the
existing implementation of Sequel::Model().
  • Loading branch information
jeremyevans committed Aug 31, 2010
1 parent 465f52e commit 6d12337
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 7 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG
@@ -1,5 +1,9 @@
=== HEAD

* Make class MyModel < Sequel::Model(DB[:table]) reload safe (jeremyevans)

* Fix a possible error when using the do (DataObjects) adapter with postgres (jeremyevans)

* Handle a many_to_many :join_table option that uses an implicit alias (mluu, jeremyevans)

* Work around bug in Microsoft's SQL Server JDBC Adapter version 3.0 (jfirebaugh)
Expand Down
17 changes: 17 additions & 0 deletions lib/sequel/dataset/misc.rb
Expand Up @@ -34,6 +34,17 @@ def initialize(db, opts = nil)
@row_proc = nil
end

# Define a hash value such that datasets with the same DB, opts, and SQL
# will be consider equal.
def ==(o)
o.is_a?(self.class) && db == o.db && opts == o.opts && sql == o.sql
end

# Alias for ==
def eql?(o)
self == o
end

# Return the dataset as an aliased expression with the given alias. You can
# use this as a FROM or JOIN dataset, or as a column if this dataset
# returns a single row and column.
Expand Down Expand Up @@ -105,6 +116,12 @@ def first_source_table
s
end
end

# Define a hash value such that datasets with the same DB, opts, and SQL
# will have the same hash value
def hash
[db, opts.sort_by{|k| k.to_s}, sql].hash
end

# Returns a string representation of the dataset including the class name
# and the corresponding SQL select statement.
Expand Down
54 changes: 53 additions & 1 deletion spec/core/dataset_spec.rb
Expand Up @@ -113,7 +113,7 @@
@dataset.row_proc = Proc.new{|r| r}
@clone = @dataset.clone

@clone.should_not === @dataset
@clone.object_id.should_not === @dataset.object_id
@clone.class.should == @dataset.class
@clone.opts.should == @dataset.opts
@clone.row_proc.should == @dataset.row_proc
Expand Down Expand Up @@ -159,6 +159,58 @@ def __xyz__; "xyz"; end
end
end

context "Dataset#==" do
before do
@db = MockDatabase.new
@h = {}
end

specify "should be the true for dataset with the same db, opts, and SQL" do
@db[:t].should == @db[:t]
end

specify "should be different for datasets with different dbs" do
@db[:t].should_not == MockDatabase.new[:t]
end

specify "should be different for datasets with different opts" do
@db[:t].should_not == @db[:t].clone(:blah=>1)
end

specify "should be different for datasets with different SQL" do
ds = @db[:t]
ds.quote_identifiers = true
ds.should_not == @db[:t]
end
end

context "Dataset#hash" do
before do
@db = MockDatabase.new
@h = {}
end

specify "should be the same for dataset with the same db, opts, and SQL" do
@db[:t].hash.should == @db[:t].hash
@h[@db[:t]] = 1
@h[@db[:t]].should == 1
end

specify "should be different for datasets with different dbs" do
@db[:t].hash.should_not == MockDatabase.new[:t].hash
end

specify "should be different for datasets with different opts" do
@db[:t].hash.should_not == @db[:t].clone(:blah=>1).hash
end

specify "should be different for datasets with different SQL" do
ds = @db[:t]
ds.quote_identifiers = true
ds.hash.should_not == @db[:t].hash
end
end

context "A simple dataset" do
before do
@dataset = Sequel::Dataset.new(nil).from(:test)
Expand Down
12 changes: 6 additions & 6 deletions spec/model/base_spec.rb
Expand Up @@ -408,18 +408,18 @@ def refresh
end

it "should depend on whether the dataset provides an accurate number of rows matched by default" do
Class.new(Sequel::Model(@ds1)).require_modification.should == false
Class.new(Sequel::Model(@ds2)).require_modification.should == true
Class.new(Sequel::Model).set_dataset(@ds1).require_modification.should == false
Class.new(Sequel::Model).set_dataset(@ds2).require_modification.should == true
end

it "should obey global setting regardless of dataset support if set" do
Sequel::Model.require_modification = true
Class.new(Sequel::Model(@ds1)).require_modification.should == true
Class.new(Sequel::Model(@ds2)).require_modification.should == true
Class.new(Sequel::Model).set_dataset(@ds1).require_modification.should == true
Class.new(Sequel::Model).set_dataset(@ds2).require_modification.should == true

Sequel::Model.require_modification = false
Class.new(Sequel::Model(@ds1)).require_modification.should == false
Class.new(Sequel::Model(@ds2)).require_modification.should == false
Class.new(Sequel::Model).set_dataset(@ds1).require_modification.should == false
Class.new(Sequel::Model).set_dataset(@ds1).require_modification.should == false
end
end

Expand Down
27 changes: 27 additions & 0 deletions spec/model/model_spec.rb
Expand Up @@ -30,6 +30,33 @@ class SmBlahTest < c
SmBlahTest.db.should == db
SmBlahTest.table_name.should == :sm_blah_tests
end

describe "reloading" do
after do
Object.send(:remove_const, :Album) if defined?(::Album)
end

it "should work without raising an exception with a symbol" do
proc do
class ::Album < Sequel::Model(:table); end
class ::Album < Sequel::Model(:table); end
end.should_not raise_error
end

it "should work without raising an exception with a database" do
proc do
class ::Album < Sequel::Model(@db); end
class ::Album < Sequel::Model(@db); end
end.should_not raise_error
end

it "should work without raising an exception with a dataset" do
proc do
class ::Album < Sequel::Model(@db[:table]); end
class ::Album < Sequel::Model(@db[:table]); end
end.should_not raise_error
end
end
end

describe Sequel::Model do
Expand Down

0 comments on commit 6d12337

Please sign in to comment.