Module: IsParanoid::ClassMethods

Defined in:
lib/is_paranoid.rb

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ Object

find_with_destroyed and other blah_with_destroyed and blah_destroyed_only methods are defined here



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/is_paranoid.rb', line 89

def method_missing name, *args
  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)
            end
          end
        end

      end
    })
  self.send(name, *args)
  else
    super(name, *args)
  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



34
35
36
# File 'lib/is_paranoid.rb', line 34

def delete_all conditions = nil
  self.with_exclusive_scope { super conditions }
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)


59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/is_paranoid.rb', line 59

def restore(id, options = {})
  options.reverse_merge!({:include_destroyed_dependents => true}) unless options[:include]
  with_exclusive_scope do
    update_all(
    "#{destroyed_field} = #{connection.quote(field_not_destroyed)}",
    "id = #{id}"
    )
  end

  self.reflect_on_all_associations.each do |association|
    if association.options[:dependent] == :destroy and association.klass.respond_to?(:restore)
      dependent_relationship = association.macro.to_s =~ /^has/
      if should_restore?(association.name, dependent_relationship, options)
        if dependent_relationship
          restore_related(association.klass, association.primary_key_name, id, options)
        else
          restore_related(
            association.klass,
            association.klass.primary_key,
            self.first(id).send(association.primary_key_name),
            options
          )
        end
      end
    end
  end
end