Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .rspec

This file was deleted.

28 changes: 27 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,41 @@ after_script:
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT

addons:
postgresql: 9.3
postgresql: 9.5

rvm:
- 2.6
- 2.5
- 2.4
- 2.3

gemfile:
- gemfiles/activerecord-6.0.Gemfile
- gemfiles/activerecord-5.2.Gemfile
- gemfiles/activerecord-5.1.Gemfile
- gemfiles/activerecord-5.0.Gemfile
- gemfiles/activerecord-4.2.Gemfile

jobs:
include:
- gemfile: gemfiles/activerecord-edge.Gemfile
rvm: 2.5
env: EDGE_TESTING=true
- gemfile: gemfiles/activerecord-edge.Gemfile
rvm: 2.6
env: EDGE_TESTING=true

allow_failures:
- gemfile: gemfiles/activerecord-edge.Gemfile

exclude:
- rvm: 2.6
gemfile: gemfiles/activerecord-4.2.Gemfile
- rvm: 2.3
gemfile: gemfiles/activerecord-6.0.Gemfile
- rvm: 2.4
gemfile: gemfiles/activerecord-6.0.Gemfile
- rvm: 2.3
gemfile: gemfiles/activerecord-edge.Gemfile
- rvm: 2.4
gemfile: gemfiles/activerecord-edge.Gemfile
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
# DeletedAt

