diff --git a/README.md b/README.md index 502516ef..ebf8275f 100644 --- a/README.md +++ b/README.md @@ -94,22 +94,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 @@ -180,12 +164,6 @@ Client.restore(id, :recursive => true) client.restore(: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. #### About indexes: @@ -221,6 +199,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. diff --git a/lib/paranoia.rb b/lib/paranoia.rb index 3d8c5a4a..5f4993b0 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 + [: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 @@ -172,25 +174,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 diff --git a/test/paranoia_test.rb b/test/paranoia_test.rb index c36e959c..1607bc8a 100644 --- a/test/paranoia_test.rb +++ b/test/paranoia_test.rb @@ -453,6 +453,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 @@ -871,15 +879,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}