Module: IsParanoid::ClassMethods
- Defined in:
- lib/is_paranoid.rb
Instance Method Summary collapse
-
#delete_all(conditions = nil) ⇒ Object
Actually delete the model, bypassing the safety net.
-
#exists_with_destroyed(id) ⇒ Object
TODO: needs better implementation.
-
#has_many(association_id, options = {}, &extension) ⇒ Object
ensure that we respect the is_paranoid conditions when being loaded as a has_many :through NOTE: this only works if is_paranoid is declared before has_many relationships.
- #is_or_equals_not_destroyed ⇒ Object
-
#method_missing(name, *args, &block) ⇒ Object
find_with_destroyed and other blah_with_destroyed and blah_destroyed_only methods are defined here.
-
#restore(id, options = {}) ⇒ Object
Use update_all with an exclusive scope to restore undo the soft-delete.
-
#with_exclusive_scope(method_scoping = {}, &block) ⇒ Object
with_exclusive_scope is used internally by ActiveRecord when preloading associations.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name, *args, &block) ⇒ Object
find_with_destroyed and other blah_with_destroyed and blah_destroyed_only methods are defined here
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/is_paranoid.rb', line 117 def method_missing name, *args, &block if name.to_s =~ /^(.*)(_destroyed_only|_with_destroyed)$/ and self.respond_to?($1) self.extend(Module.new{ if $2 == '_with_destroyed' # Example: # def count_with_destroyed(*args) # self.with_exclusive_scope{ self.send(:count, *args) } # end define_method name do |*args| self.with_exclusive_scope{ self.send($1, *args) } end else # Example: # def count_destroyed_only(*args) # self.with_exclusive_scope do # with_scope({:find => { :conditions => ["#{destroyed_field} IS NOT ?", nil] }}) do # self.send(:count, *args) # end # end # end define_method name do |*args| self.with_exclusive_scope do with_scope({:find => { :conditions => ["#{self.table_name}.#{destroyed_field} IS NOT ?", field_not_destroyed] }}) do self.send($1, *args, &block) end end end end }) self.send(name, *args, &block) else super(name, *args, &block) end end |
Instance Method Details
#delete_all(conditions = nil) ⇒ Object
Actually delete the model, bypassing the safety net. Because this method is called internally by Model.delete(id) and on the delete method in each instance, we don’t need to specify those methods separately
57 58 59 |
# File 'lib/is_paranoid.rb', line 57 def delete_all conditions = nil self.with_exclusive_scope { super conditions } end |
#exists_with_destroyed(id) ⇒ Object
TODO: needs better implementation
111 112 113 |
# File 'lib/is_paranoid.rb', line 111 def exists_with_destroyed id self.with_exclusive_scope{ exists?(id)} end |
#has_many(association_id, options = {}, &extension) ⇒ Object
ensure that we respect the is_paranoid conditions when being loaded as a has_many :through NOTE: this only works if is_paranoid is declared before has_many relationships.
44 45 46 47 48 49 50 |
# File 'lib/is_paranoid.rb', line 44 def has_many(association_id, = {}, &extension) if .key?(:through) conditions = "#{[:through].to_s.pluralize}.#{destroyed_field} #{is_or_equals_not_destroyed}" [:conditions] = "(" + [[:conditions], conditions].compact.join(") AND (") + ")" end super end |
#is_or_equals_not_destroyed ⇒ Object
34 35 36 37 38 39 40 |
# File 'lib/is_paranoid.rb', line 34 def is_or_equals_not_destroyed if [nil, 'NULL'].include?(field_not_destroyed) 'IS NULL' else "= #{field_not_destroyed}" end end |
#restore(id, options = {}) ⇒ Object
Use update_all with an exclusive scope to restore undo the soft-delete. This bypasses update-related callbacks.
By default, restores cascade through associations that are belongs_to :dependent => :destroy and under is_paranoid. You can prevent restoration of associated models by passing :include_destroyed_dependents => false, for example:
Android.restore(:include_destroyed_dependents => false)
Alternatively you can specify which relationships to restore via :include, for example:
Android.restore(:include => [:parts, memories])
Please note that specifying :include means you’re not using :include_destroyed_dependents by default, though you can explicitly use both if you want all has_* relationships and specific belongs_to relationships, for example
Android.restore(:include => [:home, :planet], :include_destroyed_dependents => true)
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/is_paranoid.rb', line 82 def restore(id, = {}) .reverse_merge!({:include_destroyed_dependents => true}) unless [:include] with_exclusive_scope do update_all( "#{destroyed_field} = #{connection.quote(field_not_destroyed)}", primary_key.to_sym => id ) end self.reflect_on_all_associations.each do |association| if association.[:dependent] == :destroy and association.klass.respond_to?(:restore) dependent_relationship = association.macro.to_s =~ /^has/ if should_restore?(association.name, dependent_relationship, ) if dependent_relationship (association.klass, association.primary_key_name, id, ) else ( association.klass, association.klass.primary_key, self.first(id).send(association.primary_key_name), ) end end end end end |
#with_exclusive_scope(method_scoping = {}, &block) ⇒ Object
with_exclusive_scope is used internally by ActiveRecord when preloading associations. Unfortunately this is problematic for is_paranoid since we want preloaded is_paranoid items to still be scoped to their deleted conditions. so we override that here.
158 159 160 161 162 163 164 165 166 |
# File 'lib/is_paranoid.rb', line 158 def with_exclusive_scope(method_scoping = {}, &block) # this is rather hacky, suggestions for improvements appreciated... the idea # is that when the caller includes the method preload_associations, we want # to apply our is_paranoid conditions if caller.any?{|c| c =~ /\d+:in `preload_associations'$/} method_scoping.deep_merge!(:find => {:conditions => {destroyed_field => field_not_destroyed} }) end super method_scoping, &block end |