Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add have_implicit_order_column matcher #1243

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/shoulda/matchers/active_record.rb
Expand Up @@ -14,6 +14,7 @@
require "shoulda/matchers/active_record/association_matchers/option_verifier"
require "shoulda/matchers/active_record/have_db_column_matcher"
require "shoulda/matchers/active_record/have_db_index_matcher"
require "shoulda/matchers/active_record/have_implicit_order_column"
require "shoulda/matchers/active_record/have_readonly_attribute_matcher"
require "shoulda/matchers/active_record/have_rich_text_matcher"
require "shoulda/matchers/active_record/have_secure_token_matcher"
Expand Down
106 changes: 106 additions & 0 deletions lib/shoulda/matchers/active_record/have_implicit_order_column.rb
@@ -0,0 +1,106 @@
module Shoulda
module Matchers
module ActiveRecord
# The `have_implicit_order_column` matcher tests that the model has `implicit_order_column`
# assigned to one of the table columns. (Rails 6+ only)
#
# class Product < ApplicationRecord
# self.implicit_order_column = :created_at
# end
#
# # RSpec
# RSpec.describe Product, type: :model do
# it { should have_implicit_order_column(:created_at) }
# end
#
# # Minitest (Shoulda)
# class ProductTest < ActiveSupport::TestCase
# should have_implicit_order_column(:created_at)
# end
#
# @return [HaveImplicitOrderColumnMatcher]
#
if RailsShim.active_record_gte_6?
def have_implicit_order_column(column_name)
HaveImplicitOrderColumnMatcher.new(column_name)
end
end

# @private
class HaveImplicitOrderColumnMatcher
attr_reader :failure_message

def initialize(column_name)
@column_name = column_name
end

def matches?(subject)
@subject = subject
check_column_exists!
check_implicit_order_column_matches!
true
rescue SecondaryCheckFailedError => error
@failure_message = Shoulda::Matchers.word_wrap(
"Expected #{model.name} to #{expectation}, " +
"but that could not be proved: #{error.message}."
)
false
rescue PrimaryCheckFailedError => error
@failure_message = Shoulda::Matchers.word_wrap(
"Expected #{model.name} to #{expectation}, but #{error.message}."
)
false
end

def failure_message_when_negated
Shoulda::Matchers.word_wrap(
"Expected #{model.name} not to #{expectation}, but it did."
)
end

def description
expectation
end

private

attr_reader :column_name, :subject

def check_column_exists!
matcher = HaveDbColumnMatcher.new(column_name)

if !matcher.matches?(@subject)
raise SecondaryCheckFailedError.new(
"The :#{model.table_name} table does not have a " +
":#{column_name} column"
)
end
end

def check_implicit_order_column_matches!
if model.implicit_order_column.to_s != column_name.to_s
message =
if model.implicit_order_column.nil?
"implicit_order_column is not set"
else
"it is :#{model.implicit_order_column}"
end

raise PrimaryCheckFailedError.new(message)
end
end

def model
subject.class
end

def expectation
"have an implicit_order_column of :#{column_name}"
end

class SecondaryCheckFailedError < StandardError; end
class PrimaryCheckFailedError < StandardError; end
end
end
end
end
4 changes: 4 additions & 0 deletions lib/shoulda/matchers/rails_shim.rb
Expand Up @@ -21,6 +21,10 @@ def active_record_gte_5?
Gem::Requirement.new('>= 5').satisfied_by?(active_record_version)
end

def active_record_gte_6?
Gem::Requirement.new('>= 6').satisfied_by?(active_record_version)
end

def active_record_version
Gem::Version.new(::ActiveRecord::VERSION::STRING)
rescue NameError
Expand Down
71 changes: 43 additions & 28 deletions spec/support/unit/active_record/create_table.rb
Expand Up @@ -25,7 +25,7 @@ def initialize(
&block
)
@table_name = table_name
@columns = columns
@columns = normalize_columns(columns)
@connection = connection
@customizer = block || proc {}
end
Expand Down Expand Up @@ -66,6 +66,26 @@ def call
to: UnitTests::DatabaseHelpers,
)

def normalize_columns(columns)
if columns.is_a?(Hash)
if columns.values.first.is_a?(Hash)
columns
else
columns.transform_values do |value|
if value == false
value
else
{ type: value }
end
end
end
else
columns.inject({}) do |hash, column_name|
hash.merge(column_name => { type: :string })
end
end
end

def add_columns_to_table(table)
columns.each do |column_name, column_specification|
add_column_to_table(table, column_name, column_specification)
Expand All @@ -75,39 +95,34 @@ def add_columns_to_table(table)
end

def add_column_to_table(table, column_name, column_specification)
if column_specification.is_a?(Hash)
column_specification = column_specification.dup
column_type = column_specification.delete(:type)
column_options = column_specification.delete(:options) { {} }
column_specification = column_specification.dup
column_type = column_specification.delete(:type)
column_options = column_specification.delete(:options) { {} }

if column_options[:array]
if !active_record_supports_array_columns?
raise ArgumentError.new(
'An array column is being added to a table, but this version ' +
"of ActiveRecord (#{active_record_version}) " +
'does not support array columns.',
)
end

if !database_supports_array_columns?
raise ArgumentError.new(
'An array column is being added to a table, but this ' +
"database adapter (#{database_adapter}) " +
'does not support array columns.',
)
end
if column_options[:array]
if !active_record_supports_array_columns?
raise ArgumentError.new(
'An array column is being added to a table, but this version ' +
"of ActiveRecord (#{active_record_version}) " +
'does not support array columns.',
)
end

if column_specification.any?
if !database_supports_array_columns?
raise ArgumentError.new(
"Invalid column specification.\nYou need to put " +
"#{column_specification.keys.map(&:inspect).to_sentence} " +
'inside an :options key!',
'An array column is being added to a table, but this ' +
"database adapter (#{database_adapter}) " +
'does not support array columns.',
)
end
else
column_type = column_specification
column_options = {}
end

if column_specification.any?
raise ArgumentError.new(
"Invalid column specification.\nYou need to put " +
"#{column_specification.keys.map(&:inspect).to_sentence} " +
'inside an :options key!',
)
end

table.column(column_name, column_type, column_options)
Expand Down
4 changes: 4 additions & 0 deletions spec/support/unit/helpers/active_record_versions.rb
Expand Up @@ -50,5 +50,9 @@ def active_record_supports_expression_indexes?
def active_record_supports_validate_presence_on_active_storage?
active_record_version >= '6.0.0.beta1'
end

def active_record_supports_implicit_order_column?
active_record_version >= '6.0.0.beta1'
end
end
end
4 changes: 4 additions & 0 deletions spec/support/unit/helpers/model_builder.rb
Expand Up @@ -10,6 +10,10 @@ def define_model(*args, &block)
ModelBuilder.define_model(*args, &block)
end

def define_model_instance(*args, &block)
define_model(*args, &block).new
end

def define_model_class(*args, &block)
ModelBuilder.define_model_class(*args, &block)
end
Expand Down
3 changes: 1 addition & 2 deletions spec/support/unit/rails_application.rb
Expand Up @@ -204,8 +204,7 @@ def update_gems
bundle.remove_gem 'byebug'
bundle.remove_gem 'web-console'
bundle.add_gem 'pg'
bundle.remove_gem 'sqlite3'
bundle.add_gem 'sqlite3', '~> 1.3.6'
bundle.add_gem 'sqlite', '~> 1.3.6'
end
end

Expand Down