Guest User

Untitled

a guest
Apr 19th, 2018
84
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.56 KB | None | 0 0
  1. diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb
  2. index f9888c7..1e53b6b 100755
  3. --- a/activerecord/lib/active_record/validations.rb
  4. +++ b/activerecord/lib/active_record/validations.rb
  5. @@ -603,8 +603,8 @@ module ActiveRecord
  6. #
  7. # Configuration options:
  8. # * <tt>message</tt> - Specifies a custom error message (default is: "has already been taken")
  9. - # * <tt>scope</tt> - One or more columns by which to limit the scope of the uniquness constraint.
  10. - # * <tt>case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (true by default).
  11. + # * <tt>scope</tt> - One or more columns by which to limit the scope of the uniqueness constraint.
  12. + # * <tt>case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (false by default).
  13. # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false)
  14. # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false)
  15. # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
  16. @@ -614,14 +614,30 @@ module ActiveRecord
  17. # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
  18. # method, proc or string should return or evaluate to a true or false value.
  19. def validates_uniqueness_of(*attr_names)
  20. - configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken], :case_sensitive => true }
  21. + configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken] }
  22. configuration.update(attr_names.extract_options!)
  23.  
  24. validates_each(attr_names,configuration) do |record, attr_name, value|
  25. - if value.nil? || (configuration[:case_sensitive] || !columns_hash[attr_name.to_s].text?)
  26. + # The check for an existing value should be run from a class that
  27. + # isn't abstract. This means working down from the current class
  28. + # (self), to the first non-abstract class. Since classes don't know
  29. + # their subclasses, we have to build the hierarchy between self and
  30. + # the record's class.
  31. + class_hierarchy = [record.class]
  32. + while class_hierarchy.first != self
  33. + class_hierarchy.insert(0, class_hierarchy.first.superclass)
  34. + end
  35. +
  36. + # Now we can work our way down the tree to the first non-abstract
  37. + # class (which has a database table to query from).
  38. + finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
  39. +
  40. + if value.nil? || (configuration[:case_sensitive] || !finder_class.columns_hash[attr_name.to_s].text?)
  41. condition_sql = "#{record.class.quoted_table_name}.#{attr_name} #{attribute_condition(value)}"
  42. condition_params = [value]
  43. else
  44. + # sqlite has case sensitive SELECT query, while MySQL/Postgresql don't.
  45. + # Hence, this is needed only for sqlite.
  46. condition_sql = "LOWER(#{record.class.quoted_table_name}.#{attr_name}) #{attribute_condition(value)}"
  47. condition_params = [value.downcase]
  48. end
  49. @@ -639,23 +655,25 @@ module ActiveRecord
  50. condition_params << record.send(:id)
  51. end
  52.  
  53. - # The check for an existing value should be run from a class that
  54. - # isn't abstract. This means working down from the current class
  55. - # (self), to the first non-abstract class. Since classes don't know
  56. - # their subclasses, we have to build the hierarchy between self and
  57. - # the record's class.
  58. - class_hierarchy = [record.class]
  59. - while class_hierarchy.first != self
  60. - class_hierarchy.insert(0, class_hierarchy.first.superclass)
  61. - end
  62. -
  63. - # Now we can work our way down the tree to the first non-abstract
  64. - # class (which has a database table to query from).
  65. - finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
  66. -
  67. - if finder_class.find(:first, :conditions => [condition_sql, *condition_params])
  68. - record.errors.add(attr_name, configuration[:message])
  69. - end
  70. + results = connection.select_all(
  71. + construct_finder_sql(
  72. + :select => "#{attr_name}",
  73. + :from => "#{finder_class.quoted_table_name}",
  74. + :conditions => [condition_sql, *condition_params]
  75. + )
  76. + )
  77. +
  78. + unless results.length.zero?
  79. + found = true
  80. +
  81. + # As MySQL/Postgres don't have case sensitive SELECT queries, we try to find duplicate
  82. + # column in ruby when case sensitive option
  83. + if configuration[:case_sensitive] && finder_class.columns_hash[attr_name.to_s].text?
  84. + found = results.any? { |a| a[attr_name.to_s] == value }
  85. + end
  86. +
  87. + record.errors.add(attr_name, configuration[:message]) if found
  88. + end
  89. end
  90. end
  91.  
  92. diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
  93. index f9a1b46..30683ba 100755
  94. --- a/activerecord/test/cases/validations_test.rb
  95. +++ b/activerecord/test/cases/validations_test.rb
  96. @@ -437,6 +437,30 @@ class ValidationsTest < ActiveRecord::TestCase
  97. assert t2.save, "should save with nil"
  98. end
  99.  
  100. + def test_validate_case_sensitive_uniqueness
  101. + Topic.validates_uniqueness_of(:title, :case_sensitive => true, :allow_nil => true)
  102. +
  103. + t = Topic.new("title" => "I'm unique!")
  104. + assert t.save, "Should save t as unique"
  105. +
  106. + t.content = "Remaining unique"
  107. + assert t.save, "Should still save t as unique"
  108. +
  109. + t2 = Topic.new("title" => "I'M UNIQUE!")
  110. + assert t2.valid?, "Should be valid"
  111. + assert t2.save, "Should save t2 as unique"
  112. + assert !t2.errors.on(:title)
  113. + assert !t2.errors.on(:parent_id)
  114. + assert_not_equal "has already been taken", t2.errors.on(:title)
  115. +
  116. + t3 = Topic.new("title" => "I'M uNiQUe!")
  117. + assert t3.valid?, "Should be valid"
  118. + assert t3.save, "Should save t2 as unique"
  119. + assert !t3.errors.on(:title)
  120. + assert !t3.errors.on(:parent_id)
  121. + assert_not_equal "has already been taken", t3.errors.on(:title)
  122. + end
  123. +
  124. def test_validate_uniqueness_with_non_standard_table_names
  125. i1 = WarehouseThing.create(:value => 1000)
  126. assert !i1.valid?, "i1 should not be valid"
Add Comment
Please, Sign In to add comment