From 5058797e1cfa857aa75419bb34897b589dc008b0 Mon Sep 17 00:00:00 2001 From: Nikolay Norkin Date: Tue, 7 Apr 2015 12:01:51 +0300 Subject: [PATCH 1/5] real_destroy callback --- lib/paranoia.rb | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/paranoia.rb b/lib/paranoia.rb index 6a5c975d..d05fc380 100644 --- a/lib/paranoia.rb +++ b/lib/paranoia.rb @@ -47,7 +47,7 @@ def restore(id_or_ids, opts = {}) module Callbacks def self.extended(klazz) - klazz.define_callbacks :restore + klazz.define_callbacks :restore, :real_destroy klazz.define_singleton_method("before_restore") do |*args, &block| set_callback(:restore, :before, *args, &block) @@ -172,25 +172,29 @@ def self.acts_as_paranoid(options={}) alias :destroy_without_paranoia :destroy def really_destroy! - dependent_reflections = self.class.reflections.select do |name, reflection| - reflection.options[:dependent] == :destroy - end - if dependent_reflections.any? - dependent_reflections.each do |name, reflection| - association_data = self.send(name) - # has_one association can return nil - # .paranoid? will work for both instances and classes - if association_data && association_data.paranoid? - if reflection.collection? - association_data.with_deleted.each(&:really_destroy!) - else - association_data.really_destroy! + transaction do + run_callbacks(:real_destroy) do + dependent_reflections = self.class.reflections.select do |name, reflection| + reflection.options[:dependent] == :destroy + end + if dependent_reflections.any? + dependent_reflections.each do |name, reflection| + association_data = self.send(name) + # has_one association can return nil + # .paranoid? will work for both instances and classes + if association_data && association_data.paranoid? + if reflection.collection? + association_data.with_deleted.each(&:really_destroy!) + else + association_data.really_destroy! + end + end end end + write_attribute(paranoia_column, current_time_from_proper_timezone) + destroy_without_paranoia end end - write_attribute(paranoia_column, current_time_from_proper_timezone) - destroy_without_paranoia end include Paranoia From 10f37a8441c96a33397ad91e8abc56050a8235ef Mon Sep 17 00:00:00 2001 From: Nikolay Norkin Date: Tue, 7 Apr 2015 12:19:06 +0300 Subject: [PATCH 2/5] Dynamic callbacks defining --- lib/paranoia.rb | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/paranoia.rb b/lib/paranoia.rb index d05fc380..2b4d92af 100644 --- a/lib/paranoia.rb +++ b/lib/paranoia.rb @@ -47,18 +47,20 @@ def restore(id_or_ids, opts = {}) module Callbacks def self.extended(klazz) - klazz.define_callbacks :restore, :real_destroy + %i(restore real_destroy).each do |callback_name| + klazz.define_callbacks callback_name - klazz.define_singleton_method("before_restore") do |*args, &block| - set_callback(:restore, :before, *args, &block) - end + klazz.define_singleton_method("before_#{callback_name}") do |*args, &block| + set_callback(callback_name, :before, *args, &block) + end - klazz.define_singleton_method("around_restore") do |*args, &block| - set_callback(:restore, :around, *args, &block) - end + klazz.define_singleton_method("around_#{callback_name}") do |*args, &block| + set_callback(callback_name, :around, *args, &block) + end - klazz.define_singleton_method("after_restore") do |*args, &block| - set_callback(:restore, :after, *args, &block) + klazz.define_singleton_method("after_#{callback_name}") do |*args, &block| + set_callback(callback_name, :after, *args, &block) + end end end end From 348b1c72d8c486ac2db6dec8ad359c800700bda5 Mon Sep 17 00:00:00 2001 From: Nikolay Norkin Date: Tue, 7 Apr 2015 12:54:11 +0300 Subject: [PATCH 3/5] Tests --- test/paranoia_test.rb | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/test/paranoia_test.rb b/test/paranoia_test.rb index 1a5e4267..f6dce06f 100644 --- a/test/paranoia_test.rb +++ b/test/paranoia_test.rb @@ -451,6 +451,14 @@ def test_real_destroy_dependent_destroy_after_normal_destroy_does_not_delete_oth assert RelatedModel.unscoped.exists?(child_2.id) end + def test_really_destroy_behavior_for_callbacks + model = CallbackModel.new + model.save + model.really_destroy! + + assert model.instance_variable_get(:@real_destroy_callback_called) + end + def test_really_delete model = ParanoidModel.new model.save @@ -843,15 +851,16 @@ class PlainModel < ActiveRecord::Base class CallbackModel < ActiveRecord::Base acts_as_paranoid - before_destroy {|model| model.instance_variable_set :@destroy_callback_called, true } - before_restore {|model| model.instance_variable_set :@restore_callback_called, true } - before_update {|model| model.instance_variable_set :@update_callback_called, true } - before_save {|model| model.instance_variable_set :@save_callback_called, true} + before_destroy { |model| model.instance_variable_set :@destroy_callback_called, true } + before_restore { |model| model.instance_variable_set :@restore_callback_called, true } + before_update { |model| model.instance_variable_set :@update_callback_called, true } + before_save { |model| model.instance_variable_set :@save_callback_called, true} + before_real_destroy { |model| model.instance_variable_set :@real_destroy_callback_called, true } - after_destroy {|model| model.instance_variable_set :@after_destroy_callback_called, true } - after_commit {|model| model.instance_variable_set :@after_commit_callback_called, true } + after_destroy { |model| model.instance_variable_set :@after_destroy_callback_called, true } + after_commit { |model| model.instance_variable_set :@after_commit_callback_called, true } - validate {|model| model.instance_variable_set :@validate_called, true } + validate { |model| model.instance_variable_set :@validate_called, true } def remove_called_variables instance_variables.each {|name| (name.to_s.end_with?('_called')) ? remove_instance_variable(name) : nil} From f5bd60f2e8a773d7f4c489848627b2fc372cbaa2 Mon Sep 17 00:00:00 2001 From: Nikolay Norkin Date: Tue, 7 Apr 2015 12:56:11 +0300 Subject: [PATCH 4/5] Callbacks array definition for old ruby versions --- lib/paranoia.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/paranoia.rb b/lib/paranoia.rb index 2b4d92af..4f55e206 100644 --- a/lib/paranoia.rb +++ b/lib/paranoia.rb @@ -47,7 +47,7 @@ def restore(id_or_ids, opts = {}) module Callbacks def self.extended(klazz) - %i(restore real_destroy).each do |callback_name| + [:restore, :real_destroy].each do |callback_name| klazz.define_callbacks callback_name klazz.define_singleton_method("before_#{callback_name}") do |*args, &block| From b468111df5eb9498577d65697c14bb9bef98e074 Mon Sep 17 00:00:00 2001 From: Nikolay Norkin Date: Tue, 30 Jun 2015 14:19:06 +0300 Subject: [PATCH 5/5] Update README.md --- README.md | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 32f23704..9f62b267 100644 --- a/README.md +++ b/README.md @@ -90,22 +90,6 @@ If you really want it gone *gone*, call `really_destroy!`: # => client ``` -If you want a method to be called on destroy, simply provide a `before_destroy` callback: - -``` ruby -class Client < ActiveRecord::Base - acts_as_paranoid - - before_destroy :some_method - - def some_method - # do stuff - end - - # ... -end -``` - If you want to use a column other than `deleted_at`, you can pass it as an option: ``` ruby @@ -172,12 +156,6 @@ If you want to restore a record and their dependently destroyed associated recor Client.restore(id, :recursive => true) ``` -If you want callbacks to trigger before a restore: - -``` ruby -before_restore :callback_name_goes_here -``` - For more information, please look at the tests. ## Acts As Paranoid Migration @@ -194,6 +172,24 @@ You can replace the older `acts_as_paranoid` methods as follows: The `recover` method in `acts_as_paranoid` runs `update` callbacks. Paranoia's `restore` method does not do this. +## Callbacks + +Paranoia provides few callbacks. It triggers `destroy` callback when the record is marked as deleted and `real_destroy` when the record is completely removed from database. It also calls `restore` callback when record is restored via paranoia + +For example if you want to index you records in some search engine you can do like this: + +```ruby +class Product < ActiveRecord::Base + acts_as_paranoid + + after_destroy :update_document_in_search_engine + after_restore :update_document_in_search_engine + after_real_destroy :remove_document_from_search_engine +end +``` + +You can use these events just like regular Rails callbacks with before, after and around hooks. + ## License This gem is released under the MIT license.