Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added with_deleted option to has_one relationship #324

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
36 changes: 32 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ or in the recover statement

```ruby
Paranoiac.only_deleted.where("name = ?", "not dead yet").first
.recover(recovery_window: 30.seconds)
.recover(recovery_window: 30.seconds)
```

### recover!
Expand Down Expand Up @@ -245,7 +245,7 @@ p1.recover #=> fails validation!

### Status

A paranoid object could be deleted or destroyed fully.
A paranoid object could be deleted or destroyed fully.

You can check if the object is deleted with the `deleted?` helper

Expand Down Expand Up @@ -311,8 +311,9 @@ Paranoiac.pretty.only_deleted.count #=> 1
Associations are also supported.

From the simplest behaviors you'd expect to more nifty things like the ones
mentioned previously or the usage of the `:with_deleted` option with
`belongs_to`
mentioned previously or the usage of the `:with_deleted` option with:

#### belongs_to

```ruby
class Parent < ActiveRecord::Base
Expand All @@ -336,6 +337,33 @@ child.parent #=> nil
child.parent_including_deleted #=> Parent (it works!)
```

#### has_one

```ruby
class Parent < ActiveRecord::Base
has_one :child, class_name: "ParanoiacChild"
has_one :child_with_deleted, class_name: "ParanoiacChild", with_deleted: true
end

class ParanoiacChild < ActiveRecord::Base
acts_as_paranoid
belongs_to :parent
end

parent = Parent.first

child = ParanoiacChild.create
parent.child = child

parent.child #=> ParanoiacChild

child.destroy
parent.reload

parent.child #=> nil
parent.child_with_deleted #=> ParanoiacChild
```

### Callbacks

There are couple of callbacks that you may use when dealing with deletion and
Expand Down
18 changes: 15 additions & 3 deletions lib/acts_as_paranoid/associations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,25 @@ def self.included(base)
class << base
alias_method :belongs_to_without_deleted, :belongs_to
alias_method :belongs_to, :belongs_to_with_deleted

alias_method :has_one_without_deleted, :has_one
alias_method :has_one, :has_one_with_deleted
end
end

module ClassMethods
def belongs_to_with_deleted(target, scope = nil, options = {})
relation_with_deleted(target, relation: :belongs_to, scope: scope, options: options)
end

def has_one_with_deleted(target, scope = nil, options = {})
relation_with_deleted(target, relation: :has_one, scope: scope, options: options)
end

private

# @param relation [String,Symbol] :belongs_to or :has_one
def relation_with_deleted(target, relation:, scope: nil, options: {})
if scope.is_a?(Hash)
options = scope
scope = nil
Expand All @@ -23,7 +37,7 @@ def belongs_to_with_deleted(target, scope = nil, options = {})
scope = make_scope_with_deleted(scope)
end

result = belongs_to_without_deleted(target, scope, **options)
result = send("#{relation}_without_deleted", target, scope, **options)

if with_deleted
options = result.values.last.options
Expand All @@ -34,8 +48,6 @@ def belongs_to_with_deleted(target, scope = nil, options = {})
result
end

private

def make_scope_with_deleted(scope)
if scope
old_scope = scope
Expand Down
80 changes: 63 additions & 17 deletions test/test_associations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,16 @@ class ParanoidHasManyDependant < ActiveRecord::Base
-> { where(name: "hello").includes(:not_paranoid) },
class_name: "ParanoidTime", foreign_key: :paranoid_time_id
belongs_to :paranoid_time_with_deleted, class_name: "ParanoidTime",
foreign_key: :paranoid_time_id,
with_deleted: true
foreign_key: :paranoid_time_id,
with_deleted: true
belongs_to :paranoid_time_with_scope_with_deleted,
-> { where(name: "hello").includes(:not_paranoid) },
class_name: "ParanoidTime", foreign_key: :paranoid_time_id,
with_deleted: true
belongs_to :paranoid_time_polymorphic_with_deleted, class_name: "ParanoidTime",
foreign_key: :paranoid_time_id,
polymorphic: true,
with_deleted: true
foreign_key: :paranoid_time_id,
polymorphic: true,
with_deleted: true

belongs_to :paranoid_belongs_dependant, dependent: :destroy
end
Expand All @@ -77,6 +77,16 @@ class ParanoidBelongsDependant < ActiveRecord::Base
acts_as_paranoid

has_many :paranoid_has_many_dependants
has_one :paranoid_has_one_dependant
has_one :paranoid_has_one_dependant_with_deleted,
class_name: "ParanoidHasOneDependant",
with_deleted: true
end

class ParanoidHasOneDependant < ActiveRecord::Base
acts_as_paranoid

belongs_to :paranoid_belongs_dependant, dependent: :destroy
end

class ParanoidTime < ActiveRecord::Base
Expand Down Expand Up @@ -196,6 +206,14 @@ def setup
timestamps t
end

create_table :paranoid_has_one_dependants do |t|
t.string :name
t.datetime :deleted_at
t.integer :paranoid_belongs_dependant_id

timestamps t
end

create_table :paranoid_destroy_companies do |t|
t.string :name
t.datetime :deleted_at
Expand Down Expand Up @@ -357,7 +375,7 @@ def test_belongs_to_with_scope_option

expected_includes_values = ParanoidTime.includes(:not_paranoid).includes_values
includes_values = paranoid_has_many_dependant
.association(:paranoid_time_with_scope).scope.includes_values
.association(:paranoid_time_with_scope).scope.includes_values

