From cc19cefe708baae3fc082ab6b46628c97f4a82ef Mon Sep 17 00:00:00 2001 From: Kyle Ferrara Date: Wed, 23 Mar 2022 06:09:21 +0200 Subject: [PATCH 1/6] [Mysql] small updated to quote table names. Mysql 8 has keywords that might match table names which cause an exception when selecting with the dleted/deleted at columns. (#512) --- lib/paranoia.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/paranoia.rb b/lib/paranoia.rb index 959e5534..57bdf16e 100644 --- a/lib/paranoia.rb +++ b/lib/paranoia.rb @@ -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 From c0d1d9a3478bea09feb529d2dc0eb8c9b8d7e805 Mon Sep 17 00:00:00 2001 From: Mathieu Jobin Date: Wed, 23 Mar 2022 15:09:07 +0900 Subject: [PATCH 2/6] release v2.6.0 - bump version, update changelog --- CHANGELOG.md | 8 ++++++++ lib/paranoia/version.rb | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33d169db..6cec5dd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # paranoia Changelog +## 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 diff --git a/lib/paranoia/version.rb b/lib/paranoia/version.rb index 946e0abb..9b8b6061 100644 --- a/lib/paranoia/version.rb +++ b/lib/paranoia/version.rb @@ -1,3 +1,3 @@ module Paranoia - VERSION = '2.5.3'.freeze + VERSION = '2.6.0'.freeze end From 09e3a9fb3b3b47c976388c103df05438267dacc6 Mon Sep 17 00:00:00 2001 From: Bogdanov Anton Date: Wed, 16 Nov 2022 03:11:40 +0400 Subject: [PATCH 3/6] skip updating paranoia_destroy_attributes for records while really_destroy! (#535) --- CHANGELOG.md | 5 +++++ README.md | 10 +++++++++- lib/paranoia.rb | 10 ++++++---- lib/paranoia/version.rb | 2 +- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cec5dd1..79424e3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # paranoia Changelog +## 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. diff --git a/README.md b/README.md index cc6746e8..cf898b8f 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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 diff --git a/lib/paranoia.rb b/lib/paranoia.rb index 57bdf16e..a7e87c77 100644 --- a/lib/paranoia.rb +++ b/lib/paranoia.rb @@ -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? @@ -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 diff --git a/lib/paranoia/version.rb b/lib/paranoia/version.rb index 9b8b6061..e9227a35 100644 --- a/lib/paranoia/version.rb +++ b/lib/paranoia/version.rb @@ -1,3 +1,3 @@ module Paranoia - VERSION = '2.6.0'.freeze + VERSION = '2.6.1'.freeze end From cb04a4d4b8c3565b67aae45bc5dfceb03f8141b0 Mon Sep 17 00:00:00 2001 From: Emil Ong Date: Mon, 5 Jun 2023 13:05:40 -0700 Subject: [PATCH 4/6] Use correct time class for earlier Rails versions (#541) --- test/paranoia_test.rb | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/paranoia_test.rb b/test/paranoia_test.rb index c6c064ca..ddc1f325 100644 --- a/test/paranoia_test.rb +++ b/test/paranoia_test.rb @@ -387,14 +387,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 @@ -403,7 +411,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 From 894269fab3ce38dbdc3c5453f44ef06815250487 Mon Sep 17 00:00:00 2001 From: Emil Ong Date: Mon, 5 Jun 2023 13:09:47 -0700 Subject: [PATCH 5/6] Recursive restore with has_many/one through assocs (#441) The query to find deleted has_many or has_one through associations was being generated incorrectly because of specifying the wrong foreign key for the table. This change uses the has_one/has_many model's primary key as the foreign key. --- lib/paranoia.rb | 9 ++++-- test/paranoia_test.rb | 66 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/lib/paranoia.rb b/lib/paranoia.rb index a7e87c77..6fc464a5 100644 --- a/lib/paranoia.rb +++ b/lib/paranoia.rb @@ -217,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 @@ -226,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) diff --git a/test/paranoia_test.rb b/test/paranoia_test.rb index ddc1f325..e302f6b8 100644 --- a/test/paranoia_test.rb +++ b/test/paranoia_test.rb @@ -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 @@ -1063,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 @@ -1563,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 From 33e433a28e55588319fdb50e2e05e72af54ae365 Mon Sep 17 00:00:00 2001 From: Mathieu Jobin <99191+mathieujobin@users.noreply.github.com> Date: Tue, 6 Jun 2023 05:35:45 +0900 Subject: [PATCH 6/6] Release 2.6.2 + Redo Github Actions matrix using includes (#537) * test-out-experimental-with-non-existing-ruby-3.2 * redo build matrix with includes * shorten the matrix with standard builds * only jruby 9.4 for rails 7 * remove experimental stuff * bump version. changelog, comment out jruby * remove dup --- .github/workflows/build.yml | 58 +++++++++++++++---------------------- CHANGELOG.md | 5 ++++ lib/paranoia/version.rb | 2 +- 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b3131be4..dd920c3f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 79424e3f..5e93e080 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # 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! diff --git a/lib/paranoia/version.rb b/lib/paranoia/version.rb index e9227a35..afe1017d 100644 --- a/lib/paranoia/version.rb +++ b/lib/paranoia/version.rb @@ -1,3 +1,3 @@ module Paranoia - VERSION = '2.6.1'.freeze + VERSION = '2.6.2'.freeze end