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:
The `recover` method in `acts_as_paranoid` runs `update` callbacks. Paranoia's
`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
This gem is released under the MIT license.
require 'active_record' unless defined? ActiveRecord
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)
klazz.extend Query
klazz.extend Callbacks
......@@ -18,7 +29,7 @@ module Paranoia
end
def only_deleted
with_deleted.where.not(paranoia_column => nil)
with_deleted.where.not(paranoia_column => paranoia_sentinel_value)
end
alias :deleted :only_deleted
......@@ -76,7 +87,7 @@ module Paranoia
def restore!(opts = {})
self.class.transaction do
run_callbacks(:restore) do
update_column paranoia_column, nil
update_column paranoia_column, paranoia_sentinel_value
restore_associated_records if opts[:recursive]
end
end
......@@ -84,7 +95,7 @@ module Paranoia
alias :restore :restore!
def destroyed?
!!send(paranoia_column)
send(paranoia_column) != paranoia_sentinel_value
end
alias :deleted? :destroyed?
......@@ -155,10 +166,11 @@ class ActiveRecord::Base
end
include Paranoia
class_attribute :paranoia_column
class_attribute :paranoia_column, :paranoia_sentinel_value
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 {
self.class.notify_observers(:before_restore, self) if self.class.respond_to?(:notify_observers)
......@@ -194,6 +206,10 @@ class ActiveRecord::Base
def paranoia_column
self.class.paranoia_column
end
def paranoia_sentinel_value
self.class.paranoia_sentinel_value
end
end
require 'paranoia/rspec' if defined? RSpec
......@@ -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 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_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)'
end
......@@ -158,6 +159,36 @@ class ParanoiaTest < test_framework
assert_equal 1, model.class.deleted.count
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
model = get_featureful_model
assert_equal 0, model.class.count
......@@ -587,6 +618,10 @@ class CustomColumnModel < ActiveRecord::Base
acts_as_paranoid column: :destroyed_at
end
class CustomSentinelModel < ActiveRecord::Base
acts_as_paranoid sentinel_value: DateTime.new(0)
end
class NonParanoidModel < ActiveRecord::Base
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