pager-acts_as_paranoid 1.0.20080505
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.
- data/CHANGELOG +74 -0
- data/MIT-LICENSE +20 -0
- data/README +26 -0
- data/RUNNING_UNIT_TESTS +41 -0
- data/Rakefile +180 -0
- data/lib/caboose/acts/belongs_to_with_deleted_association.rb +14 -0
- data/lib/caboose/acts/has_many_through_without_deleted_association.rb +27 -0
- data/lib/caboose/acts/paranoid.rb +157 -0
- data/lib/pager-acts_as_paranoid.rb +30 -0
- data/rails/init.rb +1 -0
- data/test/database.yml +18 -0
- data/test/fixtures/categories.yml +19 -0
- data/test/fixtures/categories_widgets.yml +12 -0
- data/test/fixtures/taggings.yml +9 -0
- data/test/fixtures/tags.yml +6 -0
- data/test/fixtures/widgets.yml +8 -0
- data/test/paranoid_test.rb +246 -0
- data/test/schema.rb +30 -0
- data/test/test_helper.rb +33 -0
- metadata +83 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
* (4 Oct 2007)
|
2
|
+
|
3
|
+
Update for Edge rails: remove support for legacy #count args
|
4
|
+
|
5
|
+
* (2 Feb 2007)
|
6
|
+
|
7
|
+
Add support for custom primary keys [Jeff Dean]
|
8
|
+
|
9
|
+
* (2 July 2006)
|
10
|
+
|
11
|
+
Add paranoid delete_all implementation [Marshall Roch]
|
12
|
+
|
13
|
+
* (23 May 2006)
|
14
|
+
|
15
|
+
Allow setting of future dates for content expiration.
|
16
|
+
|
17
|
+
* (15 May 2006)
|
18
|
+
|
19
|
+
Added support for dynamic finders
|
20
|
+
|
21
|
+
* (28 Mar 2006)
|
22
|
+
|
23
|
+
Updated for Rails 1.1. I love removing code.
|
24
|
+
|
25
|
+
Refactored #find method
|
26
|
+
Nested Scopes
|
27
|
+
|
28
|
+
*0.3.1* (20 Dec 2005)
|
29
|
+
|
30
|
+
* took out deleted association code for 'chainsaw butchery of base classes' [sorry Erik Terpstra]
|
31
|
+
* verified tests pass on Rails 1.0
|
32
|
+
|
33
|
+
*0.3* (27 Nov 2005)
|
34
|
+
|
35
|
+
* Deleted models will find deleted associations by default now [Erik Terpstra]
|
36
|
+
* Added :group as valid option for find [Michael Dabney]
|
37
|
+
* Changed the module namespace to Caboose::Acts::Paranoid
|
38
|
+
|
39
|
+
*0.2.0* (6 Nov 2005)
|
40
|
+
|
41
|
+
* Upgrade to Rails 1.0 RC4. ActiveRecord::Base#constrain has been replaced with scope_with.
|
42
|
+
|
43
|
+
*0.1.7* (22 Oct 2005)
|
44
|
+
|
45
|
+
* Added :with_deleted as a valid option of ActiveRecord::Base#find
|
46
|
+
|
47
|
+
*0.1.6* (25 Sep 2005)
|
48
|
+
|
49
|
+
* Fixed bug where nested constrains would get clobbered after multiple queries
|
50
|
+
|
51
|
+
*0.1.5* (22 Sep 2005)
|
52
|
+
|
53
|
+
* Fixed bug where acts_as_paranoid would clobber other constrains
|
54
|
+
* Simplified acts_as_paranoid mixin including.
|
55
|
+
|
56
|
+
*0.1.4* (18 Sep 2005)
|
57
|
+
|
58
|
+
* First RubyForge release
|
59
|
+
|
60
|
+
*0.1.3* (18 Sep 2005)
|
61
|
+
|
62
|
+
* ignore multiple calls to acts_as_paranoid on the same model
|
63
|
+
|
64
|
+
*0.1.2* (18 Sep 2005)
|
65
|
+
|
66
|
+
* fixed a bug that kept you from selecting the first deleted record
|
67
|
+
|
68
|
+
*0.1.1* (18 Sep 2005)
|
69
|
+
|
70
|
+
* Fixed bug that kept you from selecting deleted records by ID
|
71
|
+
|
72
|
+
*0.1* (17 Sep 2005)
|
73
|
+
|
74
|
+
* Initial gem
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2005 Rick Olson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,26 @@
|
|
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
|
data/RUNNING_UNIT_TESTS
ADDED
@@ -0,0 +1,41 @@
|
|
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
|
data/Rakefile
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
Gem::manage_gems
|
4
|
+
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
require 'rake/packagetask'
|
7
|
+
require 'rake/gempackagetask'
|
8
|
+
require 'rake/testtask'
|
9
|
+
require 'rake/contrib/rubyforgepublisher'
|
10
|
+
|
11
|
+
PKG_NAME = 'acts_as_paranoid'
|
12
|
+
PKG_VERSION = '0.3.1'
|
13
|
+
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
14
|
+
PROD_HOST = "technoweenie@bidwell.textdrive.com"
|
15
|
+
RUBY_FORGE_PROJECT = 'ar-paranoid'
|
16
|
+
RUBY_FORGE_USER = 'technoweenie'
|
17
|
+
|
18
|
+
desc 'Default: run unit tests.'
|
19
|
+
task :default => :test
|
20
|
+
|
21
|
+
desc 'Test the calculations plugin.'
|
22
|
+
Rake::TestTask.new(:test) do |t|
|
23
|
+
t.libs << 'lib'
|
24
|
+
t.pattern = 'test/**/*_test.rb'
|
25
|
+
t.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'Generate documentation for the acts_as_paranoid plugin.'
|
29
|
+
Rake::RDocTask.new do |rdoc|
|
30
|
+
rdoc.rdoc_dir = 'html'
|
31
|
+
rdoc.title = "#{PKG_NAME} -- protect your ActiveRecord objects from accidental deletion"
|
32
|
+
rdoc.options << '--line-numbers --inline-source --accessor cattr_accessor=object'
|
33
|
+
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
34
|
+
rdoc.rdoc_files.include('README', 'CHANGELOG', 'RUNNING_UNIT_TESTS')
|
35
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
36
|
+
end
|
37
|
+
|
38
|
+
spec = Gem::Specification.new do |s|
|
39
|
+
s.name = PKG_NAME
|
40
|
+
s.version = PKG_VERSION
|
41
|
+
s.platform = Gem::Platform::RUBY
|
42
|
+
s.summary = "acts_as_paranoid keeps models from actually being deleted by setting a deleted_at field."
|
43
|
+
s.files = FileList["{lib,test}/**/*"].to_a + %w(README MIT-LICENSE CHANGELOG RUNNING_UNIT_TESTS)
|
44
|
+
s.files.delete "acts_as_paranoid_plugin.sqlite.db"
|
45
|
+
s.files.delete "acts_as_paranoid_plugin.sqlite3.db"
|
46
|
+
s.require_path = 'lib'
|
47
|
+
s.autorequire = 'acts_as_paranoid'
|
48
|
+
s.has_rdoc = true
|
49
|
+
s.test_files = Dir['test/**/*_test.rb']
|
50
|
+
s.author = "Rick Olson"
|
51
|
+
s.email = "technoweenie@gmail.com"
|
52
|
+
s.homepage = "http://techno-weenie.net"
|
53
|
+
end
|
54
|
+
|
55
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
56
|
+
pkg.need_tar = true
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "Publish the API documentation"
|
60
|
+
task :pdoc => [:rdoc] do
|
61
|
+
Rake::RubyForgePublisher.new(RUBY_FORGE_PROJECT, RUBY_FORGE_USER).upload
|
62
|
+
end
|
63
|
+
|
64
|
+
desc 'Publish the gem and API docs'
|
65
|
+
task :publish => [:pdoc, :rubyforge_upload]
|
66
|
+
|
67
|
+
desc "Publish the release files to RubyForge."
|
68
|
+
task :rubyforge_upload => :package do
|
69
|
+
files = %w(gem tgz).map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" }
|
70
|
+
|
71
|
+
if RUBY_FORGE_PROJECT then
|
72
|
+
require 'net/http'
|
73
|
+
require 'open-uri'
|
74
|
+
|
75
|
+
project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
|
76
|
+
project_data = open(project_uri) { |data| data.read }
|
77
|
+
group_id = project_data[/[?&]group_id=(\d+)/, 1]
|
78
|
+
raise "Couldn't get group id" unless group_id
|
79
|
+
|
80
|
+
# This echos password to shell which is a bit sucky
|
81
|
+
if ENV["RUBY_FORGE_PASSWORD"]
|
82
|
+
password = ENV["RUBY_FORGE_PASSWORD"]
|
83
|
+
else
|
84
|
+
print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
|
85
|
+
password = STDIN.gets.chomp
|
86
|
+
end
|
87
|
+
|
88
|
+
login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
|
89
|
+
data = [
|
90
|
+
"login=1",
|
91
|
+
"form_loginname=#{RUBY_FORGE_USER}",
|
92
|
+
"form_pw=#{password}"
|
93
|
+
].join("&")
|
94
|
+
http.post("/account/login.php", data)
|
95
|
+
end
|
96
|
+
|
97
|
+
cookie = login_response["set-cookie"]
|
98
|
+
raise "Login failed" unless cookie
|
99
|
+
headers = { "Cookie" => cookie }
|
100
|
+
|
101
|
+
release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
|
102
|
+
release_data = open(release_uri, headers) { |data| data.read }
|
103
|
+
package_id = release_data[/[?&]package_id=(\d+)/, 1]
|
104
|
+
raise "Couldn't get package id" unless package_id
|
105
|
+
|
106
|
+
first_file = true
|
107
|
+
release_id = ""
|
108
|
+
|
109
|
+
files.each do |filename|
|
110
|
+
basename = File.basename(filename)
|
111
|
+
file_ext = File.extname(filename)
|
112
|
+
file_data = File.open(filename, "rb") { |file| file.read }
|
113
|
+
|
114
|
+
puts "Releasing #{basename}..."
|
115
|
+
|
116
|
+
release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
|
117
|
+
release_date = Time.now.strftime("%Y-%m-%d %H:%M")
|
118
|
+
type_map = {
|
119
|
+
".zip" => "3000",
|
120
|
+
".tgz" => "3110",
|
121
|
+
".gz" => "3110",
|
122
|
+
".gem" => "1400"
|
123
|
+
}; type_map.default = "9999"
|
124
|
+
type = type_map[file_ext]
|
125
|
+
boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
|
126
|
+
|
127
|
+
query_hash = if first_file then
|
128
|
+
{
|
129
|
+
"group_id" => group_id,
|
130
|
+
"package_id" => package_id,
|
131
|
+
"release_name" => PKG_FILE_NAME,
|
132
|
+
"release_date" => release_date,
|
133
|
+
"type_id" => type,
|
134
|
+
"processor_id" => "8000", # Any
|
135
|
+
"release_notes" => "",
|
136
|
+
"release_changes" => "",
|
137
|
+
"preformatted" => "1",
|
138
|
+
"submit" => "1"
|
139
|
+
}
|
140
|
+
else
|
141
|
+
{
|
142
|
+
"group_id" => group_id,
|
143
|
+
"release_id" => release_id,
|
144
|
+
"package_id" => package_id,
|
145
|
+
"step2" => "1",
|
146
|
+
"type_id" => type,
|
147
|
+
"processor_id" => "8000", # Any
|
148
|
+
"submit" => "Add This File"
|
149
|
+
}
|
150
|
+
end
|
151
|
+
|
152
|
+
query = "?" + query_hash.map do |(name, value)|
|
153
|
+
[name, URI.encode(value)].join("=")
|
154
|
+
end.join("&")
|
155
|
+
|
156
|
+
data = [
|
157
|
+
"--" + boundary,
|
158
|
+
"Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
|
159
|
+
"Content-Type: application/octet-stream",
|
160
|
+
"Content-Transfer-Encoding: binary",
|
161
|
+
"", file_data, ""
|
162
|
+
].join("\x0D\x0A")
|
163
|
+
|
164
|
+
release_headers = headers.merge(
|
165
|
+
"Content-Type" => "multipart/form-data; boundary=#{boundary}"
|
166
|
+
)
|
167
|
+
|
168
|
+
target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
|
169
|
+
http.post(target + query, data, release_headers)
|
170
|
+
end
|
171
|
+
|
172
|
+
if first_file then
|
173
|
+
release_id = release_response.body[/release_id=(\d+)/, 1]
|
174
|
+
raise("Couldn't get release id") unless release_id
|
175
|
+
end
|
176
|
+
|
177
|
+
first_file = false
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Caboose # :nodoc:
|
2
|
+
module Acts # :nodoc:
|
3
|
+
class BelongsToWithDeletedAssociation < ActiveRecord::Associations::BelongsToAssociation
|
4
|
+
private
|
5
|
+
def find_target
|
6
|
+
@reflection.klass.find_with_deleted(
|
7
|
+
@owner[@reflection.primary_key_name],
|
8
|
+
:conditions => conditions,
|
9
|
+
:include => @reflection.options[:include]
|
10
|
+
)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Caboose # :nodoc:
|
2
|
+
module Acts # :nodoc:
|
3
|
+
class HasManyThroughWithoutDeletedAssociation < ActiveRecord::Associations::HasManyThroughAssociation
|
4
|
+
protected
|
5
|
+
def current_time
|
6
|
+
ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
|
7
|
+
end
|
8
|
+
|
9
|
+
def construct_conditions
|
10
|
+
return super unless @reflection.through_reflection.klass.paranoid?
|
11
|
+
table_name = @reflection.through_reflection.table_name
|
12
|
+
conditions = construct_quoted_owner_attributes(@reflection.through_reflection).map do |attr, value|
|
13
|
+
"#{table_name}.#{attr} = #{value}"
|
14
|
+
end
|
15
|
+
|
16
|
+
deleted_attribute = @reflection.through_reflection.klass.deleted_attribute
|
17
|
+
quoted_current_time = @reflection.through_reflection.klass.quote_value(
|
18
|
+
current_time,
|
19
|
+
@reflection.through_reflection.klass.columns_hash[deleted_attribute.to_s])
|
20
|
+
conditions << "#{table_name}.#{deleted_attribute} IS NULL OR #{table_name}.#{deleted_attribute} > #{quoted_current_time}"
|
21
|
+
|
22
|
+
conditions << sql_conditions if sql_conditions
|
23
|
+
"(" + conditions.join(') AND (') + ")"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,157 @@
|
|
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.find_with_deleted(1).deleted?
|
23
|
+
# # Returns true if the record was previously destroyed, false if not
|
24
|
+
#
|
25
|
+
# Widget.count
|
26
|
+
# # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NULL
|
27
|
+
#
|
28
|
+
# Widget.count ['title = ?', 'test']
|
29
|
+
# # SELECT COUNT(*) FROM widgets WHERE widgets.deleted_at IS NULL AND title = 'test'
|
30
|
+
#
|
31
|
+
# Widget.count_with_deleted
|
32
|
+
# # SELECT COUNT(*) FROM widgets
|
33
|
+
#
|
34
|
+
# Widget.delete_all
|
35
|
+
# # UPDATE widgets SET deleted_at = '2005-09-17 17:46:36'
|
36
|
+
#
|
37
|
+
# Widget.delete_all!
|
38
|
+
# # DELETE FROM widgets
|
39
|
+
#
|
40
|
+
# @widget.destroy
|
41
|
+
# # UPDATE widgets SET deleted_at = '2005-09-17 17:46:36' WHERE id = 1
|
42
|
+
#
|
43
|
+
# @widget.destroy!
|
44
|
+
# # DELETE FROM widgets WHERE id = 1
|
45
|
+
#
|
46
|
+
module Paranoid
|
47
|
+
def self.included(base) # :nodoc:
|
48
|
+
base.extend ClassMethods
|
49
|
+
end
|
50
|
+
|
51
|
+
module ClassMethods
|
52
|
+
def acts_as_paranoid(options = {})
|
53
|
+
unless paranoid? # don't let AR call this twice
|
54
|
+
cattr_accessor :deleted_attribute
|
55
|
+
self.deleted_attribute = options[:with] || :deleted_at
|
56
|
+
alias_method :destroy_without_callbacks!, :destroy_without_callbacks
|
57
|
+
class << self
|
58
|
+
alias_method :find_every_with_deleted, :find_every
|
59
|
+
alias_method :calculate_with_deleted, :calculate
|
60
|
+
alias_method :delete_all!, :delete_all
|
61
|
+
end
|
62
|
+
end
|
63
|
+
include InstanceMethods
|
64
|
+
end
|
65
|
+
|
66
|
+
def paranoid?
|
67
|
+
self.included_modules.include?(InstanceMethods)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module InstanceMethods #:nodoc:
|
72
|
+
def self.included(base) # :nodoc:
|
73
|
+
base.extend ClassMethods
|
74
|
+
end
|
75
|
+
|
76
|
+
module ClassMethods
|
77
|
+
def find_with_deleted(*args)
|
78
|
+
options = args.extract_options!
|
79
|
+
validate_find_options(options)
|
80
|
+
set_readonly_option!(options)
|
81
|
+
options[:with_deleted] = true # yuck!
|
82
|
+
|
83
|
+
case args.first
|
84
|
+
when :first then find_initial(options)
|
85
|
+
when :all then find_every(options)
|
86
|
+
else find_from_ids(args, options)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def exists?(*args)
|
91
|
+
with_deleted_scope { exists_with_deleted?(*args) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def count_with_deleted(*args)
|
95
|
+
calculate_with_deleted(:count, *construct_count_options_from_args(*args))
|
96
|
+
end
|
97
|
+
|
98
|
+
def count(*args)
|
99
|
+
with_deleted_scope { count_with_deleted(*args) }
|
100
|
+
end
|
101
|
+
|
102
|
+
def calculate(*args)
|
103
|
+
with_deleted_scope { calculate_with_deleted(*args) }
|
104
|
+
end
|
105
|
+
|
106
|
+
def delete_all(conditions = nil)
|
107
|
+
self.update_all ["#{self.deleted_attribute} = ?", current_time], conditions
|
108
|
+
end
|
109
|
+
|
110
|
+
protected
|
111
|
+
def current_time
|
112
|
+
default_timezone == :utc ? Time.now.utc : Time.now
|
113
|
+
end
|
114
|
+
|
115
|
+
def with_deleted_scope(&block)
|
116
|
+
with_scope({:find => { :conditions => ["#{table_name}.#{deleted_attribute} IS NULL OR #{table_name}.#{deleted_attribute} > ?", current_time] } }, :merge, &block)
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
# all find calls lead here
|
121
|
+
def find_every(options)
|
122
|
+
options.delete(:with_deleted) ?
|
123
|
+
find_every_with_deleted(options) :
|
124
|
+
with_deleted_scope { find_every_with_deleted(options) }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def destroy_without_callbacks
|
129
|
+
unless new_record?
|
130
|
+
self.class.update_all self.class.send(:sanitize_sql, ["#{self.class.deleted_attribute} = ?", self.class.send(:current_time)]), ["#{self.class.primary_key} = ?", id]
|
131
|
+
end
|
132
|
+
freeze
|
133
|
+
end
|
134
|
+
|
135
|
+
def destroy_with_callbacks!
|
136
|
+
return false if callback(:before_destroy) == false
|
137
|
+
result = destroy_without_callbacks!
|
138
|
+
callback(:after_destroy)
|
139
|
+
result
|
140
|
+
end
|
141
|
+
|
142
|
+
def destroy!
|
143
|
+
transaction { destroy_with_callbacks! }
|
144
|
+
end
|
145
|
+
|
146
|
+
def deleted?
|
147
|
+
!!read_attribute(:deleted_at)
|
148
|
+
end
|
149
|
+
|
150
|
+
def recover!
|
151
|
+
self.deleted_at = nil
|
152
|
+
save!
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class << ActiveRecord::Base
|
2
|
+
def belongs_to_with_deleted(association_id, options = {})
|
3
|
+
with_deleted = options.delete :with_deleted
|
4
|
+
returning belongs_to_without_deleted(association_id, options) do
|
5
|
+
if with_deleted
|
6
|
+
reflection = reflect_on_association(association_id)
|
7
|
+
association_accessor_methods(reflection, Caboose::Acts::BelongsToWithDeletedAssociation)
|
8
|
+
association_constructor_method(:build, reflection, Caboose::Acts::BelongsToWithDeletedAssociation)
|
9
|
+
association_constructor_method(:create, reflection, Caboose::Acts::BelongsToWithDeletedAssociation)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def has_many_without_deleted(association_id, options = {}, &extension)
|
15
|
+
with_deleted = options.delete :with_deleted
|
16
|
+
returning has_many_with_deleted(association_id, options, &extension) do
|
17
|
+
if options[:through] && !with_deleted
|
18
|
+
reflection = reflect_on_association(association_id)
|
19
|
+
collection_reader_method(reflection, Caboose::Acts::HasManyThroughWithoutDeletedAssociation)
|
20
|
+
collection_accessor_methods(reflection, Caboose::Acts::HasManyThroughWithoutDeletedAssociation, false)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
alias_method_chain :belongs_to, :deleted
|
26
|
+
alias_method :has_many_with_deleted, :has_many
|
27
|
+
alias_method :has_many, :has_many_without_deleted
|
28
|
+
alias_method :exists_with_deleted?, :exists?
|
29
|
+
end
|
30
|
+
ActiveRecord::Base.send :include, Caboose::Acts::Paranoid
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'pager-acts_as_paranoid'
|
data/test/database.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
sqlite:
|
2
|
+
:adapter: sqlite
|
3
|
+
:dbfile: acts_as_paranoid_plugin.sqlite.db
|
4
|
+
sqlite3:
|
5
|
+
:adapter: sqlite3
|
6
|
+
:dbfile: acts_as_paranoid_plugin.sqlite3.db
|
7
|
+
postgresql:
|
8
|
+
:adapter: postgresql
|
9
|
+
:username: postgres
|
10
|
+
:password: postgres
|
11
|
+
:database: acts_as_paranoid_plugin_test
|
12
|
+
:min_messages: ERROR
|
13
|
+
mysql:
|
14
|
+
:adapter: mysql
|
15
|
+
:host: localhost
|
16
|
+
:username: rails
|
17
|
+
:password:
|
18
|
+
:database: acts_as_paranoid_plugin_test
|
@@ -0,0 +1,19 @@
|
|
1
|
+
category_1:
|
2
|
+
id: 1
|
3
|
+
widget_id: 1
|
4
|
+
title: 'category 1'
|
5
|
+
category_2:
|
6
|
+
id: 2
|
7
|
+
widget_id: 1
|
8
|
+
title: 'category 2'
|
9
|
+
deleted_at: '2005-01-01 00:00:00'
|
10
|
+
category_3:
|
11
|
+
id: 3
|
12
|
+
widget_id: 2
|
13
|
+
title: 'category 3'
|
14
|
+
deleted_at: '2005-01-01 00:00:00'
|
15
|
+
category_4:
|
16
|
+
id: 4
|
17
|
+
widget_id: 2
|
18
|
+
title: 'category 4'
|
19
|
+
deleted_at: '2005-01-01 00:00:00'
|
@@ -0,0 +1,246 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
|
+
|
3
|
+
class Widget < ActiveRecord::Base
|
4
|
+
acts_as_paranoid
|
5
|
+
has_many :categories, :dependent => :destroy
|
6
|
+
has_and_belongs_to_many :habtm_categories, :class_name => 'Category'
|
7
|
+
has_one :category
|
8
|
+
belongs_to :parent_category, :class_name => 'Category'
|
9
|
+
has_many :taggings
|
10
|
+
has_many :tags, :through => :taggings
|
11
|
+
has_many :any_tags, :through => :taggings, :class_name => 'Tag', :source => :tag, :with_deleted => true
|
12
|
+
end
|
13
|
+
|
14
|
+
class Category < ActiveRecord::Base
|
15
|
+
belongs_to :widget
|
16
|
+
belongs_to :any_widget, :class_name => 'Widget', :foreign_key => 'widget_id', :with_deleted => true
|
17
|
+
acts_as_paranoid
|
18
|
+
|
19
|
+
def self.search(name, options = {})
|
20
|
+
find :all, options.merge(:conditions => ['LOWER(title) LIKE ?', "%#{name.to_s.downcase}%"])
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.search_with_deleted(name, options = {})
|
24
|
+
find_with_deleted :all, options.merge(:conditions => ['LOWER(title) LIKE ?', "%#{name.to_s.downcase}%"])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Tag < ActiveRecord::Base
|
29
|
+
has_many :taggings
|
30
|
+
has_many :widgets, :through => :taggings
|
31
|
+
end
|
32
|
+
|
33
|
+
class Tagging < ActiveRecord::Base
|
34
|
+
belongs_to :tag
|
35
|
+
belongs_to :widget
|
36
|
+
acts_as_paranoid
|
37
|
+
end
|
38
|
+
|
39
|
+
class NonParanoidAndroid < ActiveRecord::Base
|
40
|
+
end
|
41
|
+
|
42
|
+
class ParanoidTest < Test::Unit::TestCase
|
43
|
+
fixtures :widgets, :categories, :categories_widgets, :tags, :taggings
|
44
|
+
|
45
|
+
def test_should_exists_with_deleted
|
46
|
+
assert Widget.exists_with_deleted?(2)
|
47
|
+
assert !Widget.exists?(2)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_should_count_with_deleted
|
51
|
+
assert_equal 1, Widget.count
|
52
|
+
assert_equal 2, Widget.count_with_deleted
|
53
|
+
assert_equal 2, Widget.calculate_with_deleted(:count, :all)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_should_set_deleted_at
|
57
|
+
assert_equal 1, Widget.count
|
58
|
+
assert_equal 1, Category.count
|
59
|
+
widgets(:widget_1).destroy
|
60
|
+
assert_equal 0, Widget.count
|
61
|
+
assert_equal 0, Category.count
|
62
|
+
assert_equal 2, Widget.calculate_with_deleted(:count, :all)
|
63
|
+
assert_equal 4, Category.calculate_with_deleted(:count, :all)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_should_destroy
|
67
|
+
assert_equal 1, Widget.count
|
68
|
+
assert_equal 1, Category.count
|
69
|
+
widgets(:widget_1).destroy!
|
70
|
+
assert_equal 0, Widget.count
|
71
|
+
assert_equal 0, Category.count
|
72
|
+
assert_equal 1, Widget.calculate_with_deleted(:count, :all)
|
73
|
+
# Category doesn't get destroyed because the dependent before_destroy callback uses #destroy
|
74
|
+
assert_equal 4, Category.calculate_with_deleted(:count, :all)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_should_delete_all
|
78
|
+
assert_equal 1, Widget.count
|
79
|
+
assert_equal 2, Widget.calculate_with_deleted(:count, :all)
|
80
|
+
assert_equal 1, Category.count
|
81
|
+
Widget.delete_all
|
82
|
+
assert_equal 0, Widget.count
|
83
|
+
# delete_all doesn't call #destroy, so the dependent callback never fires
|
84
|
+
assert_equal 1, Category.count
|
85
|
+
assert_equal 2, Widget.calculate_with_deleted(:count, :all)
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_should_delete_all_with_conditions
|
89
|
+
assert_equal 1, Widget.count
|
90
|
+
assert_equal 2, Widget.calculate_with_deleted(:count, :all)
|
91
|
+
Widget.delete_all("id < 3")
|
92
|
+
assert_equal 0, Widget.count
|
93
|
+
assert_equal 2, Widget.calculate_with_deleted(:count, :all)
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_should_delete_all2
|
97
|
+
assert_equal 1, Category.count
|
98
|
+
assert_equal 4, Category.calculate_with_deleted(:count, :all)
|
99
|
+
Category.delete_all!
|
100
|
+
assert_equal 0, Category.count
|
101
|
+
assert_equal 0, Category.calculate_with_deleted(:count, :all)
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_should_delete_all_with_conditions2
|
105
|
+
assert_equal 1, Category.count
|
106
|
+
assert_equal 4, Category.calculate_with_deleted(:count, :all)
|
107
|
+
Category.delete_all!("id < 3")
|
108
|
+
assert_equal 0, Category.count
|
109
|
+
assert_equal 2, Category.calculate_with_deleted(:count, :all)
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_should_not_count_deleted
|
113
|
+
assert_equal 1, Widget.count
|
114
|
+
assert_equal 1, Widget.count(:all, :conditions => ['title=?', 'widget 1'])
|
115
|
+
assert_equal 2, Widget.calculate_with_deleted(:count, :all)
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_should_not_find_deleted
|
119
|
+
assert_equal [widgets(:widget_1)], Widget.find(:all)
|
120
|
+
assert_equal [1, 2], Widget.find_with_deleted(:all, :order => 'id').collect { |w| w.id }
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_should_not_find_deleted_has_many_associations
|
124
|
+
assert_equal 1, widgets(:widget_1).categories.size
|
125
|
+
assert_equal [categories(:category_1)], widgets(:widget_1).categories
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_should_not_find_deleted_habtm_associations
|
129
|
+
assert_equal 1, widgets(:widget_1).habtm_categories.size
|
130
|
+
assert_equal [categories(:category_1)], widgets(:widget_1).habtm_categories
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_should_not_find_deleted_has_many_through_associations
|
134
|
+
assert_equal 1, widgets(:widget_1).tags.size
|
135
|
+
assert_equal [tags(:tag_2)], widgets(:widget_1).tags
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_should_find_has_many_through_associations_with_deleted
|
139
|
+
assert_equal 2, widgets(:widget_1).any_tags.size
|
140
|
+
assert_equal Tag.find(:all), widgets(:widget_1).any_tags
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_should_not_find_deleted_belongs_to_associations
|
144
|
+
assert_nil Category.find_with_deleted(3).widget
|
145
|
+
end
|
146
|
+
|
147
|
+
def test_should_find_belongs_to_assocation_with_deleted
|
148
|
+
assert_equal Widget.find_with_deleted(2), Category.find_with_deleted(3).any_widget
|
149
|
+
end
|
150
|
+
|
151
|
+
def test_should_find_first_with_deleted
|
152
|
+
assert_equal widgets(:widget_1), Widget.find(:first)
|
153
|
+
assert_equal 2, Widget.find_with_deleted(:first, :order => 'id desc').id
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_should_find_single_id
|
157
|
+
assert Widget.find(1)
|
158
|
+
assert Widget.find_with_deleted(2)
|
159
|
+
assert_raises(ActiveRecord::RecordNotFound) { Widget.find(2) }
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_should_find_multiple_ids
|
163
|
+
assert_equal [1,2], Widget.find_with_deleted(1,2).sort_by { |w| w.id }.collect { |w| w.id }
|
164
|
+
assert_equal [1,2], Widget.find_with_deleted([1,2]).sort_by { |w| w.id }.collect { |w| w.id }
|
165
|
+
assert_raises(ActiveRecord::RecordNotFound) { Widget.find(1,2) }
|
166
|
+
end
|
167
|
+
|
168
|
+
def test_should_ignore_multiple_includes
|
169
|
+
Widget.class_eval { acts_as_paranoid }
|
170
|
+
assert Widget.find(1)
|
171
|
+
end
|
172
|
+
|
173
|
+
def test_should_not_override_scopes_when_counting
|
174
|
+
assert_equal 1, Widget.send(:with_scope, :find => { :conditions => "title = 'widget 1'" }) { Widget.count }
|
175
|
+
assert_equal 0, Widget.send(:with_scope, :find => { :conditions => "title = 'deleted widget 2'" }) { Widget.count }
|
176
|
+
assert_equal 1, Widget.send(:with_scope, :find => { :conditions => "title = 'deleted widget 2'" }) { Widget.calculate_with_deleted(:count, :all) }
|
177
|
+
end
|
178
|
+
|
179
|
+
def test_should_not_override_scopes_when_finding
|
180
|
+
assert_equal [1], Widget.send(:with_scope, :find => { :conditions => "title = 'widget 1'" }) { Widget.find(:all) }.ids
|
181
|
+
assert_equal [], Widget.send(:with_scope, :find => { :conditions => "title = 'deleted widget 2'" }) { Widget.find(:all) }.ids
|
182
|
+
assert_equal [2], Widget.send(:with_scope, :find => { :conditions => "title = 'deleted widget 2'" }) { Widget.find_with_deleted(:all) }.ids
|
183
|
+
end
|
184
|
+
|
185
|
+
def test_should_allow_multiple_scoped_calls_when_finding
|
186
|
+
Widget.send(:with_scope, :find => { :conditions => "title = 'deleted widget 2'" }) do
|
187
|
+
assert_equal [2], Widget.find_with_deleted(:all).ids
|
188
|
+
assert_equal [2], Widget.find_with_deleted(:all).ids, "clobbers the constrain on the unmodified find"
|
189
|
+
assert_equal [], Widget.find(:all).ids
|
190
|
+
assert_equal [], Widget.find(:all).ids, 'clobbers the constrain on a paranoid find'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def test_should_allow_multiple_scoped_calls_when_counting
|
195
|
+
Widget.send(:with_scope, :find => { :conditions => "title = 'deleted widget 2'" }) do
|
196
|
+
assert_equal 1, Widget.calculate_with_deleted(:count, :all)
|
197
|
+
assert_equal 1, Widget.calculate_with_deleted(:count, :all), "clobbers the constrain on the unmodified find"
|
198
|
+
assert_equal 0, Widget.count
|
199
|
+
assert_equal 0, Widget.count, 'clobbers the constrain on a paranoid find'
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def test_should_give_paranoid_status
|
204
|
+
assert Widget.paranoid?
|
205
|
+
assert !NonParanoidAndroid.paranoid?
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_should_give_record_status
|
209
|
+
assert_equal false, Widget.find(1).deleted?
|
210
|
+
Widget.find(1).destroy
|
211
|
+
assert Widget.find_with_deleted(1).deleted?
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_should_find_deleted_has_many_assocations_on_deleted_records_by_default
|
215
|
+
w = Widget.find_with_deleted 2
|
216
|
+
assert_equal 2, w.categories.find_with_deleted(:all).length
|
217
|
+
assert_equal 2, w.categories.find_with_deleted(:all).size
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_should_find_deleted_habtm_assocations_on_deleted_records_by_default
|
221
|
+
w = Widget.find_with_deleted 2
|
222
|
+
assert_equal 2, w.habtm_categories.find_with_deleted(:all).length
|
223
|
+
assert_equal 2, w.habtm_categories.find_with_deleted(:all).size
|
224
|
+
end
|
225
|
+
|
226
|
+
def test_dynamic_finders
|
227
|
+
assert Widget.find_by_id(1)
|
228
|
+
assert_nil Widget.find_by_id(2)
|
229
|
+
end
|
230
|
+
|
231
|
+
def test_custom_finder_methods
|
232
|
+
w = Widget.find_with_deleted(:all).inject({}) { |all, w| all.merge(w.id => w) }
|
233
|
+
assert_equal [1], Category.search('c').ids
|
234
|
+
assert_equal [1,2,3,4], Category.search_with_deleted('c', :order => 'id').ids
|
235
|
+
assert_equal [1], widgets(:widget_1).categories.search('c').collect(&:id)
|
236
|
+
assert_equal [1,2], widgets(:widget_1).categories.search_with_deleted('c').ids
|
237
|
+
assert_equal [], w[2].categories.search('c').ids
|
238
|
+
assert_equal [3,4], w[2].categories.search_with_deleted('c').ids
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
class Array
|
243
|
+
def ids
|
244
|
+
collect &:id
|
245
|
+
end
|
246
|
+
end
|
data/test/schema.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 1) do
|
2
|
+
|
3
|
+
create_table :widgets, :force => true do |t|
|
4
|
+
t.column :title, :string, :limit => 50
|
5
|
+
t.column :category_id, :integer
|
6
|
+
t.column :deleted_at, :timestamp
|
7
|
+
end
|
8
|
+
|
9
|
+
create_table :categories, :force => true do |t|
|
10
|
+
t.column :widget_id, :integer
|
11
|
+
t.column :title, :string, :limit => 50
|
12
|
+
t.column :deleted_at, :timestamp
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :categories_widgets, :force => true, :id => false do |t|
|
16
|
+
t.column :category_id, :integer
|
17
|
+
t.column :widget_id, :integer
|
18
|
+
end
|
19
|
+
|
20
|
+
create_table :tags, :force => true do |t|
|
21
|
+
t.column :name, :string, :limit => 50
|
22
|
+
end
|
23
|
+
|
24
|
+
create_table :taggings, :force => true do |t|
|
25
|
+
t.column :tag_id, :integer
|
26
|
+
t.column :widget_id, :integer
|
27
|
+
t.column :deleted_at, :timestamp
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../../../../config/environment.rb'))
|
5
|
+
require 'rubygems'
|
6
|
+
require 'active_record/fixtures'
|
7
|
+
|
8
|
+
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
9
|
+
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
10
|
+
ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite'])
|
11
|
+
|
12
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
13
|
+
|
14
|
+
Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
|
15
|
+
$LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path)
|
16
|
+
|
17
|
+
class Test::Unit::TestCase #:nodoc:
|
18
|
+
def create_fixtures(*table_names)
|
19
|
+
if block_given?
|
20
|
+
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
|
21
|
+
else
|
22
|
+
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Turn off transactional fixtures if you're working with MyISAM tables in MySQL
|
27
|
+
self.use_transactional_fixtures = true
|
28
|
+
|
29
|
+
# Instantiated fixtures are slow, but give you @david where you otherwise would need people(:david)
|
30
|
+
self.use_instantiated_fixtures = false
|
31
|
+
|
32
|
+
# Add more helper methods to be used by all tests here...
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pager-acts_as_paranoid
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.20080505
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- technoweenie
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-05-05 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: avanie@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
- CHANGELOG
|
25
|
+
- MIT-LICENSE
|
26
|
+
- RUNNING_UNIT_TESTS
|
27
|
+
files:
|
28
|
+
- CHANGELOG
|
29
|
+
- Rakefile
|
30
|
+
- lib/caboose/acts/belongs_to_with_deleted_association.rb
|
31
|
+
- lib/caboose/acts/has_many_through_without_deleted_association.rb
|
32
|
+
- lib/caboose/acts/paranoid.rb
|
33
|
+
- lib/pager-acts_as_paranoid.rb
|
34
|
+
- MIT-LICENSE
|
35
|
+
- rails/init.rb
|
36
|
+
- README
|
37
|
+
- RUNNING_UNIT_TESTS
|
38
|
+
- test/database.yml
|
39
|
+
- test/fixtures/categories.yml
|
40
|
+
- test/fixtures/categories_widgets.yml
|
41
|
+
- test/fixtures/taggings.yml
|
42
|
+
- test/fixtures/tags.yml
|
43
|
+
- test/fixtures/widgets.yml
|
44
|
+
- test/paranoid_test.rb
|
45
|
+
- test/schema.rb
|
46
|
+
- test/test_helper.rb
|
47
|
+
has_rdoc: true
|
48
|
+
homepage: http://github.com/pager/acts_as_paranoid
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options:
|
51
|
+
- --main
|
52
|
+
- README
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: "0"
|
60
|
+
version:
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.0.1
|
71
|
+
signing_key:
|
72
|
+
specification_version: 2
|
73
|
+
summary: Gemified technoweenie acts_as_paranoid plugin
|
74
|
+
test_files:
|
75
|
+
- test/database.yml
|
76
|
+
- test/fixtures/categories.yml
|
77
|
+
- test/fixtures/categories_widgets.yml
|
78
|
+
- test/fixtures/taggings.yml
|
79
|
+
- test/fixtures/tags.yml
|
80
|
+
- test/fixtures/widgets.yml
|
81
|
+
- test/paranoid_test.rb
|
82
|
+
- test/schema.rb
|
83
|
+
- test/test_helper.rb
|