Uniqueness: Support UUID columns that end in "f"

This commit fixes the uniqueness matcher so that when qualified with
`scoped_to` and one of the scopes is a UUID column that happens to end
in an "f", the matcher is able to generate a proper next value during
testing and doesn't error.
This commit is contained in:
Elliot Winkler 2015-02-09 16:40:52 -07:00
parent 74fa081ef5
commit 81034eaf3d
3 changed files with 45 additions and 13 deletions

10
NEWS.md
View File

@ -21,6 +21,16 @@
* `set_session['key'].to(nil)` will no longer pass when the key in question
has not been set yet.
### Bug fixes
* So far the tests for the gem have been running against only SQLite. Now they
run against PostgreSQL, too. As a result we were able to fix some
Postgres-related bugs:
* Fix `validate_uniqueness_of` + `scoped_to` so that when one of the scope
attributes is a UUID column that ends in an "f", the matcher is able to
generate a proper "next" value without erroring.
# 2.8.0
### Deprecations

View File

@ -393,11 +393,14 @@ module Shoulda
end
def correct_type_for_column(column)
if column.type == :string
column = column_for(scope)
case column.type
when :string
'0'
elsif column.type == :datetime
when :datetime
DateTime.now
elsif column.type == :uuid
when :uuid
SecureRandom.uuid
else
0
@ -405,10 +408,14 @@ module Shoulda
end
def next_value_for(scope, previous_value)
if @subject.class.respond_to?(:defined_enums) && @subject.defined_enums[scope.to_s]
column = column_for(scope)
if column.type == :uuid
SecureRandom.uuid
elsif defined_as_enum?(scope)
available_values = available_enum_values_for(scope, previous_value)
available_values.keys.last
elsif scope.to_s =~ /_type$/ && model_class?(previous_value)
elsif polymorphic_type_attribute?(scope, previous_value)
Uniqueness::TestModels.create(previous_value).to_s
elsif previous_value.respond_to?(:next)
previous_value.next
@ -419,16 +426,21 @@ module Shoulda
end
end
def defined_as_enum?(scope)
@subject.class.respond_to?(:defined_enums) &&
@subject.defined_enums[scope.to_s]
end
def polymorphic_type_attribute?(scope, previous_value)
scope.to_s =~ /_type$/ && model_class?(previous_value)
end
def available_enum_values_for(scope, previous_value)
@subject.defined_enums[scope.to_s].reject do |key, _|
key == previous_value
end
end
def class_name
@subject.class.name
end
def existing_value
value = existing_record.__send__(@attribute)
if @options[:case_insensitive] && value.respond_to?(:swapcase!)
@ -436,6 +448,14 @@ module Shoulda
end
value
end
def class_name
@subject.class.name
end
def column_for(scope)
@subject.class.columns_hash[scope.to_s]
end
end
end
end

View File

@ -35,8 +35,8 @@ describe Shoulda::Matchers::ActiveRecord::ValidateUniquenessOfMatcher, type: :mo
context "when more than one record exists that has the next version of the attribute's value" do
it 'accepts' do
value1 = dummy_value_for(value_type)
value2 = next_version_of(value1)
value3 = next_version_of(value2)
value2 = next_version_of(value1, value_type)
value3 = next_version_of(value2, value_type)
model = define_model_validating_uniqueness(
scopes: [ build_attribute(name: :scope) ],
)
@ -633,8 +633,10 @@ describe Shoulda::Matchers::ActiveRecord::ValidateUniquenessOfMatcher, type: :mo
end
end
def next_version_of(value)
if value.is_a?(Time)
def next_version_of(value, value_type)
if value_type == :uuid
SecureRandom.uuid
elsif value_type == :time
value + 1
elsif value.respond_to?(:next)
value.next