aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record
diff options
context:
space:
mode:
authorCarlos Antonio da Silva <carlosantoniodasilva@gmail.com>2012-12-18 09:08:11 -0200
committerCarlos Antonio da Silva <carlosantoniodasilva@gmail.com>2012-12-18 09:08:11 -0200
commitf6d0eda918756bf56e2c05afcaaabda723487017 (patch)
tree35974c37f5ff4cf9dbab0728716ccaff6f41e3b5 /activerecord/lib/active_record
parentc3e573db54c55c4e4ac694940c6d2f5b42b6155b (diff)
parent0bb15174558fdeaa0be1889a94347eacfc605f0b (diff)
downloadrails-f6d0eda918756bf56e2c05afcaaabda723487017.tar.gz
rails-f6d0eda918756bf56e2c05afcaaabda723487017.tar.bz2
rails-f6d0eda918756bf56e2c05afcaaabda723487017.zip
Merge branch 'uniqueness-validation-postgresql-arrays'
When setting an array column (or other PostgreSQL-specific data type) to be validated for uniqueness, the resulting SQL query is generated with syntax errors, like this: SELECT 1 AS one FROM "postgresql_arrays" WHERE "postgresql_arrays"."nicknames" = '["john", "johnny"]' LIMIT 1 This happens because Ruby Arrays must be type-casted to their PostgreSQL equivalent. This small patch fixes this issue, calling the adapter's type_cast method. Closes #8077
Diffstat (limited to 'activerecord/lib/active_record')
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb65
1 files changed, 33 insertions, 32 deletions
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index 5fa6a0b892..1427189851 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -1,10 +1,8 @@
-require 'active_support/core_ext/array/prepend_and_append'
-
module ActiveRecord
module Validations
class UniquenessValidator < ActiveModel::EachValidator # :nodoc:
def initialize(options)
- super(options.reverse_merge(:case_sensitive => true))
+ super({ case_sensitive: true }.merge!(options))
end
# Unfortunately, we have to tie Uniqueness validators to a class.
@@ -15,35 +13,19 @@ module ActiveRecord
def validate_each(record, attribute, value)
finder_class = find_finder_class_for(record)
table = finder_class.arel_table
-
- coder = record.class.serialized_attributes[attribute.to_s]
-
- if value && coder
- value = coder.dump value
- end
+ value = deserialize_attribute(record, attribute, value)
relation = build_relation(finder_class, table, attribute, value)
- relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
-
- Array(options[:scope]).each do |scope_item|
- reflection = record.class.reflect_on_association(scope_item)
- if reflection
- scope_value = record.send(reflection.foreign_key)
- scope_item = reflection.foreign_key
- else
- scope_value = record.read_attribute(scope_item)
- end
- relation = relation.and(table[scope_item].eq(scope_value))
- end
-
+ relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
+ relation = scope_relation(record, table, relation)
relation = finder_class.unscoped.where(relation)
-
- if options[:conditions]
- relation = relation.merge(options[:conditions])
- end
+ relation.merge!(options[:conditions]) if options[:conditions]
if relation.exists?
- record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope, :conditions).merge(:value => value))
+ error_options = options.except(:case_sensitive, :scope, :conditions)
+ error_options[:value] = value
+
+ record.errors.add(attribute, :taken, error_options)
end
end
@@ -58,7 +40,7 @@ module ActiveRecord
class_hierarchy = [record.class]
while class_hierarchy.first != @klass
- class_hierarchy.prepend(class_hierarchy.first.superclass)
+ class_hierarchy.unshift(class_hierarchy.first.superclass)
end
class_hierarchy.detect { |klass| !klass.abstract_class? }
@@ -71,18 +53,37 @@ module ActiveRecord
end
column = klass.columns_hash[attribute.to_s]
- value = column.limit ? value.to_s[0, column.limit] : value.to_s if !value.nil? && column.text?
+ value = klass.connection.type_cast(value, column)
+ value = value.to_s[0, column.limit] if value && column.limit && column.text?
if !options[:case_sensitive] && value && column.text?
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
- relation = klass.connection.case_insensitive_comparison(table, attribute, column, value)
+ klass.connection.case_insensitive_comparison(table, attribute, column, value)
else
- value = klass.connection.case_sensitive_modifier(value) unless value.nil?
- relation = table[attribute].eq(value)
+ value = klass.connection.case_sensitive_modifier(value) unless value.nil?
+ table[attribute].eq(value)
+ end
+ end
+
+ def scope_relation(record, table, relation)
+ Array(options[:scope]).each do |scope_item|
+ if reflection = record.class.reflect_on_association(scope_item)
+ scope_value = record.send(reflection.foreign_key)
+ scope_item = reflection.foreign_key
+ else
+ scope_value = record.read_attribute(scope_item)
+ end
+ relation = relation.and(table[scope_item].eq(scope_value))
end
relation
end
+
+ def deserialize_attribute(record, attribute, value)
+ coder = record.class.serialized_attributes[attribute.to_s]
+ value = coder.dump value if value && coder
+ value
+ end
end
module ClassMethods