acts_as_paranoid 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,36 @@
1
+ module ActsAsParanoid
2
+ module Relation
3
+ def self.included(base)
4
+ base.class_eval do
5
+ def paranoid?
6
+ klass.try(:paranoid?) ? true : false
7
+ end
8
+
9
+ def paranoid_deletion_attributes
10
+ { klass.paranoid_column => klass.delete_now_value }
11
+ end
12
+
13
+ alias_method :orig_delete_all, :delete_all
14
+ def delete_all!(conditions = nil)
15
+ if conditions
16
+ where(conditions).delete_all!
17
+ else
18
+ orig_delete_all
19
+ end
20
+ end
21
+
22
+ def delete_all(conditions = nil)
23
+ if paranoid?
24
+ update_all(paranoid_deletion_attributes, conditions)
25
+ else
26
+ delete_all!(conditions)
27
+ end
28
+ end
29
+
30
+ def destroy!(id_or_array)
31
+ where(primary_key => id_or_array).orig_delete_all
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,43 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
3
+ module ActsAsParanoid
4
+ module Validations
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ class UniquenessWithoutDeletedValidator < ActiveRecord::Validations::UniquenessValidator
10
+ def validate_each(record, attribute, value)
11
+ finder_class = find_finder_class_for(record)
12
+ table = finder_class.arel_table
13
+
14
+ coder = record.class.serialized_attributes[attribute.to_s]
15
+
16
+ if value && coder
17
+ value = coder.dump value
18
+ end
19
+
20
+ relation = build_relation(finder_class, table, attribute, value)
21
+ [Array(finder_class.primary_key), Array(record.send(:id))].transpose.each do |pk_key, pk_value|
22
+ relation = relation.and(table[pk_key.to_sym].not_eq(pk_value))
23
+ end if record.persisted?
24
+
25
+ Array.wrap(options[:scope]).each do |scope_item|
26
+ scope_value = record.send(scope_item)
27
+ relation = relation.and(table[scope_item].eq(scope_value))
28
+ end
29
+
30
+ # Re-add ActsAsParanoid default scope conditions manually.
31
+ if finder_class.unscoped.where(finder_class.paranoid_default_scope_sql).where(relation).exists?
32
+ record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
33
+ end
34
+ end
35
+ end
36
+
37
+ module ClassMethods
38
+ def validates_uniqueness_of_without_deleted(*attr_names)
39
+ validates_with UniquenessWithoutDeletedValidator, _merge_attributes(attr_names)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,61 @@
1
+ require 'active_record/base'
2
+ require 'active_record/relation'
3
+ require 'active_record/callbacks'
4
+ require 'acts_as_paranoid/core'
5
+ require 'acts_as_paranoid/associations'
6
+ require 'acts_as_paranoid/validations'
7
+ require 'acts_as_paranoid/relation'
8
+
9
+
10
+ module ActsAsParanoid
11
+
12
+ def paranoid?
13
+ self.included_modules.include?(ActsAsParanoid::Core)
14
+ end
15
+
16
+ def validates_as_paranoid
17
+ include ActsAsParanoid::Validations
18
+ end
19
+
20
+ def acts_as_paranoid(options = {})
21
+ raise ArgumentError, "Hash expected, got #{options.class.name}" if not options.is_a?(Hash) and not options.empty?
22
+
23
+ class_attribute :paranoid_configuration, :paranoid_column_reference
24
+
25
+ self.paranoid_configuration = { :column => "deleted_at", :column_type => "time", :recover_dependent_associations => true, :dependent_recovery_window => 2.minutes }
26
+ self.paranoid_configuration.merge!({ :deleted_value => "deleted" }) if options[:column_type] == "string"
27
+ self.paranoid_configuration.merge!(options) # user options
28
+
29
+ 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]
30
+
31
+ self.paranoid_column_reference = "#{self.table_name}.#{paranoid_configuration[:column]}"
32
+
33
+ return if paranoid?
34
+
35
+ include ActsAsParanoid::Core
36
+
37
+ # Magic!
38
+ default_scope { where(paranoid_default_scope_sql) }
39
+
40
+ if paranoid_configuration[:column_type] == 'time'
41
+ scope :deleted_inside_time_window, lambda {|time, window|
42
+ deleted_after_time((time - window)).deleted_before_time((time + window))
43
+ }
44
+
45
+ scope :deleted_after_time, lambda { |time| where("#{paranoid_column} > ?", time) }
46
+ scope :deleted_before_time, lambda { |time| where("#{paranoid_column} < ?", time) }
47
+ end
48
+ end
49
+ end
50
+
51
+ # Extend ActiveRecord's functionality
52
+ ActiveRecord::Base.send :extend, ActsAsParanoid
53
+
54
+ # Extend ActiveRecord::Base with paranoid associations
55
+ ActiveRecord::Base.send :include, ActsAsParanoid::Associations
56
+
57
+ # Override ActiveRecord::Relation's behavior
58
+ ActiveRecord::Relation.send :include, ActsAsParanoid::Relation
59
+
60
+ # Push the recover callback onto the activerecord callback list
61
+ ActiveRecord::Callbacks::CALLBACKS.push(:before_recover, :after_recover)
metadata CHANGED
@@ -1,51 +1,70 @@
1
- --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.8.10
3
- specification_version: 1
1
+ --- !ruby/object:Gem::Specification
4
2
  name: acts_as_paranoid
