From 3edb851c5144f4c45dff2d4f44f76341a6039d83 Mon Sep 17 00:00:00 2001 From: Marek Stanczyk Date: Thu, 31 Jul 2025 12:50:18 +0200 Subject: [PATCH 1/3] add with_deleted scope --- lib/mongoid/paranoia.rb | 6 ++++++ spec/mongoid/paranoia_spec.rb | 23 +++++++++++++++++++++++ spec/mongoid/scoping_spec.rb | 23 +++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/lib/mongoid/paranoia.rb b/lib/mongoid/paranoia.rb index 2f8703c..4ad53fa 100644 --- a/lib/mongoid/paranoia.rb +++ b/lib/mongoid/paranoia.rb @@ -46,6 +46,12 @@ def configure default_scope -> { where(deleted_at: nil) } scope :deleted, -> { ne(deleted_at: nil) } + scope :with_deleted, lambda { + msg = 'This scope requires Mongoid >= 9 and allow_scopes_to_unset_default_scope to be set to true' + raise msg unless Mongoid.try(:allow_scopes_to_unset_default_scope) + + criteria.remove_scoping(unscoped.where(deleted_at: nil)) + } define_model_callbacks :restore define_model_callbacks :remove end diff --git a/spec/mongoid/paranoia_spec.rb b/spec/mongoid/paranoia_spec.rb index c83499b..80aacd3 100644 --- a/spec/mongoid/paranoia_spec.rb +++ b/spec/mongoid/paranoia_spec.rb @@ -98,6 +98,29 @@ class ParanoidConfiguredReset end end + describe ".with_deleted" do + + let(:posts) do + 2.times { |i| ParanoidPost.create(title: "testing #{i}") } + end + + before do + Mongoid.configure do |config| + config.try(:allow_scopes_to_unset_default_scope=, true) + end + + posts.first.destroy + end + + let(:with_deleted) do + ParanoidPost.with_deleted + end + + it "returns the deleted documents" do + expect(with_deleted).to eq(posts) + end + end if respond_to?(:allow_scopes_to_unset_default_scope=) + describe "#destroy!" do context "when the document is a root" do diff --git a/spec/mongoid/scoping_spec.rb b/spec/mongoid/scoping_spec.rb index ccf29d9..e108061 100644 --- a/spec/mongoid/scoping_spec.rb +++ b/spec/mongoid/scoping_spec.rb @@ -51,5 +51,28 @@ expect(criteria.selector).to eq({ "fresh" => true }) end end + + context "when chaining with_deleted" do + + let(:criteria) do + Fish.fresh.with_deleted + end + + if respond_to?(:allow_scopes_to_unset_default_scope=) + before do + Mongoid.configure do |config| + config.try(:allow_scopes_to_unset_default_scope=, true) + end + + it "removes only deleted_at from the selector" do + expect(criteria.selector).to eq({ "fresh" => true }) + end + end + else + it "raises an error if the feature is not supported" do + expect { criteria.selector }.to raise_error(/requires Mongoid >= 9/) + end + end + end end end From 8c87409dd60d4fd242154d431ca6ef5527882053 Mon Sep 17 00:00:00 2001 From: Marek Stanczyk Date: Mon, 10 Nov 2025 12:56:29 +0100 Subject: [PATCH 2/3] improvements --- spec/mongoid/paranoia_spec.rb | 17 +++++++++++------ spec/mongoid/scoping_spec.rb | 21 ++++++++++++--------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/spec/mongoid/paranoia_spec.rb b/spec/mongoid/paranoia_spec.rb index 80aacd3..8118e41 100644 --- a/spec/mongoid/paranoia_spec.rb +++ b/spec/mongoid/paranoia_spec.rb @@ -101,14 +101,19 @@ class ParanoidConfiguredReset describe ".with_deleted" do let(:posts) do - 2.times { |i| ParanoidPost.create(title: "testing #{i}") } + 2.times.map { |i| ParanoidPost.create(title: "testing #{i}") } end - before do - Mongoid.configure do |config| - config.try(:allow_scopes_to_unset_default_scope=, true) - end + around do |example| + original_value = Mongoid.allow_scopes_to_unset_default_scope + Mongoid.configure { |config| config.allow_scopes_to_unset_default_scope = true } + + example.run + Mongoid.configure { |config| config.allow_scopes_to_unset_default_scope = original_value } + end + + before do posts.first.destroy end @@ -119,7 +124,7 @@ class ParanoidConfiguredReset it "returns the deleted documents" do expect(with_deleted).to eq(posts) end - end if respond_to?(:allow_scopes_to_unset_default_scope=) + end if Mongoid.respond_to?(:allow_scopes_to_unset_default_scope=) describe "#destroy!" do diff --git a/spec/mongoid/scoping_spec.rb b/spec/mongoid/scoping_spec.rb index e108061..2b27b04 100644 --- a/spec/mongoid/scoping_spec.rb +++ b/spec/mongoid/scoping_spec.rb @@ -58,15 +58,18 @@ Fish.fresh.with_deleted end - if respond_to?(:allow_scopes_to_unset_default_scope=) - before do - Mongoid.configure do |config| - config.try(:allow_scopes_to_unset_default_scope=, true) - end - - it "removes only deleted_at from the selector" do - expect(criteria.selector).to eq({ "fresh" => true }) - end + if Mongoid.respond_to?(:allow_scopes_to_unset_default_scope=) + around do |example| + original_value = Mongoid.allow_scopes_to_unset_default_scope + Mongoid.configure { |config| config.allow_scopes_to_unset_default_scope = true } + + example.run + + Mongoid.configure { |config| config.allow_scopes_to_unset_default_scope = original_value } + end + + it "removes only deleted_at from the selector" do + expect(criteria.selector).to eq({ "fresh" => true }) end else it "raises an error if the feature is not supported" do From 5b2aff7857c107f1cc189fa416d38ebb2dc18d54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20=C5=A0im=C3=A1nek?= Date: Tue, 2 Dec 2025 17:24:48 +0100 Subject: [PATCH 3/3] Styling updates. --- lib/mongoid/paranoia.rb | 2 +- perf/scope.rb | 3 +++ spec/mongoid/paranoia_spec.rb | 4 +++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/mongoid/paranoia.rb b/lib/mongoid/paranoia.rb index 4ad53fa..279d714 100644 --- a/lib/mongoid/paranoia.rb +++ b/lib/mongoid/paranoia.rb @@ -83,7 +83,7 @@ def persisted? # @since 1.0.0 alias orig_delete delete - def remove(_ = {}) + def remove(_ = {}) # rubocop:disable Naming/PredicateMethod time = self.deleted_at = Time.now _paranoia_update('$set' => { paranoid_field => time }) @destroyed = true diff --git a/perf/scope.rb b/perf/scope.rb index 60c4023..2e7a7f6 100644 --- a/perf/scope.rb +++ b/perf/scope.rb @@ -11,6 +11,7 @@ class Model include Mongoid::Document + field :text, type: String index({ text: 'text' }) @@ -19,6 +20,7 @@ class Model class ParanoidModel include Mongoid::Document include Mongoid::Paranoia + field :text, type: String index({ text: 'text' }) @@ -26,6 +28,7 @@ class ParanoidModel class MetaParanoidModel include Mongoid::Document + field :text, type: String field :deleted_at, type: Time default_scope -> { where(deleted_at: nil) } diff --git a/spec/mongoid/paranoia_spec.rb b/spec/mongoid/paranoia_spec.rb index 8118e41..f8a5003 100644 --- a/spec/mongoid/paranoia_spec.rb +++ b/spec/mongoid/paranoia_spec.rb @@ -105,6 +105,8 @@ class ParanoidConfiguredReset end around do |example| + skip unless Mongoid.respond_to?(:allow_scopes_to_unset_default_scope=) + original_value = Mongoid.allow_scopes_to_unset_default_scope Mongoid.configure { |config| config.allow_scopes_to_unset_default_scope = true } @@ -124,7 +126,7 @@ class ParanoidConfiguredReset it "returns the deleted documents" do expect(with_deleted).to eq(posts) end - end if Mongoid.respond_to?(:allow_scopes_to_unset_default_scope=) + end describe "#destroy!" do