Skip to content

Commit 789a8f2

Browse files
authored
Merge pull request #217 from koic/fix_a_false_positive_for_unique_validation_without_index
[Fix #215] Fix a false positive for `Rails/UniqueValidationWithoutIndex`
2 parents e952888 + ac766e3 commit 789a8f2

File tree

3 files changed

+54
-3
lines changed

3 files changed

+54
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Bug fixes
66

77
* [#213](https://github.com/rubocop-hq/rubocop-rails/pull/213): Fix a false positive for `Rails/UniqueValidationWithoutIndex` when using conditions. ([@sunny][])
8+
* [#215](https://github.com/rubocop-hq/rubocop-rails/issues/215): Fix a false positive for `Rails/UniqueValidationWithoutIndex` when using Expression Indexes. ([@koic][])
89

910
## 2.5.0 (2020-03-24)
1011

lib/rubocop/cop/rails/unique_validation_without_index.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class UniqueValidationWithoutIndex < Cop
3232
def on_send(node)
3333
return unless node.method?(:validates)
3434
return unless uniqueness_part(node)
35+
return if condition_part?(node)
3536
return unless schema
3637
return if with_index?(node)
3738

@@ -47,13 +48,21 @@ def with_index?(node)
4748
table = schema.table_by(name: table_name(klass))
4849
return true unless table # Skip analysis if it can't find the table
4950

50-
return true if condition_part?(node)
51-
5251
names = column_names(node)
5352
return true unless names
5453

5554
table.indices.any? do |index|
56-
index.unique && index.columns.to_set == names
55+
index.unique &&
56+
(index.columns.to_set == names ||
57+
include_column_names_in_expression_index?(index, names))
58+
end
59+
end
60+
61+
def include_column_names_in_expression_index?(index, column_names)
62+
return false unless (expression_index = index.expression)
63+
64+
column_names.all? do |column_name|
65+
expression_index.include?(column_name)
5766
end
5867
end
5968

spec/rubocop/cop/rails/unique_validation_without_index_spec.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,5 +406,46 @@ class User
406406
RUBY
407407
end
408408
end
409+
410+
context 'with expression indexes' do
411+
context 'when column name is included in expression index' do
412+
let(:schema) { <<~RUBY }
413+
ActiveRecord::Schema.define(version: 2020_02_02_075409) do
414+
create_table 'emails', force: :cascade do |t|
415+
t.string 'address', null: false
416+
t.index 'lower(address)', name: 'index_emails_on_lower_address', unique: true
417+
end
418+
end
419+
RUBY
420+
421+
it 'does not register an offense' do
422+
expect_no_offenses(<<~RUBY)
423+
class Email < ApplicationRecord
424+
validates :address, presence: true, uniqueness: { case_sensitive: false }, email: true
425+
end
426+
RUBY
427+
end
428+
end
429+
430+
context 'when column name is not included in expression index' do
431+
let(:schema) { <<~RUBY }
432+
ActiveRecord::Schema.define(version: 2020_02_02_075409) do
433+
create_table 'emails', force: :cascade do |t|
434+
t.string 'address', null: false
435+
t.index 'lower(unexpected_column_name)', name: 'index_emails_on_lower_address', unique: true
436+
end
437+
end
438+
RUBY
439+
440+
it 'registers an offense' do
441+
expect_offense(<<~RUBY)
442+
class Email < ApplicationRecord
443+
validates :address, presence: true, uniqueness: { case_sensitive: false }, email: true
444+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Uniqueness validation should be with a unique index.
445+
end
446+
RUBY
447+
end
448+
end
449+
end
409450
end
410451
end

0 commit comments

Comments
 (0)