5
- version: !ruby/object:Gem::Version
6
- version: 0.3.1
7
- date: 2005-12-20
8
- summary: acts_as_paranoid keeps models from actually being deleted by setting a deleted_at field.
9
- require_paths:
10
- - lib
11
- email: technoweenie@gmail.com
12
- homepage: http://techno-weenie.net
13
- rubyforge_project:
14
- description:
15
- autorequire: acts_as_paranoid
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: true
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- -
22
- - ">"
23
- - !ruby/object:Gem::Version
24
- version: 0.0.0
25
- version:
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ prerelease:
26
6
  platform: ruby
27
- authors:
28
- - Rick Olson
29
- files:
30
- - lib/acts_as_paranoid.rb
31
- - test/database.yml
32
- - test/debug.log
33
- - test/fixtures
34
- - test/paranoid_test.rb
35
- - test/schema.rb
36
- - test/test_helper.rb
37
- - test/fixtures/categories.yml
38
- - test/fixtures/categories_widgets.yml
39
- - test/fixtures/widgets.yml
40
- - README
41
- - MIT-LICENSE
42
- - CHANGELOG
43
- - RUNNING_UNIT_TESTS
44
- test_files:
45
- - test/paranoid_test.rb
46
- rdoc_options: []
47
- extra_rdoc_files: []
7
+ authors:
8
+ - Goncalo Silva
9
+ - Charles G.
10
+ - Rick Olson
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2012-12-09 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: activerecord
18
+ requirement: &70256076943380 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: '3.2'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: *70256076943380
27
+ description: Active Record (~>3.2) plugin which allows you to hide and restore records
28
+ without actually deleting them. Check its GitHub page for more in-depth information.
29
+ email:
30
+ - goncalossilva@gmail.com
48
31
  executables: []
49
32
  extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - lib/acts_as_paranoid/associations.rb
36
+ - lib/acts_as_paranoid/core.rb
37
+ - lib/acts_as_paranoid/relation.rb
38
+ - lib/acts_as_paranoid/validations.rb
39
+ - lib/rails3_acts_as_paranoid.rb
40
+ - LICENSE
41
+ - README.markdown
42
+ homepage: https://github.com/goncalossilva/rails3_acts_as_paranoid
43
+ licenses: []
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ segments:
55
+ - 0
56
+ hash: -309367461026455685
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: 1.3.6
50
63
  requirements: []
