Expose an `invert_where` method that will invert all scope conditions.

```ruby
class User
  scope :active, -> { where(accepted: true, locked: false) }
end

User.active

User.active.invert_where
```
This commit is contained in:
Kevin Deisz 2020-09-17 16:12:58 -04:00
parent 432698ef2b
commit 812e90482d
No known key found for this signature in database
GPG Key ID: 463A1B0CD545B337
4 changed files with 50 additions and 1 deletions

View File

@ -1,3 +1,19 @@
* Expose an `invert_where` method that will invert all scope conditions.
```ruby
class User
scope :active, -> { where(accepted: true, locked: false) }
end
User.active
# ... WHERE `accepted` = 1 AND `locked` = 0
User.active.invert_where
# ... WHERE NOT (`accepted` = 1 AND `locked` = 0)
```
*Kevin Deisz*
* Restore possibility of passing `false` to :polymorphic option of `belongs_to`.
Previously, passing `false` would trigger the option validation logic

View File

@ -13,7 +13,7 @@ module ActiveRecord
:destroy_all, :delete_all, :update_all, :touch_all, :destroy_by, :delete_by,
:find_each, :find_in_batches, :in_batches,
:select, :reselect, :order, :reorder, :group, :limit, :offset, :joins, :left_joins, :left_outer_joins,
:where, :rewhere, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
:where, :rewhere, :invert_where, :preload, :extract_associated, :eager_load, :includes, :from, :lock, :readonly,
:and, :or, :annotate, :optimizer_hints, :extending,
:having, :create_with, :distinct, :references, :none, :unscope, :merge, :except, :only,
:count, :average, :minimum, :maximum, :sum, :calculate,

View File

@ -693,6 +693,32 @@ module ActiveRecord
scope
end
# Allows you to invert an entire where clause instead of manually applying conditions.
#
# class User
# scope :active, -> { where(accepted: true, locked: false) }
# end
#
# User.where(accepted: true)
# # WHERE `accepted` = 1
#
# User.where(accepted: true).invert_where
# # WHERE `accepted` != 1
#
# User.active
# # WHERE `accepted` = 1 AND `locked` = 0
#
# User.active.invert_where
# # WHERE NOT (`accepted` = 1 AND `locked` = 0)
def invert_where
spawn.invert_where!
end
def invert_where! # :nodoc:
self.where_clause = where_clause.invert
self
end
# Returns a new relation, which is the logical intersection of this relation and the one passed
# as an argument.
#

View File

@ -425,5 +425,12 @@ module ActiveRecord
def test_where_with_unsupported_arguments
assert_raises(ArgumentError) { Author.where(42) }
end
def test_invert_where
author = authors(:david)
posts = author.posts.where.not(id: 1)
assert_equal 1, posts.invert_where.first.id
end
end
end