Skip to content

Commit

Permalink
Merge branch 'core' into fix-nil-error-on-dependency-handling
Browse files Browse the repository at this point in the history
  • Loading branch information
mathieujobin authored Jun 5, 2023
2 parents 5de9fc6 + 33e433a commit b61e83b
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 47 deletions.
58 changes: 24 additions & 34 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,30 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby:
- 3.1
- '3.0'
- 2.7
- 2.6
- 2.5
# - jruby-9.2.19.0
# - jruby-9.3.1.0
rails:
- '~> 5.1.0'
- '~> 5.2.0'
- '~> 6.0.0'
- '~> 6.1.0'
- '~> 7.0.0'
- 'edge'
exclude:
# Rails edge is now 7.x and requires ruby 2.7
- rails: 'edge'
ruby: 2.6
- rails: 'edge'
ruby: 2.5
- rails: '~> 7.0.0'
ruby: 2.6
- rails: '~> 7.0.0'
ruby: 2.5
# Legacy Rails with newer rubies
- rails: '~> 5.1.0'
ruby: '3.0'
- rails: '~> 5.2.0'
ruby: '3.0'
- rails: '~> 5.1.0'
ruby: 3.1
- rails: '~> 5.2.0'
ruby: 3.1
rails: ["~> 7.0.0", "~> 6.1.0", "~> 6.0.0"]
ruby: ["3.2.2", "3.1.4", "3.0.6", "2.7.8"]
include:
- ruby: 3.2
rails: 'edge'
# single test failure with jruby
#- ruby: jruby-9.4
# rails: '~> 7.0.0'
- ruby: 2.6
rails: '~> 6.1.0'
- ruby: 2.6
rails: '~> 6.0.0'
- ruby: 2.6
rails: '~> 5.2.0'
- ruby: 2.6
rails: '~> 5.1.0'
- ruby: 2.5
rails: '~> 6.0.0'
- ruby: 2.5
rails: '~> 5.2.0'
- ruby: 2.5
rails: '~> 5.1.0'
#os: ubuntu-latest
#arch: x64

env:
RAILS: ${{ matrix.rails }}
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# paranoia Changelog

## 2.6.2

