Commit d0703231 by Shaun Dern Committed by Ryan Bigg

Add optional sentinel_value

Use fetch for sentinel_value

Use DateTime for sentinel_value example

Use an epoch. rails convers '0' or 0 to nil for datetime fields

Add test for restore

Update readme

Move MYSQL note to bottom of readme

update destroyed? for sentinel value
parent 7022080f
...@@ -182,6 +182,27 @@ You can replace the older `acts_as_paranoid` methods as follows: ...@@ -182,6 +182,27 @@ You can replace the older `acts_as_paranoid` methods as follows:
The `recover` method in `acts_as_paranoid` runs `update` callbacks. Paranoia's The `recover` method in `acts_as_paranoid` runs `update` callbacks. Paranoia's
`restore` method does not do this. `restore` method does not do this.
## Support for Unique Keys with Null Values
Most databases ignore null columns when it comes to resolving unique index
constraints. This means unique constraints that involve nullable columns may be
problematic. Instead of using `NULL` to represent a not-deleted row, you can pick
a value that you want paranoia to mean not deleted. Note that you can/should
now apply a `NOT NULL` constraint to your `deleted_at` column.
Per model:
```ruby
# pick some value
acts_as_paranoid sentinel_value: DateTime.new(0)
```
or globally in a rails initializer, e.g. `config/initializer/paranoia.rb`
```ruby
Paranoia.default_sentinel_value = DateTime.new(0)
```
## License ## License
This gem is released under the MIT license. This gem is released under the MIT license.
require 'active_record' unless defined? ActiveRecord require 'active_record' unless defined? ActiveRecord
module Paranoia module Paranoia
@@default_sentinel_value = nil
# Change default_sentinel_value in a rails initilizer
def self.default_sentinel_value=(val)
@@default_sentinel_value = val
end
def self.default_sentinel_value
@@default_sentinel_value
end
def self.included(klazz) def self.included(klazz)
klazz.extend Query klazz.extend Query
klazz.extend Callbacks klazz.extend Callbacks
...@@ -18,7 +29,7 @@ module Paranoia ...@@ -18,7 +29,7 @@ module Paranoia
end end
def only_deleted def only_deleted
with_deleted.where.not(paranoia_column => nil) with_deleted.where.not(paranoia_column => paranoia_sentinel_value)
end end
alias :deleted :only_deleted alias :deleted :only_deleted
...@@ -76,7 +87,7 @@ module Paranoia ...@@ -76,7 +87,7 @@ module Paranoia
def restore!(opts = {}) def restore!(opts = {})
self.class.transaction do self.class.transaction do
run_callbacks(:restore) do run_callbacks(:restore) do
update_column paranoia_column, nil update_column paranoia_column, paranoia_sentinel_value
restore_associated_records if opts[:recursive] restore_associated_records if opts[:recursive]
end end
end end
...@@ -84,7 +95,7 @@ module Paranoia ...@@ -84,7 +95,7 @@ module Paranoia
alias :restore :restore! alias :restore :restore!
def destroyed? def destroyed?
!!send(paranoia_column) send(paranoia_column) != paranoia_sentinel_value
end end
alias :deleted? :destroyed? alias :deleted? :destroyed?
...@@ -155,10 +166,11 @@ class ActiveRecord::Base ...@@ -155,10 +166,11 @@ class ActiveRecord::Base
end end
include Paranoia include Paranoia
class_attribute :paranoia_column class_attribute :paranoia_column, :paranoia_sentinel_value
self.paranoia_column = options[:column] || :deleted_at self.paranoia_column = options[:column] || :deleted_at
default_scope { where(paranoia_column => nil) } self.paranoia_sentinel_value = options.fetch(:sentinel_value) { Paranoia.default_sentinel_value }
default_scope { where(paranoia_column => paranoia_sentinel_value) }
before_restore { before_restore {
self.class.notify_observers(:before_restore, self) if self.class.respond_to?(:notify_observers) self.class.notify_observers(:before_restore, self) if self.class.respond_to?(:notify_observers)
...@@ -194,6 +206,10 @@ class ActiveRecord::Base ...@@ -194,6 +206,10 @@ class ActiveRecord::Base
def paranoia_column def paranoia_column
self.class.paranoia_column self.class.paranoia_column
end end
def paranoia_sentinel_value
self.class.paranoia_sentinel_value
end
end end
require 'paranoia/rspec' if defined? RSpec require 'paranoia/rspec' if defined? RSpec
...@@ -24,6 +24,7 @@ def connect! ...@@ -24,6 +24,7 @@ def connect!
ActiveRecord::Base.connection.execute 'CREATE TABLE employees (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)' ActiveRecord::Base.connection.execute 'CREATE TABLE employees (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME)'
ActiveRecord::Base.connection.execute 'CREATE TABLE jobs (id INTEGER NOT NULL PRIMARY KEY, employer_id INTEGER NOT NULL, employee_id INTEGER NOT NULL, deleted_at DATETIME)' ActiveRecord::Base.connection.execute 'CREATE TABLE jobs (id INTEGER NOT NULL PRIMARY KEY, employer_id INTEGER NOT NULL, employee_id INTEGER NOT NULL, deleted_at DATETIME)'
ActiveRecord::Base.connection.execute 'CREATE TABLE custom_column_models (id INTEGER NOT NULL PRIMARY KEY, destroyed_at DATETIME)' ActiveRecord::Base.connection.execute 'CREATE TABLE custom_column_models (id INTEGER NOT NULL PRIMARY KEY, destroyed_at DATETIME)'
ActiveRecord::Base.connection.execute 'CREATE TABLE custom_sentinel_models (id INTEGER NOT NULL PRIMARY KEY, deleted_at DATETIME NOT NULL)'
ActiveRecord::Base.connection.execute 'CREATE TABLE non_paranoid_models (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER)' ActiveRecord::Base.connection.execute 'CREATE TABLE non_paranoid_models (id INTEGER NOT NULL PRIMARY KEY, parent_model_id INTEGER)'
end end
...@@ -158,6 +159,36 @@ class ParanoiaTest < test_framework ...@@ -158,6 +159,36 @@ class ParanoiaTest < test_framework
assert_equal 1, model.class.deleted.count assert_equal 1, model.class.deleted.count
end end
def test_default_sentinel_value
assert_equal nil, ParanoidModel.paranoia_sentinel_value
end
def test_sentinel_value_for_custom_sentinel_models
model = CustomSentinelModel.new
assert_equal 0, model.class.count
model.save!
assert_equal DateTime.new(0), model.deleted_at
assert_equal 1, model.class.count
model.destroy
assert DateTime.new(0) != model.deleted_at
assert model.destroyed?
assert_equal 0, model.class.count
assert_equal 1, model.class.unscoped.count
assert_equal 1, model.class.only_deleted.count
assert_equal 1, model.class.deleted.count
model.restore
assert_equal DateTime.new(0), model.deleted_at
assert !model.destroyed?
assert_equal 1, model.class.count
assert_equal 1, model.class.unscoped.count
assert_equal 0, model.class.only_deleted.count
assert_equal 0, model.class.deleted.count
end
def test_destroy_behavior_for_featureful_paranoid_models def test_destroy_behavior_for_featureful_paranoid_models
model = get_featureful_model model = get_featureful_model
assert_equal 0, model.class.count assert_equal 0, model.class.count
...@@ -587,6 +618,10 @@ class CustomColumnModel < ActiveRecord::Base ...@@ -587,6 +618,10 @@ class CustomColumnModel < ActiveRecord::Base
acts_as_paranoid column: :destroyed_at acts_as_paranoid column: :destroyed_at
end end
class CustomSentinelModel < ActiveRecord::Base
acts_as_paranoid sentinel_value: DateTime.new(0)
end
class NonParanoidModel < ActiveRecord::Base class NonParanoidModel < ActiveRecord::Base
end end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment