Skip to content

Commit b61e83b

Browse files
authored
Merge branch 'core' into fix-nil-error-on-dependency-handling
2 parents 5de9fc6 + 33e433a commit b61e83b

File tree

6 files changed

+142
-47
lines changed

6 files changed

+142
-47
lines changed

.github/workflows/build.yml

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,40 +15,30 @@ jobs:
1515
strategy:
1616
fail-fast: false
1717
matrix:
18-
ruby:
19-
- 3.1
20-
- '3.0'
21-
- 2.7
22-
- 2.6
23-
- 2.5
24-
# - jruby-9.2.19.0
25-
# - jruby-9.3.1.0
26-
rails:
27-
- '~> 5.1.0'
28-
- '~> 5.2.0'
29-
- '~> 6.0.0'
30-
- '~> 6.1.0'
31-
- '~> 7.0.0'
32-
- 'edge'
33-
exclude:
34-
# Rails edge is now 7.x and requires ruby 2.7
35-
- rails: 'edge'
36-
ruby: 2.6
37-
- rails: 'edge'
38-
ruby: 2.5
39-
- rails: '~> 7.0.0'
40-
ruby: 2.6
41-
- rails: '~> 7.0.0'
42-
ruby: 2.5
43-
# Legacy Rails with newer rubies
44-
- rails: '~> 5.1.0'
45-
ruby: '3.0'
46-
- rails: '~> 5.2.0'
47-
ruby: '3.0'
48-
- rails: '~> 5.1.0'
49-
ruby: 3.1
50-
- rails: '~> 5.2.0'
51-
ruby: 3.1
18+
rails: ["~> 7.0.0", "~> 6.1.0", "~> 6.0.0"]
19+
ruby: ["3.2.2", "3.1.4", "3.0.6", "2.7.8"]
20+
include:
21+
- ruby: 3.2
22+
rails: 'edge'
23+
# single test failure with jruby
24+
#- ruby: jruby-9.4
25+
# rails: '~> 7.0.0'
26+
- ruby: 2.6
27+
rails: '~> 6.1.0'
28+
- ruby: 2.6
29+
rails: '~> 6.0.0'
30+
- ruby: 2.6
31+
rails: '~> 5.2.0'
32+
- ruby: 2.6
33+
rails: '~> 5.1.0'
34+
- ruby: 2.5
35+
rails: '~> 6.0.0'
36+
- ruby: 2.5
37+
rails: '~> 5.2.0'
38+
- ruby: 2.5
39+
rails: '~> 5.1.0'
40+
#os: ubuntu-latest
41+
#arch: x64
5242

5343
env:
5444
RAILS: ${{ matrix.rails }}

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# paranoia Changelog
22