* [#441](https://github.com/rubysherpas/paranoia/pull/441) Recursive restore with has_many/one through assocs (#441)
[Emil Ong](https://github.com/emilong)

## 2.6.1

* [#535](https://github.com/rubysherpas/paranoia/pull/535) Allow to skip updating paranoia_destroy_attributes for records while really_destroy!
[Anton Bogdanov](https://github.com/kortirso)

## 2.6.0

* [#512](https://github.com/rubysherpas/paranoia/pull/512) Quote table names; Mysql 8 has keywords that might match table names which cause an exception.
* [#476](https://github.com/rubysherpas/paranoia/pull/476) Fix syntax error in documentation.
* [#485](https://github.com/rubysherpas/paranoia/pull/485) Rollback transaction if destroy aborted.
* [#522](https://github.com/rubysherpas/paranoia/pull/522) Add failing tests for association with abort on destroy.
* [#513](https://github.com/rubysherpas/paranoia/pull/513) Fix create callback called on destroy.

## 2.5.3

* [#532](https://github.com/rubysherpas/paranoia/pull/532) Fix: correct bug when sentinel_value is not a timestamp
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[![Gem Version](https://badge.fury.io/rb/paranoia.svg)](https://badge.fury.io/rb/paranoia)
[![build](https://github.com/rubysherpas/paranoia/actions/workflows/build.yml/badge.svg)](https://github.com/rubysherpas/paranoia/actions/workflows/build.yml)

**Notice:**
**Notice:**

`paranoia` has some surprising behaviour (like overriding ActiveRecord's `delete` and `destroy`) and is not recommended for new projects. See [`discard`'s README](https://github.com/jhawthorn/discard#why-not-paranoia-or-acts_as_paranoid) for more details.

Expand Down Expand Up @@ -103,6 +103,14 @@ If you really want it gone *gone*, call `really_destroy!`:
# => client
```

If you need skip updating timestamps for deleting records, call `really_destroy!(update_destroy_attributes: false)`.
When we call `really_destroy!(update_destroy_attributes: false)` on the parent `client`, then each child `email` will also have `really_destroy!(update_destroy_attributes: false)` called.

``` ruby
>> client.really_destroy!(update_destroy_attributes: false)
# => client
```

If you want to use a column other than `deleted_at`, you can pass it as an option:

``` ruby
Expand Down
21 changes: 14 additions & 7 deletions lib/paranoia.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def only_deleted
# these will not match != sentinel value because "NULL != value" is
# NULL under the sql standard
# Scoping with the table_name is mandatory to avoid ambiguous errors when joining tables.
scoped_quoted_paranoia_column = "#{self.table_name}.#{connection.quote_column_name(paranoia_column)}"
scoped_quoted_paranoia_column = "#{connection.quote_table_name(self.table_name)}.#{connection.quote_column_name(paranoia_column)}"
with_deleted.where("#{scoped_quoted_paranoia_column} IS NULL OR #{scoped_quoted_paranoia_column} != ?", paranoia_sentinel_value)
end
alias_method :deleted, :only_deleted
Expand Down Expand Up @@ -144,7 +144,7 @@ def paranoia_destroyed?
end
alias :deleted? :paranoia_destroyed?

def really_destroy!
def really_destroy!(update_destroy_attributes: true)
with_transaction_returning_status do
run_callbacks(:real_destroy) do
@_disable_counter_cache = paranoia_destroyed?
Expand All @@ -158,12 +158,14 @@ def really_destroy!
# .paranoid? will work for both instances and classes
next unless association_data && association_data.paranoid?
if reflection.collection?
next association_data.with_deleted.each(&:really_destroy!)
next association_data.with_deleted.find_each { |record|
record.really_destroy!(update_destroy_attributes: update_destroy_attributes)
}
end
association_data.really_destroy!
association_data.really_destroy!(update_destroy_attributes: update_destroy_attributes)
end
end
update_columns(paranoia_destroy_attributes)
update_columns(paranoia_destroy_attributes) if update_destroy_attributes
destroy_without_paranoia
end
end
Expand Down Expand Up @@ -215,7 +217,12 @@ def restore_associated_records(recovery_window_range = nil)

if association_data.nil? && association.macro.to_s == "has_one"
association_class_name = association.klass.name
association_foreign_key = association.foreign_key

association_foreign_key = if association.options[:through].present?
association.klass.primary_key
else
association.foreign_key
end

if association.type
association_polymorphic_type = association.type
Expand All @@ -224,7 +231,7 @@ def restore_associated_records(recovery_window_range = nil)
association_find_conditions = { association_foreign_key => self.id }
end

association_class = association_class_name.constantize
association_class = association.klass
if association_class.paranoid?
association_class.only_deleted.where(association_find_conditions).first
.try!(:restore, recursive: true, :recovery_window_range => recovery_window_range)
Expand Down
2 changes: 1 addition & 1 deletion lib/paranoia/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Paranoia
VERSION = '2.5.3'.freeze
VERSION = '2.6.2'.freeze
end
80 changes: 76 additions & 4 deletions test/paranoia_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ def setup!
'active_column_model_with_uniqueness_validations' => 'name VARCHAR(32), deleted_at DATETIME, active BOOLEAN',
'paranoid_model_with_belongs_to_active_column_model_with_has_many_relationships' => 'name VARCHAR(32), deleted_at DATETIME, active BOOLEAN, active_column_model_with_has_many_relationship_id INTEGER',
'active_column_model_with_has_many_relationships' => 'name VARCHAR(32), deleted_at DATETIME, active BOOLEAN',
'without_default_scope_models' => 'deleted_at DATETIME'
'without_default_scope_models' => 'deleted_at DATETIME',
'paranoid_has_through_restore_parents' => 'deleted_at DATETIME',
'empty_paranoid_models' => 'deleted_at DATETIME',
'paranoid_has_one_throughs' => 'paranoid_has_through_restore_parent_id INTEGER NOT NULL, empty_paranoid_model_id INTEGER NOT NULL, deleted_at DATETIME',
'paranoid_has_many_throughs' => 'paranoid_has_through_restore_parent_id INTEGER NOT NULL, empty_paranoid_model_id INTEGER NOT NULL, deleted_at DATETIME',
}.each do |table_name, columns_as_sql_string|
ActiveRecord::Base.connection.execute "CREATE TABLE #{table_name} (id INTEGER NOT NULL PRIMARY KEY, #{columns_as_sql_string})"
end
Expand Down Expand Up @@ -387,14 +391,22 @@ def test_active_column_model_with_uniqueness_validation_still_works_on_non_delet
end

def test_sentinel_value_for_custom_sentinel_models
time_zero = if ActiveRecord::VERSION::MAJOR < 6
Time.new(0)
elsif ActiveRecord::VERSION::MAJOR == 6 && ActiveRecord::VERSION::MINOR < 1
Time.new(0)
else
DateTime.new(0)
end

model = CustomSentinelModel.new
assert_equal 0, model.class.count
model.save!
assert_equal DateTime.new(0), model.deleted_at
assert_equal time_zero, model.deleted_at
assert_equal 1, model.class.count
model.destroy

assert DateTime.new(0) != model.deleted_at
assert time_zero != model.deleted_at
assert model.paranoia_destroyed?

assert_equal 0, model.class.count
Expand All @@ -403,7 +415,7 @@ def test_sentinel_value_for_custom_sentinel_models
assert_equal 1, model.class.deleted.count

model.restore
assert_equal DateTime.new(0), model.deleted_at
assert_equal time_zero, model.deleted_at
assert !model.destroyed?

assert_equal 1, model.class.count
Expand Down Expand Up @@ -1055,6 +1067,40 @@ def test_restore_recursive_on_polymorphic_has_one_association
assert_equal 1, polymorphic.class.count
end

def test_recursive_restore_with_has_through_associations
parent = ParanoidHasThroughRestoreParent.create
one = EmptyParanoidModel.create
ParanoidHasOneThrough.create(
:paranoid_has_through_restore_parent => parent,
:empty_paranoid_model => one,
)
many = Array.new(3) do
many = EmptyParanoidModel.create
ParanoidHasManyThrough.create(
:paranoid_has_through_restore_parent => parent,
:empty_paranoid_model => many,
)

many
end

assert_equal true, parent.empty_paranoid_model.present?
assert_equal 3, parent.empty_paranoid_models.count

parent.destroy

assert_equal true, parent.empty_paranoid_model.reload.deleted?
assert_equal 0, parent.empty_paranoid_models.count

parent = ParanoidHasThroughRestoreParent.with_deleted.first
parent.restore(recursive: true)

assert_equal false, parent.empty_paranoid_model.deleted?
assert_equal one, parent.empty_paranoid_model
assert_equal 3, parent.empty_paranoid_models.count
assert_equal many, parent.empty_paranoid_models
end

# Ensure that we're checking parent_type when restoring
def test_missing_restore_recursive_on_polymorphic_has_one_association
parent = ParentModel.create
Expand Down Expand Up @@ -1555,3 +1601,29 @@ class ParanoidBelongsTo < ActiveRecord::Base
belongs_to :paranoid_has_one
end
end

class ParanoidHasThroughRestoreParent < ActiveRecord::Base
acts_as_paranoid

has_one :paranoid_has_one_through, dependent: :destroy
has_one :empty_paranoid_model, through: :paranoid_has_one_through, dependent: :destroy

has_many :paranoid_has_many_throughs, dependent: :destroy
has_many :empty_paranoid_models, through: :paranoid_has_many_throughs, dependent: :destroy
end

class EmptyParanoidModel < ActiveRecord::Base
acts_as_paranoid
end

class ParanoidHasOneThrough < ActiveRecord::Base
acts_as_paranoid
belongs_to :paranoid_has_through_restore_parent
belongs_to :empty_paranoid_model, dependent: :destroy
end

class ParanoidHasManyThrough < ActiveRecord::Base
acts_as_paranoid
belongs_to :paranoid_has_through_restore_parent
belongs_to :empty_paranoid_model, dependent: :destroy
end

0 comments on commit b61e83b

Please sign in to comment.