Skip to content

Commit

Permalink
feat: implement allow_blank
Browse files Browse the repository at this point in the history
  • Loading branch information
andreLumor committed Aug 4, 2022
1 parent 206d2da commit 0d27baf
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 2 deletions.
1 change: 1 addition & 0 deletions lib/shoulda/matchers/active_model/qualifiers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ module Qualifiers
end

require_relative 'qualifiers/allow_nil'
require_relative 'qualifiers/allow_blank'
require_relative 'qualifiers/ignore_interference_by_writer'
require_relative 'qualifiers/ignoring_interference_by_writer'
26 changes: 26 additions & 0 deletions lib/shoulda/matchers/active_model/qualifiers/allow_blank.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module Shoulda
module Matchers
module ActiveModel
module Qualifiers
# @private
module AllowBlank
def initialize(*args)
super
@expects_to_allow_blank = false
end

def allow_blank
@expects_to_allow_blank = true
self
end

protected

def expects_to_allow_blank?
@expects_to_allow_blank
end
end
end
end
end
end
28 changes: 26 additions & 2 deletions lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,27 @@ module ActiveModel
# should validate_presence_of(:nickname).allow_nil
# end
#
# #### allow_blank
#
# Use `allow_blank` to assert that the attribute allows blank.
#
# class Robot
# include ActiveModel::Model
# attr_accessor :nickname
#
# validates_presence_of :nickname, allow_blank: true
# end
#
# # RSpec
# RSpec.describe Robot, type: :model do
# it { should validate_presence_of(:nickname).allow_blank }
# end
#
# # Minitest (Shoulda)
# class RobotTest < ActiveSupport::TestCase
# should validate_presence_of(:nickname).allow_blank
# end
#
# ##### on
#
# Use `on` if your validation applies only under a certain context.
Expand Down Expand Up @@ -133,6 +154,7 @@ def validate_presence_of(attr)
# @private
class ValidatePresenceOfMatcher < ValidationMatcher
include Qualifiers::AllowNil
include Qualifiers::AllowBlank

def initialize(attribute)
super
Expand All @@ -152,6 +174,7 @@ def matches?(subject)
end
else
(!expects_to_allow_nil? || allows_value_of(nil)) &&
(!expects_to_allow_blank? || allows_value_of('')) &&
disallowed_values.all? do |value|
disallows_original_or_typecast_value?(value)
end
Expand All @@ -171,6 +194,7 @@ def does_not_match?(subject)
end
else
(expects_to_allow_nil? && disallows_value_of(nil)) ||
(expects_to_allow_blank? && disallows_value_of('')) ||
disallowed_values.any? do |value|
allows_original_or_typecast_value?(value)
end
Expand Down Expand Up @@ -241,11 +265,11 @@ def disallowed_values
else
values = []

if attribute_accepts_string_values?
if attribute_accepts_string_values? && !expects_to_allow_blank?
values << ''
end

if !expects_to_allow_nil?
if !expects_to_allow_nil? && !expects_to_allow_blank?
values << nil
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,82 @@ def foo=(_value)
end
end

context 'qualified with allow_blank' do
context 'when validating a model with a presence validator' do
context 'and it is specified with allow_blank: true' do
it 'matches in the positive' do
record = validating_presence(allow_blank: true)
expect(record).to matcher.allow_blank
end

it 'does not match in the negative' do
record = validating_presence(allow_blank: true)

assertion = -> { expect(record).not_to matcher.allow_blank }

expect(&assertion).to fail_with_message(<<-MESSAGE)
Expected Example not to validate that :attr cannot be empty/falsy, but
this could not be proved.
After setting :attr to ‹""›, the matcher expected the Example to be
invalid, but it was valid instead.
MESSAGE
end
end

context 'and it is not specified with allow_blank: true' do
it 'does not match in the positive' do
record = validating_presence

assertion = lambda do
expect(record).to matcher.allow_blank
end

message = <<-MESSAGE
Expected Example to validate that :attr cannot be empty/falsy, but this
could not be proved.
After setting :attr to ‹""›, the matcher expected the Example to be
valid, but it was invalid instead, producing these validation errors:
* attr: ["can't be blank"]
MESSAGE

expect(&assertion).to fail_with_message(message)
end
end

it 'matches in the negative' do
record = validating_presence

expect(record).not_to matcher.allow_blank
end
end

context 'when validating a model without a presence validator' do
it 'matches in the positive' do
record = without_validating_presence

expect(record).to matcher.allow_blank
end

it 'does not match in the negative' do
record = without_validating_presence

assertion = lambda do
expect(record).not_to matcher.allow_blank
end

message = <<-MESSAGE
Expected Example not to validate that :attr cannot be empty/falsy, but
this could not be proved.
After setting :attr to ‹""›, the matcher expected the Example to be
invalid, but it was valid instead.
MESSAGE

expect(&assertion).to fail_with_message(message)
end
end
end

def matcher
validate_presence_of(:attr)
end
Expand Down

0 comments on commit 0d27baf

Please sign in to comment.