2014-01-23 18:07:36 +00:00
|
|
|
module Shoulda
|
2010-12-15 22:34:19 +00:00
|
|
|
module Matchers
|
2014-01-23 18:07:36 +00:00
|
|
|
module ActiveRecord
|
|
|
|
# The `have_db_index` matcher tests that the table that backs your model
|
|
|
|
# has a index on a specific column.
|
|
|
|
#
|
|
|
|
# class CreateBlogs < ActiveRecord::Migration
|
|
|
|
# def change
|
|
|
|
# create_table :blogs do |t|
|
|
|
|
# t.integer :user_id
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# add_index :blogs, :user_id
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Blog do
|
|
|
|
# it { should have_db_index(:user_id) }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # Test::Unit
|
|
|
|
# class BlogTest < ActiveSupport::TestCase
|
|
|
|
# should have_db_index(:user_id)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# #### Qualifiers
|
|
|
|
#
|
|
|
|
# ##### unique
|
|
|
|
#
|
|
|
|
# Use `unique` to assert that the index is unique.
|
|
|
|
#
|
|
|
|
# class CreateBlogs < ActiveRecord::Migration
|
|
|
|
# def change
|
|
|
|
# create_table :blogs do |t|
|
|
|
|
# t.string :name
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# add_index :blogs, :name, unique: true
|
|
|
|
# end
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # RSpec
|
|
|
|
# describe Blog do
|
|
|
|
# it { should have_db_index(:name).unique(true) }
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# # Test::Unit
|
|
|
|
# class BlogTest < ActiveSupport::TestCase
|
|
|
|
# should have_db_index(:name).unique(true)
|
|
|
|
# end
|
|
|
|
#
|
|
|
|
# Since it only ever makes since for `unique` to be `true`, you can also
|
|
|
|
# leave off the argument to save some keystrokes:
|
2010-12-15 22:34:19 +00:00
|
|
|
#
|
2014-01-23 18:07:36 +00:00
|
|
|
# # RSpec
|
|
|
|
# describe Blog do
|
|
|
|
# it { should have_db_index(:name).unique }
|
|
|
|
# end
|
2010-12-15 22:34:19 +00:00
|
|
|
#
|
2014-01-23 18:07:36 +00:00
|
|
|
# # Test::Unit
|
|
|
|
# class BlogTest < ActiveSupport::TestCase
|
|
|
|
# should have_db_index(:name).unique
|
|
|
|
# end
|
2010-12-15 22:34:19 +00:00
|
|
|
#
|
2014-01-23 18:07:36 +00:00
|
|
|
# @return [HaveDbIndexMatcher]
|
2010-12-15 22:34:19 +00:00
|
|
|
#
|
|
|
|
def have_db_index(columns)
|
2012-03-30 15:36:04 +00:00
|
|
|
HaveDbIndexMatcher.new(columns)
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
2014-01-23 18:07:36 +00:00
|
|
|
# @private
|
|
|
|
class HaveDbIndexMatcher
|
2012-03-30 15:36:04 +00:00
|
|
|
def initialize(columns)
|
2010-12-15 22:34:19 +00:00
|
|
|
@columns = normalize_columns_to_array(columns)
|
2012-04-23 21:37:40 +00:00
|
|
|
@options = {}
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
2014-03-04 12:47:16 +00:00
|
|
|
def unique(unique = true)
|
2012-04-23 21:37:40 +00:00
|
|
|
@options[:unique] = unique
|
2010-12-15 22:34:19 +00:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def matches?(subject)
|
|
|
|
@subject = subject
|
|
|
|
index_exists? && correct_unique?
|
|
|
|
end
|
|
|
|
|
2013-12-24 11:24:27 +00:00
|
|
|
def failure_message
|
2010-12-15 22:34:19 +00:00
|
|
|
"Expected #{expectation} (#{@missing})"
|
|
|
|
end
|
|
|
|
|
2013-12-24 11:24:27 +00:00
|
|
|
def failure_message_when_negated
|
2010-12-15 22:34:19 +00:00
|
|
|
"Did not expect #{expectation}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def description
|
2012-04-23 21:37:40 +00:00
|
|
|
if @options.key?(:unique)
|
|
|
|
"have a #{index_type} index on columns #{@columns.join(' and ')}"
|
|
|
|
else
|
|
|
|
"have an index on columns #{@columns.join(' and ')}"
|
|
|
|
end
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
protected
|
|
|
|
|
|
|
|
def index_exists?
|
|
|
|
! matched_index.nil?
|
|
|
|
end
|
|
|
|
|
|
|
|
def correct_unique?
|
2012-04-23 21:37:40 +00:00
|
|
|
return true unless @options.key?(:unique)
|
|
|
|
|
2012-09-19 07:40:57 +00:00
|
|
|
is_unique = matched_index.unique
|
|
|
|
|
|
|
|
is_unique = !is_unique unless @options[:unique]
|
|
|
|
|
|
|
|
unless is_unique
|
2010-12-15 22:34:19 +00:00
|
|
|
@missing = "#{table_name} has an index named #{matched_index.name} " <<
|
2012-09-19 07:40:57 +00:00
|
|
|
"of unique #{matched_index.unique}, not #{@options[:unique]}."
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
2012-09-19 07:40:57 +00:00
|
|
|
|
|
|
|
is_unique
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def matched_index
|
|
|
|
indexes.detect { |each| each.columns == @columns }
|
|
|
|
end
|
|
|
|
|
|
|
|
def model_class
|
|
|
|
@subject.class
|
|
|
|
end
|
|
|
|
|
|
|
|
def table_name
|
|
|
|
model_class.table_name
|
|
|
|
end
|
|
|
|
|
|
|
|
def indexes
|
|
|
|
::ActiveRecord::Base.connection.indexes(table_name)
|
|
|
|
end
|
|
|
|
|
|
|
|
def expectation
|
2012-03-30 15:22:31 +00:00
|
|
|
"#{model_class.name} to #{description}"
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def index_type
|
2012-04-23 21:37:40 +00:00
|
|
|
if @options[:unique]
|
2010-12-15 22:34:19 +00:00
|
|
|
'unique'
|
2012-04-23 21:37:40 +00:00
|
|
|
else
|
|
|
|
'non-unique'
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def normalize_columns_to_array(columns)
|
2012-04-10 01:03:19 +00:00
|
|
|
Array.wrap(columns).map(&:to_s)
|
2010-12-15 22:34:19 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|