## 0.6.0 _(May 25, 2019)_
- Major overhaul of functionality, extracted to [`ActiveRecord::Framing`](https://github.com/TwilightCoders/active_record-framing) and refined into gem
- Consumes `ActiveRecord::Framing`
- Simplified gem and distilled to only domain specific functionality
- Adds official support for ActiveSupport up to 5.2
- Adds official support for Ruby 2.6
- Unbound upper constraint for ActiveSupport
- 100% test coverage

## 0.5.0 _(June 25, 2018)_
- Removed use of invasive views in preference of sub-selects
- Dropped support for Ruby 2.0, 2.1, 2.2
- Dropped support for Rails 4.1
- Dropped support for ActiveSupport 4.1
- Default `deleted_at` options using `Proc`

## 0.4.0 _(Never Released)_
- Specs for Rails 4.0-5.1
- Specs for ActiveSupport 4.0-5.1
- Uses `combustion` gem for cleaner and more comprehensive testing
- Added badges to ReadMe
- Using `:prepend` to leverage ancestry chain
Expand Down
13 changes: 0 additions & 13 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,3 @@
source 'https://rubygems.org'

gemspec

group :test do

# Generates coverage stats of specs
gem 'simplecov'

gem 'rspec'

gem 'database_cleaner'

gem 'combustion'

end
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2017 Dale Stevens
Copyright (c) 2019 Dale Stevens

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ Hide your "deleted" data (unless specifically asked for) [without resorting to](
## Requirements

- Ruby 2.3+
- ActiveRecord 4.1+
- ActiveRecord 4.2+

_Note: Be sure to check the builds to be sure your version is in-fact supported. The requirements are left unbounded on the upper constraint for posterity, but may not be gaurenteed to work._

## Installation

Expand Down Expand Up @@ -102,10 +104,9 @@ class IndexDeletedAtColumns < ActiveRecord::Migration
end
```


## [Upgrading](#upgrading)

If you've used `deleted_at` prior to v0.5.0, you'll need to migrate your schema. The new version of `deleted_at` no longer uses views, instead constructing a subselect on the relations. This significantly reduces code polution and monkey patching, as well as reducing the runtime memory usage for rails. Your Database will look (and be) a lot cleaner with no `deleted_at` views and your ERDs will be much cleaner as well.
If you've used `deleted_at` prior to v0.5.0, you'll need to migrate your schema. The new version of `deleted_at` no longer uses views, instead constructing a common table expression (CTE) on the relations. This significantly reduces code polution and monkey patching, as well as reducing the runtime memory usage for rails. Your Database will look (and be) a lot cleaner with no `deleted_at` views (and your ERDs will be much cleaner as well).

Here is an example of a migration for upgrading
```ruby
Expand Down
18 changes: 11 additions & 7 deletions deleted_at.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,19 @@ Gem::Specification.new do |spec|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ['lib']

rails_versions = ['>= 4.2', '< 6']
rails_versions = ['>= 4.2', '< 6.1']
spec.required_ruby_version = '>= 2.3'

spec.add_runtime_dependency 'activerecord', rails_versions

spec.add_development_dependency 'pg', '~> 0'
spec.add_development_dependency 'pry-byebug', '~> 3'
spec.add_development_dependency 'bundler', '~> 1.3'
spec.add_development_dependency 'rake', '~> 12.0'
spec.add_development_dependency 'combustion', '~> 0.7'
spec.add_runtime_dependency 'active_record-framing', '~> 0.1.0-10'

spec.add_development_dependency 'pg'
spec.add_development_dependency 'bundler'
spec.add_development_dependency 'rspec'
spec.add_development_dependency 'combustion'
spec.add_development_dependency 'pry-byebug'
spec.add_development_dependency 'rake'
spec.add_development_dependency 'database_cleaner'
spec.add_development_dependency 'simplecov'

end
1 change: 1 addition & 0 deletions gemfiles/activerecord-4.2.Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
eval_gemfile File.join(File.dirname(__FILE__), "../Gemfile")

gem 'pg', '~> 0.20'
gem 'activerecord', '~> 4.2.0'
3 changes: 3 additions & 0 deletions gemfiles/activerecord-6.0.Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
eval_gemfile File.join(File.dirname(__FILE__), "../Gemfile")

gem 'activerecord', '~> 6.0.0'
3 changes: 3 additions & 0 deletions gemfiles/activerecord-edge.Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
eval_gemfile File.join(File.dirname(__FILE__), "../Gemfile")

gem 'activerecord', github: 'rails/rails', require: 'active_record'
7 changes: 6 additions & 1 deletion lib/deleted_at.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
require 'active_record-framing'

require 'deleted_at/version'
require 'deleted_at/relation'
require 'deleted_at/core'
require 'deleted_at/table_definition'
require 'deleted_at/railtie' if defined?(Rails::Railtie)

module DeletedAt

MissingColumn = Class.new(StandardError)
MissingColumnError = Class.new(StandardError)

DEFAULT_OPTIONS = {
column: :deleted_at,
Expand Down
57 changes: 40 additions & 17 deletions lib/deleted_at/active_record.rb
Original file line number Diff line number Diff line change
@@ -1,50 +1,73 @@
require 'active_record'
require 'deleted_at/relation'

module DeletedAt
module ActiveRecord

def self.prepended(subclass)
subclass.init_deleted_at_relations
subclass.extend(ClassMethods)

subclass.class_eval do
init_deleted_at_relations
default_frame { where(deleted_at[:column] => nil) }
frame :all, -> {}
frame :deleted, -> { where.not(deleted_at[:column] => nil) }
end
end

def initialize(*args)
super
@destroyed = !deleted_at.nil?
super.tap do
@destroyed = deleted_at_nil?
end
end

def destroy
soft_delete
super
run_callbacks(:destroy) do
soft_delete
end
end

def delete
soft_delete
super
end

def destroy!
run_callbacks(:destroy) do
soft_delete
end
end

def delete!
soft_delete
end

private

def soft_delete
return if destroyed?
update_columns(self.class.deleted_at_attributes)
@destroyed = true
freeze
self
end

def deleted_at_nil?
!read_attribute(self.class.deleted_at[:column]).nil?
end

module ClassMethods

def inherited(subclass)
super
subclass.init_deleted_at_relations
# subclass.init_deleted_at_relations if deleted_at[:inherit]
end

def deleted_at_attributes
attributes = {
deleted_at[:column] => deleted_at[:proc].call
}
end

def const_missing(const)
case const
when :All, :Deleted
all.tap do |_query|
_query.deleted_at_scope = const
end
else super
def init_deleted_at_relations
instance_variable_get(:@relation_delegate_cache).each do |base, klass|
klass.send(:prepend, DeletedAt::Relation)
end
end

Expand Down
29 changes: 11 additions & 18 deletions lib/deleted_at/core.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
require 'deleted_at/active_record'

module DeletedAt

module Core
Expand All @@ -15,39 +14,33 @@ class << subclass

def self.raise_missing(klass)
message = "Missing `#{klass.deleted_at[:column]}` in `#{klass.name}` when trying to employ `deleted_at`"
raise(DeletedAt::MissingColumn, message)
raise(DeletedAt::MissingColumnError, message)
end

def self.has_deleted_at_column?(klass)
klass.columns.map(&:name).include?(klass.deleted_at.dig(:column).to_s)
end

def self.deleted_at_ready?(klass)
!::DeletedAt.disabled? &&
klass != ::ActiveRecord::Base &&
!klass.abstract_class? &&
klass.connected? &&
klass.table_exists? &&
!(klass < DeletedAt::ActiveRecord)
end

module ClassMethods

def with_deleted_at(options={}, &block)
self.deleted_at = DeletedAt::DEFAULT_OPTIONS.merge(options)
self.deleted_at[:proc] = block if block_given?

return if ::DeletedAt.disabled?

return unless Core.deleted_at_ready?(self)
DeletedAt::Core.raise_missing(self) unless Core.has_deleted_at_column?(self)

self.prepend(DeletedAt::ActiveRecord)

end

def deleted_at_attributes
attributes = {
deleted_at[:column] => deleted_at[:proc].call
}
end

def init_deleted_at_relations
instance_variable_get(:@relation_delegate_cache).each do |base, klass|
klass.send(:prepend, DeletedAt::Relation)
end
end

end # End ClassMethods

end
Expand Down
10 changes: 6 additions & 4 deletions lib/deleted_at/legacy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ def self.uninstall(model)
def self.install(model)
return false unless Core.has_deleted_at_column?(model)

install_present_view(model)
install_deleted_view(model)
model.unframed do
install_present_view(model)
install_deleted_view(model)
end
end

private
Expand All @@ -24,7 +26,7 @@ def self.install_present_view(model)
model.connection.execute("ALTER TABLE \"#{present_table_name}\" RENAME TO \"#{model.table_name}\"")
model.connection.execute <<-SQL
CREATE OR REPLACE VIEW "#{present_table_name}"
AS #{ model.select('*').where(model.deleted_at_column => nil).to_sql }
AS #{ model.select('*').where(model.deleted_at[:column] => nil).to_sql }
SQL
end
end
Expand All @@ -36,7 +38,7 @@ def self.install_deleted_view(model)
while_spoofing_table_name(model, all_table(model)) do
model.connection.execute <<-SQL
CREATE OR REPLACE VIEW "#{table_name}"
AS #{ model.select('*').where.not(model.deleted_at_column => nil).to_sql }
AS #{ model.select('*').where.not(model.deleted_at[:column] => nil).to_sql }
SQL
end
end
Expand Down
2 changes: 0 additions & 2 deletions lib/deleted_at/railtie.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
require 'rails/railtie'
require 'deleted_at/core'
require 'deleted_at/table_definition'

module DeletedAt
class Railtie < Rails::Railtie
Expand Down
Loading