Skip to content

Commit

Permalink
Merge branch 'master' into replace-require-with-autoloading
Browse files Browse the repository at this point in the history
  • Loading branch information
vsppedro committed Jul 15, 2020
2 parents f861070 + 4d4a985 commit 73624d2
Show file tree
Hide file tree
Showing 18 changed files with 528 additions and 83 deletions.
39 changes: 39 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,46 @@
matcher, since `has_secure_token` encourages use of an index but does not
enforce it. ([#1278])

* Add `allow_blank` to `validate_length_of` to match other validation matchers.
([#725], [#1318])

* Add new matcher `have_implicit_order_column` which can be used to test the
`implicit_order_column` setting for ActiveRecord models under Rails 6+.
([#1243])

[#1278]: https://github.com/thoughtbot/shoulda-matchers/pull/1278
[#725]: https://github.com/thoughtbot/shoulda-matchers/pull/725
[#1318]: https://github.com/thoughtbot/shoulda-matchers/pull/1318
[#1243]: https://github.com/thoughtbot/shoulda-matchers/pull/1243

### Bug fixes

* Fix `have_many` so that it is possible to test an association that has a scope
that takes an argument. ([#952], [#992])

* Update `validate_uniqueness_of` to use the public `validators_on` instead of
the private `_validators` when reading validations off of a model. This
enables shoulda-matchers to be used with the [schema_validations] gem.
([#995])

* Update `validate_uniqueness_of` to work with scopes that are `time` columns.
([#1190])

[#952]: https://github.com/thoughtbot/shoulda-matchers/issues/952
[#992]: https://github.com/thoughtbot/shoulda-matchers/pull/992
[schema_validations]: https://github.com/SchemaPlus/schema_validations
[#995]: https://github.com/thoughtbot/shoulda-matchers/pull/995
[#1190]: https://github.com/thoughtbot/shoulda-matchers/pull/1190

### Improvements

* Update `have_many` when used against a `:through` association so that it fails
if the inverse model does not have a `belongs_to` association. ([#646],
[#723], [c0a1578])

[#646]: https://github.com/thoughtbot/shoulda-matchers/issues/646
[#723]: https://github.com/thoughtbot/shoulda-matchers/pull/723
[c0a1578]: https://github.com/thoughtbot/shoulda-matchers/commit/c0a1578435f66d6fbf0db1164205bd8d99f6aa2f

## 4.3.0 - 2020-02-18

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,8 @@ about any of them, make sure to [consult the documentation][rubydocs]!
tests that the table that backs your model has a specific column.
* **[have_db_index](lib/shoulda/matchers/active_record/have_db_index_matcher.rb)**
tests that the table that backs your model has an index on a specific column.
* **[have_implicit_order_column](lib/shoulda/matchers/active_record/have_implicit_order_column.rb)**
tests usage of `implicit_order_column`.
* **[have_many](lib/shoulda/matchers/active_record/association_matcher.rb)**
tests your `has_many` associations.
* **[have_one](lib/shoulda/matchers/active_record/association_matcher.rb)**
Expand Down
7 changes: 7 additions & 0 deletions custom_plan.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ def boot
$LOAD_PATH << File.expand_path('../lib', __FILE__)
$LOAD_PATH << File.expand_path('../spec', __FILE__)

# Fix Zeus for Pry 0.13.0+
Pry::Pager.class_eval do
def best_available
Pry::Pager::NullPager.new(pry_instance.output)
end
end

require_relative 'spec/support/unit/load_environment'
rescue Gem::LoadError => error
raise CouldNotBootZeusError.create(underlying_error: error)
Expand Down
23 changes: 2 additions & 21 deletions lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -311,15 +311,6 @@ def in_range(range)
self
end

def allow_blank
@options[:allow_blank] = true
self
end

def expects_to_allow_blank?
@options[:allow_blank]
end

def allow_nil
@options[:allow_nil] = true
self
Expand Down Expand Up @@ -423,14 +414,14 @@ def matches_for_array?
allows_all_values_in_array? &&
disallows_all_values_outside_of_array? &&
allows_nil_value? &&
allows_blank_value?
allow_blank_matches?
end

def does_not_match_for_array?
disallows_any_values_in_array? ||
allows_any_value_outside_of_array? ||
disallows_nil_value? ||
disallows_blank_value?
allow_blank_does_not_match?
end

def allows_lower_value
Expand Down Expand Up @@ -616,16 +607,6 @@ def disallows_nil_value?
@options[:allow_nil] && disallows_value_of(nil)
end

def allows_blank_value?
@options[:allow_blank] != true ||
BLANK_VALUES.all? { |value| allows_value_of(value) }
end

def disallows_blank_value?
@options[:allow_blank] &&
BLANK_VALUES.any? { |value| disallows_value_of(value) }
end

def inspected_array
Shoulda::Matchers::Util.inspect_values(@array).to_sentence(
two_words_connector: " or ",
Expand Down
30 changes: 27 additions & 3 deletions lib/shoulda/matchers/active_model/validate_length_of_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,34 @@ module ActiveModel
# it { should validate_length_of(:bio).is_at_least(15).allow_nil }
# end
#
# # Test::Unit
# # Minitest (Shoulda)
# class UserTest < ActiveSupport::TestCase
# should validate_length_of(:bio).is_at_least(15).allow_nil
# end
#
# @return [ValidateLengthOfMatcher]
#
# # ##### allow_blank
#
# Use `allow_blank` to assert that the attribute allows blank.
#
# class User
# include ActiveModel::Model
# attr_accessor :bio
#
# validates_length_of :bio, minimum: 15, allow_blank: true
# end
#
# # RSpec
# describe User do
# it { should validate_length_of(:bio).is_at_least(15).allow_blank }
# end
#
# # Minitest (Shoulda)
# class UserTest < ActiveSupport::TestCase
# should validate_length_of(:bio).is_at_least(15).allow_blank
# end
#
def validate_length_of(attr)
ValidateLengthOfMatcher.new(attr)
end
Expand Down Expand Up @@ -331,15 +352,17 @@ def matches?(subject)

lower_bound_matches? &&
upper_bound_matches? &&
allow_nil_matches?
allow_nil_matches? &&
allow_blank_matches?
end

def does_not_match?(subject)
super(subject)

lower_bound_does_not_match? ||
upper_bound_does_not_match? ||
allow_nil_does_not_match?
allow_nil_does_not_match? ||
allow_blank_does_not_match?
end

private
Expand Down Expand Up @@ -376,6 +399,7 @@ def allows_lower_length?
def disallows_lower_length?
!@options.key?(:minimum) ||
@options[:minimum] == 0 ||
(@options[:minimum] == 1 && expects_to_allow_blank?) ||
disallows_length_of?(
@options[:minimum] - 1,
translated_short_message
Expand Down
25 changes: 25 additions & 0 deletions lib/shoulda/matchers/active_model/validation_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ def on(context)
self
end

def allow_blank
options[:allow_blank] = true
self
end

def strict
@expects_strict = true
self
Expand Down Expand Up @@ -118,8 +123,20 @@ def disallow_value_matcher(value, message = nil, &block)
)
end

def allow_blank_matches?
!expects_to_allow_blank? ||
blank_values.all? { |value| allows_value_of(value) }
end

def allow_blank_does_not_match?
expects_to_allow_blank? &&
blank_values.all? { |value| disallows_value_of(value) }
end

private

attr_reader :options

def overall_failure_message
Shoulda::Matchers.word_wrap(
"Expected #{model.name} to #{description}, but this could not be " +
Expand Down Expand Up @@ -163,6 +180,14 @@ def run_allow_or_disallow_matcher(matcher)
@last_submatcher_run = matcher
matcher.matches?(subject)
end

def expects_to_allow_blank?
options[:allow_blank]
end

def blank_values
['', ' ', "\n", "\r", "\t", "\f"]
end
end
end
end
Expand Down
26 changes: 26 additions & 0 deletions lib/shoulda/matchers/active_record.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
require "shoulda/matchers/active_record/association_matcher"
require "shoulda/matchers/active_record/association_matchers"
require "shoulda/matchers/active_record/association_matchers/counter_cache_matcher"
require "shoulda/matchers/active_record/association_matchers/inverse_of_matcher"
require "shoulda/matchers/active_record/association_matchers/join_table_matcher"
require "shoulda/matchers/active_record/association_matchers/order_matcher"
require "shoulda/matchers/active_record/association_matchers/through_matcher"
require "shoulda/matchers/active_record/association_matchers/dependent_matcher"
require "shoulda/matchers/active_record/association_matchers/required_matcher"
require "shoulda/matchers/active_record/association_matchers/optional_matcher"
require "shoulda/matchers/active_record/association_matchers/source_matcher"
require "shoulda/matchers/active_record/association_matchers/model_reflector"
require "shoulda/matchers/active_record/association_matchers/model_reflection"
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"
require "shoulda/matchers/active_record/serialize_matcher"
require "shoulda/matchers/active_record/accept_nested_attributes_for_matcher"
require "shoulda/matchers/active_record/define_enum_for_matcher"
require "shoulda/matchers/active_record/uniqueness"
require "shoulda/matchers/active_record/validate_uniqueness_of_matcher"

module Shoulda
module Matchers
# This module provides matchers that are used to test behavior within
Expand Down
106 changes: 106 additions & 0 deletions lib/shoulda/matchers/active_record/have_implicit_order_column.rb
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,7 @@ def description_for_case_sensitive_qualifier
end

def validations
model._validators[@attribute].select do |validator|
model.validators_on(@attribute).select do |validator|
validator.is_a?(::ActiveRecord::Validations::UniquenessValidator)
end
end
Expand Down Expand Up @@ -820,7 +820,7 @@ def next_scalar_value_for(scope, previous_value)
elsif previous_value.respond_to?(:next)
previous_value.next
elsif previous_value.respond_to?(:to_datetime)
previous_value.to_datetime.next
previous_value.to_datetime.in(60).next
elsif boolean_value?(previous_value)
!previous_value
else
Expand Down
4 changes: 4 additions & 0 deletions lib/shoulda/matchers/rails_shim.rb
Original file line number Diff line number Diff line change
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
Loading

0 comments on commit 73624d2

Please sign in to comment.