assert_equal expected_includes_values, includes_values

Expand Down Expand Up @@ -385,7 +403,7 @@ def test_belongs_to_with_scope_and_deleted_option
includes_values = ParanoidTime.includes(:not_paranoid).includes_values

assert_equal includes_values, paranoid_has_many_dependant
.association(:paranoid_time_with_scope_with_deleted).scope.includes_values
.association(:paranoid_time_with_scope_with_deleted).scope.includes_values

paranoid_time = ParanoidTime.create(name: "not-hello")
paranoid_has_many_dependant.paranoid_time = paranoid_time
Expand All @@ -397,19 +415,19 @@ def test_belongs_to_with_scope_and_deleted_option
paranoid_has_many_dependant.reload

assert_equal paranoid_time, paranoid_has_many_dependant
.paranoid_time_with_scope_with_deleted
.paranoid_time_with_scope_with_deleted

paranoid_time.destroy
paranoid_has_many_dependant.reload

assert_equal paranoid_time, paranoid_has_many_dependant
.paranoid_time_with_scope_with_deleted
.paranoid_time_with_scope_with_deleted
end

def test_belongs_to_with_deleted
paranoid_time = ParanoidTime.create! name: "paranoid"
paranoid_has_many_dependant = paranoid_time.paranoid_has_many_dependants
.create(name: "dependant!")
.create(name: "dependant!")

assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time
assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time_with_deleted
Expand Down Expand Up @@ -470,17 +488,17 @@ def test_belongs_to_with_deleted_as_inverse_of_has_many
def test_belongs_to_polymorphic_with_deleted
paranoid_time = ParanoidTime.create! name: "paranoid"
paranoid_has_many_dependant = ParanoidHasManyDependant
.create!(name: "dependant!", paranoid_time_polymorphic_with_deleted: paranoid_time)
.create!(name: "dependant!", paranoid_time_polymorphic_with_deleted: paranoid_time)

assert_equal paranoid_time, paranoid_has_many_dependant.paranoid_time
assert_equal paranoid_time, paranoid_has_many_dependant
.paranoid_time_polymorphic_with_deleted
.paranoid_time_polymorphic_with_deleted

paranoid_time.destroy

assert_nil paranoid_has_many_dependant.reload.paranoid_time
assert_equal paranoid_time, paranoid_has_many_dependant
.reload.paranoid_time_polymorphic_with_deleted
.reload.paranoid_time_polymorphic_with_deleted
end

def test_belongs_to_nil_polymorphic_with_deleted
Expand All @@ -500,7 +518,7 @@ def test_belongs_to_nil_polymorphic_with_deleted

def test_belongs_to_options
paranoid_time = ParanoidHasManyDependant.reflections
.with_indifferent_access[:paranoid_time]
.with_indifferent_access[:paranoid_time]

assert_equal :belongs_to, paranoid_time.macro
assert_nil paranoid_time.options[:with_deleted]
Expand All @@ -509,15 +527,15 @@ def test_belongs_to_options
def test_belongs_to_with_deleted_options
paranoid_time_with_deleted =
ParanoidHasManyDependant.reflections
.with_indifferent_access[:paranoid_time_with_deleted]
.with_indifferent_access[:paranoid_time_with_deleted]

assert_equal :belongs_to, paranoid_time_with_deleted.macro
assert paranoid_time_with_deleted.options[:with_deleted]
end

def test_belongs_to_polymorphic_with_deleted_options
paranoid_time_polymorphic_with_deleted = ParanoidHasManyDependant.reflections
.with_indifferent_access[:paranoid_time_polymorphic_with_deleted]
.with_indifferent_access[:paranoid_time_polymorphic_with_deleted]

assert_equal :belongs_to, paranoid_time_polymorphic_with_deleted.macro
assert paranoid_time_polymorphic_with_deleted.options[:with_deleted]
Expand All @@ -542,6 +560,34 @@ def test_only_find_associated_records_when_finding_with_paranoid_deleted
assert_equal [child], parent.paranoid_has_many_dependants.with_deleted.to_a
end

def test_has_one_with_deleted_options
parent =
ParanoidBelongsDependant.reflections
.with_indifferent_access[:paranoid_has_one_dependant_with_deleted]

assert_equal :has_one, parent.macro
assert parent.options[:with_deleted]
end

def test_has_one_associated_records_when_finding_with_paranoid_deleted
parent = ParanoidBelongsDependant.create
child = ParanoidHasOneDependant.create
parent.paranoid_has_one_dependant = child

unrelated_parent = ParanoidBelongsDependant.create
unrelated_child = ParanoidHasOneDependant.create
unrelated_parent.paranoid_has_one_dependant = unrelated_child

child.destroy

assert_paranoid_deletion(child)

parent.reload

assert_nil parent.paranoid_has_one_dependant
assert_equal child, parent.paranoid_has_one_dependant_with_deleted
end

def test_join_with_model_with_deleted
obj = ParanoidHasManyDependant.create(paranoid_time: ParanoidTime.create)

Expand Down Expand Up @@ -569,7 +615,7 @@ def test_includes_with_deleted
paranoid_time.destroy

ParanoidHasManyDependant.with_deleted
.includes(:paranoid_time_with_deleted).each do |hasmany|
.includes(:paranoid_time_with_deleted).each do |hasmany|
assert_not_nil hasmany.paranoid_time_with_deleted
end
end
Expand Down