From 9937512664d2338ffe1fe3cd522e84dfbf0cafa6 Mon Sep 17 00:00:00 2001 From: Emil Ong Date: Wed, 25 Apr 2018 14:40:18 -0700 Subject: [PATCH] Recursive restore with has_many/one through assocs 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 c6c064ca..907912f7 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 @@ -1055,6 +1059,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 @@ -1555,3 +1593,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