Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add :only_if_modified option to validates_unique method in validation…

…_helpers plugin

Using this option should speed up the common case where the unique
attribute is not modified for an existing record.  It is not on by
default, since the following is possible:

  album = Album[1]
  album.name # 'YJM'
  # Database is modified outside application:
  #   UPDATE albums SET name ='BB' WHERE id = 1
  #   UPDATE albums SET name = 'YJM' WHERE id = 2

  # Would violate uniqueness constraint by setting album 1's
  # name back to 'YJM'
  album.save
  • Loading branch information...
commit a52365582a3eeb679fc96336b42421d0c165554e 1 parent ae8ed72
Jeremy Evans authored
2  CHANGELOG
View
@@ -1,5 +1,7 @@
=== HEAD
+* Add :only_if_modified option to validates_unique method in validation_helpers plugin (jeremyevans)
+
* Allow specifying the dataset alias via :alias option when using union/intersect/except (jeremyevans)
* Allow Model#destroy to take an options hash and respect a :transaction option (john_firebaugh)
9 lib/sequel/plugins/validation_helpers.rb
View
@@ -153,6 +153,8 @@ def validates_presence(atts, opts={})
#
# Possible Options:
# * :message - The message to use (default: 'is already taken')
+ # * :only_if_modified - Only check the uniqueness if the object is new or
+ # one of the columns has been modified.
def validates_unique(*atts)
opts = default_validation_helpers_options(:unique)
if atts.last.is_a?(Hash)
@@ -160,9 +162,12 @@ def validates_unique(*atts)
end
message = validation_error_message(opts[:message])
atts.each do |a|
- ds = model.filter(Array(a).map{|x| [x, send(x)]})
+ arr = Array(a)
+ next if opts[:only_if_modified] && !new? && !arr.any?{|x| changed_columns.include?(x)}
+ ds = model.filter(arr.map{|x| [x, send(x)]})
ds = yield(ds) if block_given?
- errors.add(a, message) unless (new? ? ds : ds.exclude(pk_hash)).count == 0
+ ds = ds.exclude(pk_hash) unless new?
+ errors.add(a, message) unless ds.count == 0
end
end
35 spec/extensions/validation_helpers_spec.rb
View
@@ -377,4 +377,39 @@ def fetch_rows (sql)
MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND active) LIMIT 1",
"SELECT COUNT(*) AS count FROM items WHERE (((username = '0records') AND active) AND (id != 3)) LIMIT 1"]
end
+
+ it "should support :only_if_modified option for validates_unique, and not check uniqueness for existing records if values haven't changed" do
+ @c.columns(:id, :username, :password)
+ @c.set_dataset MODEL_DB[:items]
+ @c.set_validations{validates_unique([:username, :password], :only_if_modified=>true)}
+
+ @c.dataset.extend(Module.new {
+ def fetch_rows (sql)
+ @db << sql
+ yield({:v => 0})
+ end
+ })
+
+ MODEL_DB.reset
+ @c.new(:username => "0records", :password => "anothertest").should be_valid
+ MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE ((username = '0records') AND (password = 'anothertest')) LIMIT 1"]
+ MODEL_DB.reset
+ m = @c.load(:id=>3, :username => "0records", :password => "anothertest")
+ m.should be_valid
+ MODEL_DB.sqls.should == []
+
+ m.username = '1'
+ m.should be_valid
+ MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE (((username = '1') AND (password = 'anothertest')) AND (id != 3)) LIMIT 1"]
+
+ m = @c.load(:id=>3, :username => "0records", :password => "anothertest")
+ MODEL_DB.reset
+ m.password = '1'
+ m.should be_valid
+ MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE (((username = '0records') AND (password = '1')) AND (id != 3)) LIMIT 1"]
+ MODEL_DB.reset
+ m.username = '2'
+ m.should be_valid
+ MODEL_DB.sqls.should == ["SELECT COUNT(*) AS count FROM items WHERE (((username = '2') AND (password = '1')) AND (id != 3)) LIMIT 1"]
+ end
end
Please sign in to comment.
Something went wrong with that request. Please try again.