Permalink
Browse files

support boolean with default set to false, rather than NULL; and pass…

…ed all tests
  • Loading branch information...
1 parent 9088943 commit 11e1a01554bc888475cf70ff9b3b998d8673fdac @mvj3 committed Apr 9, 2013
View
@@ -18,4 +18,5 @@ group :test do
gem "minitest"
gem "ZenTest"
gem "autotest-growl"
+ # gem 'pry-debugger'
end
View
@@ -186,29 +186,32 @@ 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`
```ruby
-class ParanoiacParent < ActiveRecord::Base
+class Parent < ActiveRecord::Base
has_many :children, :class_name => "ParanoiacChild"
end
class ParanoiacChild < ActiveRecord::Base
- belongs_to :parent, :class_name => "ParanoiacParent"
- belongs_to :parent_with_deleted, :class_name => "ParanoiacParent", :with_deleted => true
+ belongs_to :parent
+ belongs_to :parent_including_deleted, :class_name => "Parent", :with_deleted => true
+ # You cannot name association *_with_deleted
end
-parent = ParanoiacParent.first
+parent = Parent.first
child = parent.children.create
parent.destroy
child.parent #=> nil
-child.parent_with_deleted #=> ParanoiacParent (it works!)
+child.parent_including_deleted #=> Parent (it works!)
```
## Caveats
Watch out for these caveats:
+
- You cannot use scopes named `with_deleted` and `only_deleted`
- You cannot use scopes named `deleted_inside_time_window`, `deleted_before_time`, `deleted_after_time` **if** your paranoid column's type is `time`
+- You cannot name association `*_with_deleted`
- `unscoped` will return all records, deleted or not
# Support
View
@@ -5,15 +5,15 @@ rescue LoadError
$stderr.puts "You need to have Bundler installed to be able build this gem."
end
require "rake/testtask"
-require "rake/rdoctask"
+require "rdoc/task"
gemspec = eval(File.read(Dir["*.gemspec"].first))
desc 'Default: run unit tests.'
task :default => :test
-desc 'Test the rails3_acts_as_paranoid plugin.'
+desc 'Test the acts_as_paranoid plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
@@ -25,7 +25,7 @@ Rake::TestTask.new(:test) do |t|
t.verbose = true
end
-desc 'Generate documentation for the rails3_acts_as_paranoid plugin.'
+desc 'Generate documentation for the acts_as_paranoid plugin.'
Rake::RDocTask.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'ActsAsParanoid'
View
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = "acts_as_paranoid"
- s.version = "0.0.0"
+ s.version = "0.4.1"
s.platform = Gem::Platform::RUBY
s.authors = ["Goncalo Silva", "Charles G.", "Rick Olson"]
s.email = ["goncalossilva@gmail.com"]
View
@@ -1 +1 @@
-require 'rails3_acts_as_paranoid'
+require 'acts_as_paranoid'
@@ -8,35 +8,38 @@
module ActsAsParanoid
-
+
def paranoid?
self.included_modules.include?(ActsAsParanoid::Core)
end
-
+
def validates_as_paranoid
include ActsAsParanoid::Validations
end
-
+
def acts_as_paranoid(options = {})
raise ArgumentError, "Hash expected, got #{options.class.name}" if not options.is_a?(Hash) and not options.empty?
-
+
class_attribute :paranoid_configuration, :paranoid_column_reference
-
+
self.paranoid_configuration = { :column => "deleted_at", :column_type => "time", :recover_dependent_associations => true, :dependent_recovery_window => 2.minutes }
self.paranoid_configuration.merge!({ :deleted_value => "deleted" }) if options[:column_type] == "string"
self.paranoid_configuration.merge!(options) # user options
raise ArgumentError, "'time', 'boolean' or 'string' expected for :column_type option, got #{paranoid_configuration[:column_type]}" unless ['time', 'boolean', 'string'].include? paranoid_configuration[:column_type]
self.paranoid_column_reference = "#{self.table_name}.#{paranoid_configuration[:column]}"
-
+
return if paranoid?
-
+
include ActsAsParanoid::Core
-
+
# Magic!
default_scope { where(paranoid_default_scope_sql) }
+ # The paranoid column should not be mass-assignable
+ attr_protected paranoid_configuration[:column]
+
if paranoid_configuration[:column_type] == 'time'
scope :deleted_inside_time_window, lambda {|time, window|
deleted_after_time((time - window)).deleted_before_time((time + window))
@@ -22,11 +22,8 @@ def with_deleted
end
def only_deleted
- if string_type_with_deleted_value?
- without_paranoid_default_scope.where("#{paranoid_column_reference} IS ?", paranoid_configuration[:deleted_value])
- else
- without_paranoid_default_scope.where("#{paranoid_column_reference} IS NOT ?", nil)
- end
+ _is_not = string_type_with_deleted_value? ? "" : " NOT "
+ without_paranoid_default_scope.where("#{paranoid_column_reference} IS #{_is_not} ?", paranoid_configuration[:deleted_value])
end
def delete_all!(conditions = nil)
@@ -38,12 +35,19 @@ def delete_all(conditions = nil)
end
def paranoid_default_scope_sql
+ # lazy load self.columns
+ @@set_paranoid_configuration_deleted_value ||= {}
+ @@set_paranoid_configuration_deleted_value[self.table_name.to_sym] ||= begin
+ if (paranoid_configuration[:column_type] == "boolean") && (self.columns.detect {|column| column.name == self.paranoid_configuration[:column] }.default === false)
+ self.paranoid_configuration.merge!({:deleted_value => false})
+ end
+ true
+ end
+
if string_type_with_deleted_value?
- self.scoped.table[paranoid_column].eq(nil).
- or(self.scoped.table[paranoid_column].not_eq(paranoid_configuration[:deleted_value])).
- to_sql
+ self.scoped.table[paranoid_column].eq(nil).or(self.scoped.table[paranoid_column].not_eq(paranoid_configuration[:deleted_value])).to_sql
else
- self.scoped.table[paranoid_column].eq(nil).to_sql
+ self.scoped.table[paranoid_column].eq(paranoid_configuration[:deleted_value]).to_sql
end
end
@@ -94,7 +98,7 @@ def destroy!
run_callbacks :destroy do
destroy_dependent_associations!
# Handle composite keys, otherwise we would just use `self.class.primary_key.to_sym => self.id`.
- self.class.delete_all!(Hash[[Array(self.class.primary_key), Array(self.id)].transpose])
+ self.class.delete_all!(Hash[[Array(self.class.primary_key), Array(self.id)].transpose]) if persisted?
self.paranoid_value = self.class.delete_now_value
freeze
end
@@ -106,7 +110,7 @@ def destroy
with_transaction_returning_status do
run_callbacks :destroy do
# Handle composite keys, otherwise we would just use `self.class.primary_key.to_sym => self.id`.
- self.class.delete_all(Hash[[Array(self.class.primary_key), Array(self.id)].transpose])
+ self.class.delete_all(Hash[[Array(self.class.primary_key), Array(self.id)].transpose]) if persisted?
self.paranoid_value = self.class.delete_now_value
self
end
@@ -180,5 +184,6 @@ def deleted?
def paranoid_value=(value)
self.send("#{self.class.paranoid_column}=", value)
end
+
end
end
View
@@ -6,11 +6,16 @@ def test_paranoid?
assert_raise(NoMethodError) { NotParanoid.delete_all! }
assert_raise(NoMethodError) { NotParanoid.first.destroy! }
assert_raise(NoMethodError) { NotParanoid.with_deleted }
- assert_raise(NoMethodError) { NotParanoid.only_deleted }
+ assert_raise(NoMethodError) { NotParanoid.only_deleted }
assert ParanoidTime.paranoid?
end
+ def test_attr_protected_column
+ pt = ParanoidTime.new(:deleted_at => Time.now)
+ assert_nil pt.deleted_at
+ end
+
def test_scope_inclusion_with_time_column_type
assert ParanoidTime.respond_to?(:deleted_inside_time_window)
assert ParanoidTime.respond_to?(:deleted_before_time)
@@ -63,6 +68,20 @@ def test_real_removal
assert_empty ParanoidTime.with_deleted.all
end
+ def test_non_persisted_destroy
+ pt = ParanoidTime.new
+ assert_nil pt.paranoid_value
+ pt.destroy
+ assert_not_nil pt.paranoid_value
+ end
+
+ def test_non_persisted_destroy!
+ pt = ParanoidTime.new
+ assert_nil pt.paranoid_value
+ pt.destroy!
+ assert_not_nil pt.paranoid_value
+ end
+
def test_recovery
assert_equal 3, ParanoidBoolean.count
ParanoidBoolean.first.destroy
@@ -79,7 +98,7 @@ def test_recovery
def setup_recursive_tests
@paranoid_time_object = ParanoidTime.first
-
+
# Create one extra ParanoidHasManyDependant record so that we can validate
# the correct dependants are recovered.
ParanoidTime.where('id <> ?', @paranoid_time_object.id).first.paranoid_has_many_dependants.create(:name => "should not be recovered").destroy
@@ -208,25 +227,25 @@ def test_deleted?
ParanoidString.first.destroy
assert ParanoidString.with_deleted.first.deleted?
end
-
+
def test_paranoid_destroy_callbacks
@paranoid_with_callback = ParanoidWithCallback.first
ParanoidWithCallback.transaction do
@paranoid_with_callback.destroy
end
-
+
assert @paranoid_with_callback.called_before_destroy
assert @paranoid_with_callback.called_after_destroy
assert @paranoid_with_callback.called_after_commit_on_destroy
end
-
+
def test_hard_destroy_callbacks
@paranoid_with_callback = ParanoidWithCallback.first
-
+
ParanoidWithCallback.transaction do
@paranoid_with_callback.destroy!
end
-
+
assert @paranoid_with_callback.called_before_destroy
assert @paranoid_with_callback.called_after_destroy
assert @paranoid_with_callback.called_after_commit_on_destroy
@@ -252,45 +271,45 @@ def test_delete_by_multiple_id_is_paranoid
model_a = ParanoidBelongsDependant.create
model_b = ParanoidBelongsDependant.create
ParanoidBelongsDependant.delete([model_a.id, model_b.id])
-
+
assert_paranoid_deletion(model_a)
assert_paranoid_deletion(model_b)
end
-
+
def test_destroy_by_multiple_id_is_paranoid
model_a = ParanoidBelongsDependant.create
model_b = ParanoidBelongsDependant.create
ParanoidBelongsDependant.destroy([model_a.id, model_b.id])
-
+
assert_paranoid_deletion(model_a)
assert_paranoid_deletion(model_b)
end
-
+
def test_delete_by_single_id_is_paranoid
model = ParanoidBelongsDependant.create
ParanoidBelongsDependant.delete(model.id)
-
+
assert_paranoid_deletion(model)
end
-
+
def test_destroy_by_single_id_is_paranoid
model = ParanoidBelongsDependant.create
ParanoidBelongsDependant.destroy(model.id)
-
+
assert_paranoid_deletion(model)
end
-
+
def test_instance_delete_is_paranoid
model = ParanoidBelongsDependant.create
model.delete
-
+
assert_paranoid_deletion(model)
end
-
+
def test_instance_destroy_is_paranoid
model = ParanoidBelongsDependant.create
model.destroy
-
+
assert_paranoid_deletion(model)
end
Oops, something went wrong.

0 comments on commit 11e1a01

Please sign in to comment.