51
- dependencies: []
64
+ rubyforge_project: acts_as_paranoid
65
+ rubygems_version: 1.8.17
66
+ signing_key:
67
+ specification_version: 3
68
+ summary: Active Record (~>3.2) plugin which allows you to hide and restore records
69
+ without actually deleting them.
70
+ test_files: []
data/CHANGELOG DELETED
@@ -1,47 +0,0 @@
1
- *0.3.1* (20 Dec 2005)
2
-
3
- * took out deleted association code for 'chainsaw butchery of base classes' [sorry Erik Terpstra]
4
- * verified tests pass on Rails 1.0
5
-
6
- *0.3* (27 Nov 2005)
7
-
8
- * Deleted models will find deleted associations by default now [Erik Terpstra]
9
- * Added :group as valid option for find [Michael Dabney]
10
- * Changed the module namespace to Caboose::Acts::Paranoid
11
-
12
- *0.2.0* (6 Nov 2005)
13
-
14
- * Upgrade to Rails 1.0 RC4. ActiveRecord::Base#constrain has been replaced with scope_with.
15
-
16
- *0.1.7* (22 Oct 2005)
17
-
18
- * Added :with_deleted as a valid option of ActiveRecord::Base#find
19
-
20
- *0.1.6* (25 Sep 2005)
21
-
22
- * Fixed bug where nested constrains would get clobbered after multiple queries
23
-
24
- *0.1.5* (22 Sep 2005)
25
-
26
- * Fixed bug where acts_as_paranoid would clobber other constrains
27
- * Simplified acts_as_paranoid mixin including.
28
-
29
- *0.1.4* (18 Sep 2005)
30
-
31
- * First RubyForge release
32
-
33
- *0.1.3* (18 Sep 2005)
34
-
35
- * ignore multiple calls to acts_as_paranoid on the same model
36
-
37
- *0.1.2* (18 Sep 2005)
38
-
39
- * fixed a bug that kept you from selecting the first deleted record
40
-
41
- *0.1.1* (18 Sep 2005)
42
-
43
- * Fixed bug that kept you from selecting deleted records by ID
44
-
45
- *0.1* (17 Sep 2005)
46
-
47
- * Initial gem
data/README DELETED
@@ -1,26 +0,0 @@
1
- = acts_as_paranoid
2
-
3
- Overrides some basic methods for the current model so that calling #destroy sets a 'deleted_at' field to the
4
- current timestamp. ActiveRecord is required.
5
-
6
- == Resources
7
-
8
- Install
9
-
10
- * gem install acts_as_paranoid
11
-
12
- Rubyforge project
13
-
14
- * http://rubyforge.org/projects/ar-paranoid
15
-
16
- RDocs
17
-
18
- * http://ar-paranoid.rubyforge.org
19
-
20
- Subversion
21
-
22
- * http://techno-weenie.net/svn/projects/acts_as_paranoid
23
-
24
- Collaboa
25
-
26
- * http://collaboa.techno-weenie.net/repository/browse/acts_as_paranoid
@@ -1,41 +0,0 @@
1
- == Creating the test database
2
-
3
- The default name for the test databases is "activerecord_paranoid". If you
4
- want to use another database name then be sure to update the connection
5
- adapter setups you want to test with in test/connections/<your database>/connection.rb.
6
- When you have the database online, you can import the fixture tables with
7
- the test/fixtures/db_definitions/*.sql files.
8
-
9
- Make sure that you create database objects with the same user that you specified in i
10
- connection.rb otherwise (on Postgres, at least) tests for default values will fail.
11
-
12
- == Running with Rake
13
-
14
- The easiest way to run the unit tests is through Rake. The default task runs
15
- the entire test suite for all the adapters. You can also run the suite on just
16
- one adapter by using the tasks test_mysql_ruby, test_ruby_mysql, test_sqlite,
17
- or test_postresql. For more information, checkout the full array of rake tasks with "rake -T"
18
-
19
- Rake can be found at http://rake.rubyforge.org
20
-
21
- == Running by hand
22
-
23
- Unit tests are located in test directory. If you only want to run a single test suite,
24
- or don't want to bother with Rake, you can do so with something like:
25
-
26
- cd test; ruby -I "connections/native_mysql" base_test.rb
27
-
28
- That'll run the base suite using the MySQL-Ruby adapter. Change the adapter
29
- and test suite name as needed.
30
-
31
- == Faster tests
32
-
33
- If you are using a database that supports transactions, you can set the
34
- "AR_TX_FIXTURES" environment variable to "yes" to use transactional fixtures.
35
- This gives a very large speed boost. With rake:
36
-
37
- rake AR_TX_FIXTURES=yes
38
-
39
- Or, by hand:
40
-
41
- AR_TX_FIXTURES=yes ruby -I connections/native_sqlite3 base_test.rb
@@ -1,166 +0,0 @@
1
- module Caboose #:nodoc:
2
- module Acts #:nodoc:
3
- # Overrides some basic methods for the current model so that calling #destroy sets a 'deleted_at' field to the current timestamp.
4
- # This assumes the table has a deleted_at date/time field. Most normal model operations will work, but there will be some oddities.
5
- #
6
- # class Widget < ActiveRecord::Base
7
- # acts_as_paranoid
8
- # end
9
- #
10
- # Widget.find(:all)
11
- # # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL
12
- #
13
- # Widget.find(:first, :conditions => ['title = ?', 'test'], :order => 'title')
14
- # # SELECT * FROM widgets WHERE widgets.deleted_at IS NULL AND title = 'test' ORDER BY title LIMIT 1
15
- #
16
- # Widget.find_with_deleted(:all)
17
- # # SELECT * FROM widgets
18
- #
19
- # Widget.find(:all, :with_deleted => true)
20
- # # SELECT * FROM widgets
21
- #
22
- # Widget.count
23
- # # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NULL
24
- #
25
- # Widget.count ['title = ?', 'test']
26
- # # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NULL AND title = 'test'
27
- #
28
- # Widget.count_with_deleted
29
- # # SELECT COUNT(*) FROM widgets
30
- #
31
- # @widget.destroy
32
- # # UPDATE widgets SET deleted_at = '2005-09-17 17:46:36' WHERE id = 1
33
- #
34
- # @widget.destroy!
35
- # # DELETE FROM widgets WHERE id = 1
36
- #
37
- module Paranoid
38
- module ActiveRecord
39
- def self.included(base) # :nodoc:
40
- base.extend ClassMethods
41
- class << base
42
- alias_method :validate_find_options_without_deleted, :validate_find_options
43
- alias_method :validate_find_options, :validate_find_options_with_deleted
44
- end
45
- end
46
-
47
- module ClassMethods
48
- def acts_as_paranoid
49
- unless paranoid? # don't let AR call this twice
50
- alias_method :destroy_without_callbacks!, :destroy_without_callbacks
51
- class << self
52
- alias_method :original_find, :find
53
- alias_method :count_with_deleted, :count
54
- alias_method :clobbering_with_scope, :with_scope
55
- end
56
- end
57
- include InstanceMethods
58
- end
59
-
60
- def paranoid?
61
- self.included_modules.include?(InstanceMethods)
62
- end
63
-
64
- protected
65
- def validate_find_options_with_deleted(options)
66
- options.assert_valid_keys [:conditions, :group, :include, :joins, :limit, :offset, :order, :select, :readonly, :with_deleted]
67
- end
68
- end
69
-
70
- module InstanceMethods #:nodoc:
71
- def self.included(base) # :nodoc:
72
- base.extend ClassMethods
73
- end
74
-
75
- module ClassMethods
76
- def find(*args)
77
- options = extract_options_from_args!(args)
78
- call_original_find = lambda { original_find(*(args << options)) }
79
-
80
- if !options[:with_deleted]
81
- with_deleted_scope { return call_original_find.call }
82
- end
83
-
84
- call_original_find.call
85
- end
86
-
87
- def find_with_deleted(*args)
88
- original_find(*(args << extract_options_from_args!(args).merge(:with_deleted => true)))
89
- end
90
-
91
- def count(conditions = nil, joins = nil)
92
- with_deleted_scope { count_with_deleted(conditions, joins) }
93
- end
94
-
95
- def with_scope(method_scoping = {}, is_new_scope = true)
96
- # Dup first and second level of hash (method and params).
97
- method_scoping = method_scoping.inject({}) do |hash, (method, params)|
98
- hash[method] = params.dup
99
- hash
100
- end
101
-
102
- method_scoping.assert_valid_keys [:find, :create]
103
- if f = method_scoping[:find]
104
- f.assert_valid_keys [:conditions, :joins, :offset, :limit, :readonly]
105
- f[:readonly] = true if !f[:joins].blank? && !f.has_key?(:readonly)
106
- end
107
-
108
- raise ArgumentError, "Nested scopes are not yet supported: #{scoped_methods.inspect}" unless scoped_methods.nil?
109
-
110
- self.scoped_methods = method_scoping
111
- yield
112
- ensure
113
- self.scoped_methods = nil if is_new_scope
114
- end
115
-
116
- protected
117
- def with_deleted_scope(&block)
118
- deleted_cond = "#{table_name}.deleted_at IS NULL"
119
- if scoped_methods.nil?
120
- is_new_scope = true
121
- current_scope = {}
122
- else
123
- is_new_scope = false
124
- current_scope = scoped_methods.clone
125
- self.scoped_methods = nil
126
- end
127
-
128
- current_scope ||= {}
129
- current_scope[:find] ||= {}
130
- if not current_scope[:find][:conditions] =~ /#{deleted_cond}/
131
- current_scope[:find][:conditions] = current_scope[:find][:conditions].nil? ?
132
- deleted_cond :
133
- "(#{current_scope[:find][:conditions]}) AND #{deleted_cond}"
134
- end
135
-
136
- with_scope(current_scope, is_new_scope, &block)
137
- end
138
- end
139
-
140
- def destroy_without_callbacks
141
- unless new_record?
142
- sql = self.class.send(:sanitize_sql,
143
- ["UPDATE #{self.class.table_name} SET deleted_at = ? WHERE id = ?",
144
- self.class.default_timezone == :utc ? Time.now.utc : Time.now, id])
145
- self.connection.update(sql)
146
- end
147
- freeze
148
- end
149
-
150
- def destroy_with_callbacks!
151
- return false if callback(:before_destroy) == false
152
- result = destroy_without_callbacks!
153
- callback(:after_destroy)
154
- result
155
- end
156
-
157
- def destroy!
158
- transaction { destroy_with_callbacks! }
159
- end
160
- end
161
- end
162
- end
163
- end
164
- end
165
-
166
- ActiveRecord::Base.send :include, Caboose::Acts::Paranoid::ActiveRecord