3+
## 2.6.2
4+
5+
* [#441](https://github.com/rubysherpas/paranoia/pull/441) Recursive restore with has_many/one through assocs (#441)
6+
[Emil Ong](https://github.com/emilong)
7+
8+
## 2.6.1
9+
10+
* [#535](https://github.com/rubysherpas/paranoia/pull/535) Allow to skip updating paranoia_destroy_attributes for records while really_destroy!
11+
[Anton Bogdanov](https://github.com/kortirso)
12+
13+
## 2.6.0
14+
15+
* [#512](https://github.com/rubysherpas/paranoia/pull/512) Quote table names; Mysql 8 has keywords that might match table names which cause an exception.
16+
* [#476](https://github.com/rubysherpas/paranoia/pull/476) Fix syntax error in documentation.
17+
* [#485](https://github.com/rubysherpas/paranoia/pull/485) Rollback transaction if destroy aborted.
18+
* [#522](https://github.com/rubysherpas/paranoia/pull/522) Add failing tests for association with abort on destroy.
19+
* [#513](https://github.com/rubysherpas/paranoia/pull/513) Fix create callback called on destroy.
20+
321
## 2.5.3
422

523
* [#532](https://github.com/rubysherpas/paranoia/pull/532) Fix: correct bug when sentinel_value is not a timestamp

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[![Gem Version](https://badge.fury.io/rb/paranoia.svg)](https://badge.fury.io/rb/paranoia)
22
[![build](https://github.com/rubysherpas/paranoia/actions/workflows/build.yml/badge.svg)](https://github.com/rubysherpas/paranoia/actions/workflows/build.yml)
33

4-
**Notice:**
4+
**Notice:**
55

66
`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.
77

@@ -103,6 +103,14 @@ If you really want it gone *gone*, call `really_destroy!`:
103103
# => client
104104
```
105105

106+
If you need skip updating timestamps for deleting records, call `really_destroy!(update_destroy_attributes: false)`.
107+
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.
108+
109+
``` ruby
110+
>> client.really_destroy!(update_destroy_attributes: false)
111+
# => client
112+
```
113+
106114
If you want to use a column other than `deleted_at`, you can pass it as an option:
107115

108116
``` ruby

lib/paranoia.rb

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def only_deleted
4040
# these will not match != sentinel value because "NULL != value" is
4141
# NULL under the sql standard
4242
# Scoping with the table_name is mandatory to avoid ambiguous errors when joining tables.
43-
scoped_quoted_paranoia_column = "#{self.table_name}.#{connection.quote_column_name(paranoia_column)}"
43+
scoped_quoted_paranoia_column = "#{connection.quote_table_name(self.table_name)}.#{connection.quote_column_name(paranoia_column)}"
4444
with_deleted.where("#{scoped_quoted_paranoia_column} IS NULL OR #{scoped_quoted_paranoia_column} != ?", paranoia_sentinel_value)
4545
end
4646
alias_method :deleted, :only_deleted
@@ -144,7 +144,7 @@ def paranoia_destroyed?
144144
end
145145
alias :deleted? :paranoia_destroyed?
146146

147-
def really_destroy!
147+
def really_destroy!(update_destroy_attributes: true)
148148
with_transaction_returning_status do
149149
run_callbacks(:real_destroy) do
150150
@_disable_counter_cache = paranoia_destroyed?
@@ -158,12 +158,14 @@ def really_destroy!
158158
# .paranoid? will work for both instances and classes
159159
next unless association_data && association_data.paranoid?
160160
if reflection.collection?
161-
next association_data.with_deleted.each(&:really_destroy!)
161+
next association_data.with_deleted.find_each { |record|
162+
record.really_destroy!(update_destroy_attributes: update_destroy_attributes)
163+
}
162164
end
163-
association_data.really_destroy!
165+
association_data.really_destroy!(update_destroy_attributes: update_destroy_attributes)
164166
end
165167
end
166-
update_columns(paranoia_destroy_attributes)
168+
update_columns(paranoia_destroy_attributes) if update_destroy_attributes
167169
destroy_without_paranoia
168170
end
169171
end
@@ -215,7 +217,12 @@ def restore_associated_records(recovery_window_range = nil)
215217

216218
if association_data.nil? && association.macro.to_s == "has_one"
217219
association_class_name = association.klass.name
218-
association_foreign_key = association.foreign_key
220+
221+
association_foreign_key = if association.options[:through].present?
222+
association.klass.primary_key
223+
else
224+
association.foreign_key
225+
end
219226

220227
if association.type
221228
association_polymorphic_type = association.type
@@ -224,7 +231,7 @@ def restore_associated_records(recovery_window_range = nil)
224231
association_find_conditions = { association_foreign_key => self.id }
225232
end
226233

227-
association_class = association_class_name.constantize
234+
association_class = association.klass
228235
if association_class.paranoid?
229236
association_class.only_deleted.where(association_find_conditions).first
230237
.try!(:restore, recursive: true, :recovery_window_range => recovery_window_range)

lib/paranoia/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module Paranoia
2-
VERSION = '2.5.3'.freeze
2+
VERSION = '2.6.2'.freeze
33
end

test/paranoia_test.rb

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ def setup!
4949
'active_column_model_with_uniqueness_validations' => 'name VARCHAR(32), deleted_at DATETIME, active BOOLEAN',
5050
'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',
5151
'active_column_model_with_has_many_relationships' => 'name VARCHAR(32), deleted_at DATETIME, active BOOLEAN',
52-
'without_default_scope_models' => 'deleted_at DATETIME'
52+
'without_default_scope_models' => 'deleted_at DATETIME',
53+
'paranoid_has_through_restore_parents' => 'deleted_at DATETIME',
54+
'empty_paranoid_models' => 'deleted_at DATETIME',
55+
'paranoid_has_one_throughs' => 'paranoid_has_through_restore_parent_id INTEGER NOT NULL, empty_paranoid_model_id INTEGER NOT NULL, deleted_at DATETIME',
56+
'paranoid_has_many_throughs' => 'paranoid_has_through_restore_parent_id INTEGER NOT NULL, empty_paranoid_model_id INTEGER NOT NULL, deleted_at DATETIME',
5357
}.each do |table_name, columns_as_sql_string|
5458
ActiveRecord::Base.connection.execute "CREATE TABLE #{table_name} (id INTEGER NOT NULL PRIMARY KEY, #{columns_as_sql_string})"
5559
end
@@ -387,14 +391,22 @@ def test_active_column_model_with_uniqueness_validation_still_works_on_non_delet
387391
end
388392

389393
def test_sentinel_value_for_custom_sentinel_models
394+
time_zero = if ActiveRecord::VERSION::MAJOR < 6
395+
Time.new(0)
396+
elsif ActiveRecord::VERSION::MAJOR == 6 && ActiveRecord::VERSION::MINOR < 1
397+
Time.new(0)
398+
else
399+
DateTime.new(0)
400+
end
401+
390402
model = CustomSentinelModel.new
391403
assert_equal 0, model.class.count
392404
model.save!
393-
assert_equal DateTime.new(0), model.deleted_at
405+
assert_equal time_zero, model.deleted_at
394406
assert_equal 1, model.class.count
395407
model.destroy
396408

397-
assert DateTime.new(0) != model.deleted_at
409+
assert time_zero != model.deleted_at
398410
assert model.paranoia_destroyed?
399411

400412
assert_equal 0, model.class.count
@@ -403,7 +415,7 @@ def test_sentinel_value_for_custom_sentinel_models
403415
assert_equal 1, model.class.deleted.count
404416

405417
model.restore
406-
assert_equal DateTime.new(0), model.deleted_at
418+
assert_equal time_zero, model.deleted_at
407419
assert !model.destroyed?
408420

409421
assert_equal 1, model.class.count
@@ -1055,6 +1067,40 @@ def test_restore_recursive_on_polymorphic_has_one_association
10551067
assert_equal 1, polymorphic.class.count
10561068
end
10571069

1070+
def test_recursive_restore_with_has_through_associations
1071+
parent = ParanoidHasThroughRestoreParent.create
1072+
one = EmptyParanoidModel.create
1073+
ParanoidHasOneThrough.create(
1074+
:paranoid_has_through_restore_parent => parent,
1075+
:empty_paranoid_model => one,
1076+
)
1077+
many = Array.new(3) do
1078+
many = EmptyParanoidModel.create
1079+
ParanoidHasManyThrough.create(
1080+
:paranoid_has_through_restore_parent => parent,
1081+
:empty_paranoid_model => many,
1082+
)
1083+
1084+
many
1085+
end
1086+
1087+
assert_equal true, parent.empty_paranoid_model.present?
1088+
assert_equal 3, parent.empty_paranoid_models.count
1089+
1090+
parent.destroy
1091+
1092+
assert_equal true, parent.empty_paranoid_model.reload.deleted?
1093+
assert_equal 0, parent.empty_paranoid_models.count
1094+
1095+
parent = ParanoidHasThroughRestoreParent.with_deleted.first
1096+
parent.restore(recursive: true)
1097+
1098+
assert_equal false, parent.empty_paranoid_model.deleted?
1099+
assert_equal one, parent.empty_paranoid_model
1100+
assert_equal 3, parent.empty_paranoid_models.count
1101+
assert_equal many, parent.empty_paranoid_models
1102+
end
1103+
10581104
# Ensure that we're checking parent_type when restoring
10591105
def test_missing_restore_recursive_on_polymorphic_has_one_association
10601106
parent = ParentModel.create
@@ -1555,3 +1601,29 @@ class ParanoidBelongsTo < ActiveRecord::Base
15551601
belongs_to :paranoid_has_one
15561602
end
15571603
end
1604+
1605+
class ParanoidHasThroughRestoreParent < ActiveRecord::Base
1606+
acts_as_paranoid
1607+
1608+
has_one :paranoid_has_one_through, dependent: :destroy
1609+
has_one :empty_paranoid_model, through: :paranoid_has_one_through, dependent: :destroy
1610+
1611+
has_many :paranoid_has_many_throughs, dependent: :destroy
1612+
has_many :empty_paranoid_models, through: :paranoid_has_many_throughs, dependent: :destroy
1613+
end
1614+
1615+
class EmptyParanoidModel < ActiveRecord::Base
1616+
acts_as_paranoid
1617+
end
1618+
1619+
class ParanoidHasOneThrough < ActiveRecord::Base
1620+
acts_as_paranoid
1621+
belongs_to :paranoid_has_through_restore_parent
1622+
belongs_to :empty_paranoid_model, dependent: :destroy
1623+
end
1624+
1625+
class ParanoidHasManyThrough < ActiveRecord::Base
1626+
acts_as_paranoid
1627+
belongs_to :paranoid_has_through_restore_parent
1628+
belongs_to :empty_paranoid_model, dependent: :destroy
1629+
end

0 commit comments

Comments
 (0)