acts_as_paranoid 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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