Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
### 4.3.0 (Next)

* [#247](https://github.com/mongoid/mongoid-rspec/pull/247): Migrate to danger-pr-comment workflow - [@dblock](https://github.com/dblock).
* [#249](https://github.com/mongoid/mongoid-rspec/pull/249): Ignore `:if`/`:unless` validator for class subjects (do not raise error) - [@johnnyshields](https://github.com/johnnyshields).
* [#249](https://github.com/mongoid/mongoid-rspec/pull/249): Support Arrays and Procs with non-zero arity for `:if`/`:unless` validator - [@johnnyshields](https://github.com/johnnyshields).
* [#248](https://github.com/mongoid/mongoid-rspec/pull/248): Add frozen_string_literal: true to all files and enforce rubocop - [@johnnyshields](https://github.com/johnnyshields).
* [#248](https://github.com/mongoid/mongoid-rspec/pull/248): Various small CI fixes - [@johnnyshields](https://github.com/johnnyshields).
* [#247](https://github.com/mongoid/mongoid-rspec/pull/247): Migrate to danger-pr-comment workflow - [@dblock](https://github.com/dblock).
* Your contribution here.

### 4.2.0 (2024/06/04)
Expand Down
14 changes: 8 additions & 6 deletions lib/matchers/validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,26 @@ def with_message(message)

def if_condition_matches?(actual, validator)
return true unless validator.options[:if]
return true if actual.is_a?(Class)

check_condition actual, validator.options[:if]
check_condition(actual, validator.options[:if])
end

def unless_condition_matches?(actual, validator)
return true unless validator.options[:unless]
return true if actual.is_a?(Class)

!check_condition actual, validator.options[:unless]
!check_condition(actual, validator.options[:unless])
end

def check_condition(actual, filter)
raise ArgumentError, 'Spec subject must be object instance when testing validators with if/unless condition.' if actual.is_a?(Class)

case filter
when Symbol
actual.send filter
actual.send(filter)
when ::Proc
actual.instance_exec(&filter)
filter.arity.zero? ? actual.instance_exec(&filter) : filter.call(actual)
when Array
filter.all? { |f| check_condition(actual, f) }
else
raise ArgumentError, "Unexpected filter: #{filter.inspect}"
end
Expand Down
10 changes: 10 additions & 0 deletions spec/models/article.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class Article
field :status, type: Symbol
field :deletion_date, type: DateTime, default: nil
field :reviewer, type: String, default: nil
field :editor, type: String, default: nil
field :summary, type: String, default: nil

embeds_many :comments, cascade_callbacks: true, inverse_of: :article
embeds_one :permalink, inverse_of: :linkable, class_name: 'Permalink'
Expand All @@ -37,6 +39,14 @@ class Article

validates_absence_of :comments, unless: :allow_comments if Mongoid::Compatibility::Version.mongoid4_or_newer?

validates_presence_of :editor, if: ->(article) { article.status == :approved }

validates_presence_of :summary, if: [:published?, ->(article) { article.status == :approved }]

def published?
published == true
end

index({title: 1 }, { unique: true, background: true})
index({ published: 1 })

Expand Down
44 changes: 44 additions & 0 deletions spec/unit/validations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -119,5 +119,49 @@
it { is_expected.not_to validate_presence_of(:reviewer) }
end
end

describe 'with Class subject (no instance)' do
it 'skips condition evaluation and finds the validator' do
expect(User).to validate_length_of(:password).greater_than(20)
end

it 'skips unless condition evaluation and finds the validator' do
expect(Article).to validate_presence_of(:reviewer)
end
end

describe 'with lambda that takes an explicit argument' do
context 'when the condition is met' do
subject { Article.new(status: :approved) }

it { is_expected.to validate_presence_of(:editor) }
end

context 'when the condition is not met' do
subject { Article.new(status: :pending) }

it { is_expected.not_to validate_presence_of(:editor) }
end
end

describe 'with array of conditions (symbol + lambda)' do
context 'when all conditions are met' do
subject { Article.new(published: true, status: :approved) }

it { is_expected.to validate_presence_of(:summary) }
end

context 'when symbol condition is not met' do
subject { Article.new(published: false, status: :approved) }

it { is_expected.not_to validate_presence_of(:summary) }
end

context 'when lambda condition is not met' do
subject { Article.new(published: true, status: :pending) }

it { is_expected.not_to validate_presence_of(:summary) }
end
end
end
end
Loading