TwilightCoders/deleted_at

View on GitHub
lib/deleted_at/relation.rb

Summary

Maintainability
A
0 mins
Test Coverage
module DeletedAt

  module Relation

    def self.prepended(subclass)
      subclass.class_eval do
        attr_writer :deleted_at_scope
      end
    end

    def deleted_at_scope
      @deleted_at_scope ||= :Present
    end

    def deleted_at_select
      scoped_arel = case deleted_at_scope
      when :Deleted
        table.where(table[deleted_at[:column]].not_eq(nil))
      when :Present
        table.where(table[deleted_at[:column]].eq(nil))
      end
    end


    def deleted_at_subselect(arel)
      if (subselect = deleted_at_select)
        subselect.project(arel_columns(columns.map(&:name)))
        Arel::Nodes::TableAlias.new(Arel::Nodes::Grouping.new(subselect.ast), table_name)
      end
    end

    def build_arel
      super.tap do |arel|
        if (subselect = deleted_at_subselect(arel)) && !arel.froms.include?(subselect)
          DeletedAt.logger.debug("DeletedAt sub-selecting from #{subselect.to_sql}")
          arel.from(subselect)
        end
      end
    end

    # Deletes the records matching +conditions+ without instantiating the records
    # first, and hence not calling the +destroy+ method nor invoking callbacks. This
    # is a single SQL DELETE statement that goes straight to the database, much more
    # efficient than +destroy_all+. Be careful with relations though, in particular
    # <tt>:dependent</tt> rules defined on associations are not honored. Returns the
    # number of rows affected.
    #
    #   Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
    #   Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
    #   Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
    #
    # Both calls delete the affected posts all at once with a single DELETE statement.
    # If you need to destroy dependent associations or call your <tt>before_*</tt> or
    # +after_destroy+ callbacks, use the +destroy_all+ method instead.
    #
    # If an invalid method is supplied, +delete_all+ raises an ActiveRecord error:
    #
    #   Post.limit(100).delete_all
    #   # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
    def delete_all(*args)
      conditions = args.pop
      if conditions
        ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
          Passing conditions to delete_all is not supported in DeletedAt
          To achieve the same use where(conditions).delete_all.
        MESSAGE
      end
      update_all(klass.deleted_at_attributes)
    end
  end

end