Skip to content

Acts As Paranoid – Has One With Deleted

Out of the box acts_as_paranoid comes with the ability to define a belongs_to relationship as including deleted records (although to get it to work you need to add require File.dirname(FILE) + ’/lib/caboose/acts/belongs_to_with_deleted_association’ to the init.rb file):

class ModelA < ActiveRecord::Base
  belongs_to :model_b, :with_deleted => true
end

This works quite nicely for that ModelA, but what happens if you want to include the deleted ModelA’s from ModelB through a has_one relationship…… its falls over. However that is easily fixed.

Simply create a new file in the lib/caboose/acts directory called has_one_with_deleted_association.rb and paste in the following:

module Caboose # :nodoc:
  module Acts # :nodoc:
    class HasOneWithDeletedAssociation < ActiveRecord::Associations::HasOneAssociation
      private
      def find_target
        @reflection.klass.find_with_deleted(:first,
          :conditions => @finder_sql,
          :o rder      => @reflection.options[:order],
          :include    => @reflection.options[:include]
        )
      end
    end
  end
end

Then open up the init.rb file and change the contents to match:

require File.dirname(__FILE__) + '/lib/caboose/acts/belongs_to_with_deleted_association'
require File.dirname(__FILE__) + '/lib/caboose/acts/has_one_with_deleted_association'

class << ActiveRecord::Base
  def belongs_to_with_deleted(association_id, options = {})
    with_deleted = options.delete :with_deleted
    returning belongs_to_without_deleted(association_id, options) do
      if with_deleted
        reflection = reflect_on_association(association_id)
        association_accessor_methods(reflection,            Caboose::Acts::BelongsToWithDeletedAssociation)
        association_constructor_method(:build,  reflection, Caboose::Acts::BelongsToWithDeletedAssociation)
        association_constructor_method(:create, reflection, Caboose::Acts::BelongsToWithDeletedAssociation)
      end
    end
  end
  alias_method_chain :belongs_to, :deleted

  def has_one_with_deleted(association_id, options = {})
    with_deleted = options.delete :with_deleted
    returning has_one_without_deleted(association_id, options) do
      if with_deleted
        reflection = reflect_on_association(association_id)
        association_accessor_methods(reflection,            Caboose::Acts::HasOneWithDeletedAssociation)
        association_constructor_method(:build,  reflection, Caboose::Acts::HasOneWithDeletedAssociation)
        association_constructor_method(:create, reflection, Caboose::Acts::HasOneWithDeletedAssociation)
      end
    end
  end
  alias_method_chain :has_one, :deleted
end
ActiveRecord::Base.send :include, Caboose::Acts::Paranoid

Voila, you can now say:

class ModelB < ActiveRecord::Base
  has_one :model_a, :with_deleted => true
end

There is every chance this is a little hacky, but it seems to be working for me at the mo.