goldstar-is_paranoid 0.9.0.1 → 0.9.6.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ is_paranoid.db
2
+ pkg
3
+ *.gem
4
+ doc/
data/CHANGELOG CHANGED
@@ -1,5 +1,17 @@
1
1
  This will only document major changes. Please see the commit log for minor changes.
2
2
 
3
+ -2009-09-25
4
+ * fixing bug with has_many :through not respecting is_paranoid conditions (thanks, Ben Johnson)
5
+
6
+ -2009-09-18
7
+ * making is_paranoid play nice with habtm relationships
8
+
9
+ -2009-09-17
10
+ * fixed when primary key was not "id" (ie: "uuid") via Thibaud Guillaume-Gentil
11
+
12
+ -2009-09-15
13
+ * added support for has_many associations (i.e. @r2d2.components_with_destroyed) via Amiel Martin
14
+
3
15
  -2009-06-13
4
16
  * added support for is_paranoid conditions being maintained on preloaded associations
5
17
  * destroy and restore now return self (to be more in keeping with other ActiveRecord methods) via Brent Dillingham
@@ -28,4 +40,4 @@ This will only document major changes. Please see the commit log for minor chan
28
40
  * requiring specific syntax to include calculations
29
41
 
30
42
  -2009-03-21
31
- * initial release
43
+ * initial release
data/README.textile CHANGED
@@ -1,5 +1,19 @@
1
+ h1. NOTICE: this library is no longer supported or actively developed by the original author. It never made it to a 1.0 stable version. Use it at your own risk and write lots of tests.
2
+
3
+ You can read more here: http://blog.semanticart.com/killing_is_paranoid/
4
+
1
5
  h1. is_paranoid ( same as it ever was )
2
6
 
7
+ h3. advice and disclaimer
8
+
9
+ You should always declare is_paranoid before any associations in your model unless you have a good reason for doing otherwise. Some relationships might not behave properly if you fail to do so. If you know what you're doing (and have written tests) and want to supress the warning then you can pass :suppress_load_order_warning => true as an option.
10
+
11
+ <pre>
12
+ is_paranoid :suppress_load_order_warning => true
13
+ </pre>
14
+
15
+ You should never expect _any_ library to work or behave exactly how you want it to: test, test, test and file an issue if you have any problems. Bonus points if you include sample failing code. Extra bonus points if you send a pull request that implements a feature/fixes a bug.
16
+
3
17
  h3. and you may ask yourself, well, how did I get here?
4
18
 
5
19
  Sometimes you want to delete something in ActiveRecord, but you realize you might need it later (for an undo feature, or just as a safety net, etc.). There are a plethora of plugins that accomplish this, the most famous of which is the venerable acts_as_paranoid which is great but not really actively developed any more. What's more, acts_as_paranoid was written for an older version of ActiveRecord and, with default_scope in 2.3, it is now possible to do the same thing with significantly less complexity. Thus, *is_paranoid*.
@@ -17,7 +31,7 @@ If you're working with Rails, in your environment.rb, add the following to your
17
31
  <pre>
18
32
  Rails::Initializer.run do |config|
19
33
  # ...
20
- config.gem "jchupp-is_paranoid", :lib => 'is_paranoid', :version => ">= 0.0.1"
34
+ config.gem "semanticart-is_paranoid", :lib => 'is_paranoid', :version => ">= 0.0.1"
21
35
  end
22
36
  </pre>
23
37
 
@@ -91,7 +105,7 @@ And now the validates_uniqueness_of will ignore items that are destroyed.
91
105
 
92
106
  h3. and you may ask yourself, where does that highway go to?
93
107
 
94
- If you find any bugs, have any ideas of features you think are missing, or find things you're like to see work differently, feel free to send me a message or a pull request.
108
+ If you find any bugs, have any ideas of features you think are missing, or find things you're like to see work differently, feel free to file an issue or send a pull request.
95
109
 
96
110
  Currently on the todo list:
97
111
  * add options for merging additional default_scope options (i.e. order, etc.)
@@ -100,4 +114,4 @@ h3. Thanks
100
114
 
101
115
  Thanks to Rick Olson for acts_as_paranoid which is obviously an inspiration in concept and execution, Ryan Bates for mentioning the idea of using default_scope for this on Ryan Daigle's "post introducing default_scope":defscope, and the Talking Heads for being the Talking Heads.
102
116
 
103
- [defscope]http://ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping
117
+ [defscope]http://ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ begin
11
11
  require 'jeweler'
12
12
  Jeweler::Tasks.new do |s|
13
13
  s.name = %q{goldstar-is_paranoid}
14
- s.summary = %q{ActiveRecord 2.3 compatible gem "allowing you to hide and restore records without actually deleting them." Yes, like acts_as_paranoid, only with less code and less complexity. This particular Goldstar version is patched to not emit warnings with Rails 2.3.10.}
14
+ s.summary = %q{ActiveRecord 2.3 compatible gem "allowing you to hide and restore records without actually deleting them." Yes, like acts_as_paranoid, only with less code and less complexity.}
15
15
  s.email = %q{jeff@semanticart.com}
16
16
  s.homepage = %q{http://github.com/jchupp/is_paranoid/}
17
17
  s.description = ""
data/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- :patch: 0
2
+ :patch: 6
3
3
  :major: 0
4
- :build: 1
5
4
  :minor: 9
5
+ :build: 1
data/is_paranoid.gemspec CHANGED
@@ -1,40 +1,47 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
1
4
  # -*- encoding: utf-8 -*-
2
5
 
3
6
  Gem::Specification.new do |s|
4
7
  s.name = %q{is_paranoid}
5
- s.version = "0.9.0"
8
+ s.version = "0.9.6"
6
9
 
7
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
11
  s.authors = ["Jeffrey Chupp"]
9
- s.date = %q{2009-06-13}
12
+ s.date = %q{2009-09-26}
10
13
  s.description = %q{}
11
14
  s.email = %q{jeff@semanticart.com}
12
15
  s.extra_rdoc_files = [
13
16
  "README.textile"
14
17
  ]
15
18
  s.files = [
16
- "README.textile",
17
- "Rakefile",
18
- "VERSION.yml",
19
- "lib/is_paranoid.rb",
20
- "spec/database.yml",
21
- "spec/is_paranoid_spec.rb",
22
- "spec/models.rb",
23
- "spec/schema.rb",
24
- "spec/spec.opts",
25
- "spec/spec_helper.rb"
19
+ ".gitignore",
20
+ "CHANGELOG",
21
+ "MIT-LICENSE",
22
+ "README.textile",
23
+ "Rakefile",
24
+ "VERSION.yml",
25
+ "init.rb",
26
+ "is_paranoid.gemspec",
27
+ "lib/is_paranoid.rb",
28
+ "spec/database.yml",
29
+ "spec/is_paranoid_spec.rb",
30
+ "spec/models.rb",
31
+ "spec/schema.rb",
32
+ "spec/spec.opts",
33
+ "spec/spec_helper.rb"
26
34
  ]
27
- s.has_rdoc = true
28
35
  s.homepage = %q{http://github.com/jchupp/is_paranoid/}
29
36
  s.rdoc_options = ["--charset=UTF-8"]
30
37
  s.require_paths = ["lib"]
31
- s.rubygems_version = %q{1.3.2}
38
+ s.rubygems_version = %q{1.3.5}
32
39
  s.summary = %q{ActiveRecord 2.3 compatible gem "allowing you to hide and restore records without actually deleting them." Yes, like acts_as_paranoid, only with less code and less complexity.}
33
40
  s.test_files = [
34
41
  "spec/is_paranoid_spec.rb",
35
- "spec/models.rb",
36
- "spec/schema.rb",
37
- "spec/spec_helper.rb"
42
+ "spec/models.rb",
43
+ "spec/schema.rb",
44
+ "spec/spec_helper.rb"
38
45
  ]
39
46
 
40
47
  if s.respond_to? :specification_version then
data/lib/is_paranoid.rb CHANGED
@@ -15,6 +15,10 @@ module IsParanoid
15
15
  class_inheritable_accessor :destroyed_field, :field_destroyed, :field_not_destroyed
16
16
  self.destroyed_field, self.field_destroyed, self.field_not_destroyed = opts[:field]
17
17
 
18
+ if self.reflect_on_all_associations.size > 0 && ! opts[:suppress_load_order_warning]
19
+ warn "is_paranoid warning in class #{self}: You should declare is_paranoid before your associations"
20
+ end
21
+
18
22
  # This is the real magic. All calls made to this model will append
19
23
  # the conditions deleted_at => nil (or whatever your destroyed_field
20
24
  # and field_not_destroyed are). All exceptions require using
@@ -27,6 +31,25 @@ module IsParanoid
27
31
  end
28
32
 
29
33
  module ClassMethods
34
+ def is_or_equals_not_destroyed
35
+ if [nil, 'NULL'].include?(field_not_destroyed)
36
+ 'IS NULL'
37
+ else
38
+ "= #{field_not_destroyed}"
39
+ end
40
+ end
41
+
42
+ # ensure that we respect the is_paranoid conditions when being loaded as a has_many :through
43
+ # NOTE: this only works if is_paranoid is declared before has_many relationships.
44
+ def has_many(association_id, options = {}, &extension)
45
+ if options.key?(:through)
46
+ conditions = "#{options[:through].to_s.pluralize}.#{destroyed_field} #{is_or_equals_not_destroyed}"
47
+ options[:conditions] = "(" + [options[:conditions], conditions].compact.join(") AND (") + ")"
48
+ end
49
+ super
50
+ end
51
+
52
+
30
53
  # Actually delete the model, bypassing the safety net. Because
31
54
  # this method is called internally by Model.delete(id) and on the
32
55
  # delete method in each instance, we don't need to specify those
@@ -60,8 +83,8 @@ module IsParanoid
60
83
  options.reverse_merge!({:include_destroyed_dependents => true}) unless options[:include]
61
84
  with_exclusive_scope do
62
85
  update_all(
63
- "#{destroyed_field} = #{connection.quote(field_not_destroyed)}",
64
- "id = #{id}"
86
+ "#{destroyed_field} = #{connection.quote(field_not_destroyed)}",
87
+ primary_key.to_sym => id
65
88
  )
66
89
  end
67
90
 
@@ -176,15 +199,30 @@ module IsParanoid
176
199
 
177
200
  self.class.send(
178
201
  :include,
179
- Module.new{ # Example:
180
- define_method name do |*args| # def android_with_destroyed
181
- parent_klass.first_with_destroyed( # Android.first_with_destroyed(
182
- :conditions => { # :conditions => {
183
- parent_klass.primary_key => # :id =>
184
- self.send(assoc.primary_key_name) # self.send(:android_id)
185
- } # }
186
- ) # )
187
- end # end
202
+ Module.new {
203
+ if assoc.macro.to_s =~ /^has/
204
+ parent_method = assoc.macro.to_s =~ /^has_one/ ? 'first_with_destroyed' : 'all_with_destroyed'
205
+ # Example:
206
+ define_method name do |*args| # def android_with_destroyed
207
+ parent_klass.send("#{parent_method}", # Android.all_with_destroyed(
208
+ :conditions => { # :conditions => {
209
+ assoc.primary_key_name => # :person_id =>
210
+ self.send(parent_klass.primary_key) # self.send(:id)
211
+ } # }
212
+ ) # )
213
+ end # end
214
+
215
+ else
216
+ # Example:
217
+ define_method name do |*args| # def android_with_destroyed
218
+ parent_klass.first_with_destroyed( # Android.first_with_destroyed(
219
+ :conditions => { # :conditions => {
220
+ parent_klass.primary_key => # :id =>
221
+ self.send(assoc.primary_key_name) # self.send(:android_id)
222
+ } # }
223
+ ) # )
224
+ end # end
225
+ end
188
226
  }
189
227
  )
190
228
  self.send(name, *args, &block)
@@ -194,10 +232,10 @@ module IsParanoid
194
232
  end
195
233
 
196
234
  # Mark the model deleted_at as now.
197
- def destroy_without_callbacks
235
+ def alt_destroy_without_callbacks
198
236
  self.class.update_all(
199
237
  "#{destroyed_field} = #{self.class.connection.quote(( field_destroyed.respond_to?(:call) ? field_destroyed.call : field_destroyed))}",
200
- "id = #{self.id}"
238
+ self.class.primary_key.to_sym => self.id
201
239
  )
202
240
  self
203
241
  end
@@ -209,7 +247,7 @@ module IsParanoid
209
247
  # separately.
210
248
  def destroy
211
249
  return false if callback(:before_destroy) == false
212
- result = destroy_without_callbacks
250
+ result = alt_destroy_without_callbacks
213
251
  callback(:after_destroy)
214
252
  self
215
253
  end
data/spec/database.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  test:
2
2
  :adapter: sqlite3
3
- :dbfile: is_paranoid.db
3
+ :database: is_paranoid.db
@@ -17,6 +17,8 @@ describe IsParanoid do
17
17
 
18
18
  @r2d2.memories.create(:name => 'A pretty sunset')
19
19
  @c3p0.sticker = Sticker.create(:name => 'OMG, PONIES!')
20
+ @tatooine = Place.create(:name => "Tatooine")
21
+ @r2d2.places << @tatooine
20
22
  end
21
23
 
22
24
  describe 'non-is_paranoid models' do
@@ -69,6 +71,32 @@ describe IsParanoid do
69
71
  }.should change(Android, :count).from(2).to(0)
70
72
  Android.count_with_destroyed.should == 2
71
73
  end
74
+
75
+ it "shouldn't have problems with has_many :through relationships" do
76
+ # TODO: this spec can be cleaner and more specific, replace it later
77
+ # Dings use a boolean non-standard is_paranoid field
78
+ # Scratch uses the defaults. Testing both ensures compatibility
79
+ [[:dings, Ding], [:scratches, Scratch]].each do |method, klass|
80
+ @r2d2.dings.should == []
81
+
82
+ dent = Dent.create(:description => 'really terrible', :android_id => @r2d2.id)
83
+ item = klass.create(:description => 'quite nasty', :dent_id => dent.id)
84
+ @r2d2.reload
85
+ @r2d2.send(method).should == [item]
86
+
87
+ dent.destroy
88
+ @r2d2.reload
89
+ @r2d2.send(method).should == []
90
+ end
91
+ end
92
+
93
+ it "should not choke has_and_belongs_to_many relationships" do
94
+ @r2d2.places.should include(@tatooine)
95
+ @tatooine.destroy
96
+ @r2d2.reload
97
+ @r2d2.places.should_not include(@tatooine)
98
+ Place.all_with_destroyed.should include(@tatooine)
99
+ end
72
100
  end
73
101
  end
74
102
 
@@ -203,6 +231,13 @@ describe IsParanoid do
203
231
  end
204
232
  end
205
233
 
234
+ it "should be able to access destroyed children" do
235
+ comps = @r2d2.components
236
+ comps.to_s # I have no idea why this makes it pass, but hey, here it is
237
+ @r2d2.components.first.destroy
238
+ @r2d2.components_with_destroyed.should == comps
239
+ end
240
+
206
241
  it "should return nil if no destroyed parent exists" do
207
242
  sticker = Sticker.new(:name => 'Rainbows')
208
243
  # because the default relationship works this way, i.e.
@@ -274,4 +309,11 @@ describe IsParanoid do
274
309
  end
275
310
 
276
311
  end
312
+
313
+ describe "alternate primary key" do
314
+ it "should destroy without problem" do
315
+ uuid = Uuid.create(:name => "foo")
316
+ uuid.destroy.should be_true
317
+ end
318
+ end
277
319
  end
data/spec/models.rb CHANGED
@@ -4,12 +4,15 @@ class Person < ActiveRecord::Base #:nodoc:
4
4
  end
5
5
 
6
6
  class Android < ActiveRecord::Base #:nodoc:
7
+ is_paranoid
7
8
  validates_uniqueness_of :name
8
9
  has_many :components, :dependent => :destroy
9
10
  has_one :sticker
10
11
  has_many :memories, :foreign_key => 'parent_id'
11
-
12
- is_paranoid
12
+ has_many :dents
13
+ has_many :dings, :through => :dents
14
+ has_many :scratches, :through => :dents
15
+ has_and_belongs_to_many :places
13
16
 
14
17
  # this code is to ensure that our destroy and restore methods
15
18
  # work without triggering before/after_update callbacks
@@ -19,6 +22,23 @@ class Android < ActiveRecord::Base #:nodoc:
19
22
  end
20
23
  end
21
24
 
25
+ class Dent < ActiveRecord::Base #:nodoc:
26
+ is_paranoid
27
+ belongs_to :android
28
+ has_many :dings
29
+ has_many :scratches
30
+ end
31
+
32
+ class Ding < ActiveRecord::Base #:nodoc:
33
+ is_paranoid :field => [:not_deleted, true, false]
34
+ belongs_to :dent
35
+ end
36
+
37
+ class Scratch < ActiveRecord::Base #:nodoc:
38
+ is_paranoid
39
+ belongs_to :dent
40
+ end
41
+
22
42
  class Component < ActiveRecord::Base #:nodoc:
23
43
  is_paranoid
24
44
  belongs_to :android, :dependent => :destroy
@@ -41,7 +61,7 @@ class Memory < ActiveRecord::Base #:nodoc:
41
61
  belongs_to :android, :class_name => "Android", :foreign_key => "parent_id"
42
62
  end
43
63
 
44
- class Sticker < ActiveRecord::Base #:nodoc
64
+ class Sticker < ActiveRecord::Base #:nodoc:
45
65
  MM_NAME = "You've got method_missing"
46
66
 
47
67
  # this simply serves to ensure that we don't break method_missing
@@ -60,6 +80,14 @@ class AndroidWithScopedUniqueness < ActiveRecord::Base #:nodoc:
60
80
  is_paranoid
61
81
  end
62
82
 
83
+ class Place < ActiveRecord::Base #:nodoc:
84
+ is_paranoid
85
+ has_and_belongs_to_many :androids
86
+ end
87
+
88
+ class AndroidsPlaces < ActiveRecord::Base #:nodoc:
89
+ end
90
+
63
91
  class Ninja < ActiveRecord::Base #:nodoc:
64
92
  validates_uniqueness_of :name, :scope => :visible
65
93
  is_paranoid :field => [:visible, false, true]
@@ -91,4 +119,14 @@ class UndestroyablePirate < ActiveRecord::Base #:nodoc:
91
119
  def before_destroy
92
120
  false
93
121
  end
94
- end
122
+ end
123
+
124
+ class Uuid < ActiveRecord::Base #:nodoc:
125
+ set_primary_key "uuid"
126
+
127
+ def before_create
128
+ self.uuid = "295b3430-85b8-012c-cfe4-002332cf7d5e"
129
+ end
130
+
131
+ is_paranoid
132
+ end
data/spec/schema.rb CHANGED
@@ -7,6 +7,34 @@ ActiveRecord::Schema.define(:version => 20090317164830) do
7
7
  t.datetime "updated_at"
8
8
  end
9
9
 
10
+ create_table "dents", :force => true do |t|
11
+ t.integer "android_id"
12
+ t.string "description"
13
+ t.datetime "deleted_at"
14
+ end
15
+
16
+ create_table "dings", :force => true do |t|
17
+ t.integer "dent_id"
18
+ t.string "description"
19
+ t.boolean "not_deleted"
20
+ end
21
+
22
+ create_table "scratches", :force => true do |t|
23
+ t.integer "dent_id"
24
+ t.string "description"
25
+ t.datetime "deleted_at"
26
+ end
27
+
28
+ create_table "androids_places", :force => true, :id => false do |t|
29
+ t.integer "android_id"
30
+ t.integer "place_id"
31
+ end
32
+
33
+ create_table "places", :force => true do |t|
34
+ t.string "name"
35
+ t.datetime "deleted_at"
36
+ end
37
+
10
38
  create_table "people", :force => true do |t|
11
39
  t.string "name"
12
40
  t.datetime "created_at"
@@ -48,4 +76,11 @@ ActiveRecord::Schema.define(:version => 20090317164830) do
48
76
  t.string "name"
49
77
  t.boolean "alive", :default => true
50
78
  end
79
+
80
+ create_table "uuids", :id => false, :force => true do |t|
81
+ t.string "uuid", :limit => 36, :primary => true
82
+ t.string "name"
83
+ t.datetime "deleted_at"
84
+ end
85
+
51
86
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'rubygems'
2
2
  require "#{File.dirname(__FILE__)}/../lib/is_paranoid"
3
- require 'active_record'
3
+ require 'activerecord'
4
4
  require 'yaml'
5
5
  require 'spec'
6
6
 
@@ -11,4 +11,4 @@ end
11
11
 
12
12
  # Open ActiveRecord connection
13
13
  connect('test')
14
- load(File.dirname(__FILE__) + "/schema.rb")
14
+ load(File.dirname(__FILE__) + "/schema.rb")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: goldstar-is_paranoid
3
3
  version: !ruby/object:Gem::Version
4
- hash: 5
4
+ hash: 29
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 0
9
+ - 6
10
10
  - 1
11
- version: 0.9.0.1
11
+ version: 0.9.6.1
12
12
  platform: ruby
13
13
  authors:
14
14
  - Jeffrey Chupp
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-11-23 00:00:00 -05:00
19
+ date: 2010-11-04 00:00:00 -04:00
20
20
  default_executable:
21
21
  dependencies: []
22
22
 
@@ -29,6 +29,7 @@ extensions: []
29
29
  extra_rdoc_files:
30
30
  - README.textile
31
31
  files:
32
+ - .gitignore
32
33
  - CHANGELOG
33
34
  - MIT-LICENSE
34
35
  - README.textile
@@ -48,8 +49,8 @@ homepage: http://github.com/jchupp/is_paranoid/
48
49
  licenses: []
49
50
 
50
51
  post_install_message:
51
- rdoc_options: []
52
-
52
+ rdoc_options:
53
+ - --charset=UTF-8
53
54
  require_paths:
54
55
  - lib
55
56
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -76,7 +77,7 @@ rubyforge_project:
76
77
  rubygems_version: 1.3.7
77
78
  signing_key:
78
79
  specification_version: 3
79
- summary: ActiveRecord 2.3 compatible gem "allowing you to hide and restore records without actually deleting them." Yes, like acts_as_paranoid, only with less code and less complexity. This particular Goldstar version is patched to not emit warnings with Rails 2.3.10.
80
+ summary: ActiveRecord 2.3 compatible gem "allowing you to hide and restore records without actually deleting them." Yes, like acts_as_paranoid, only with less code and less complexity.
80
81
  test_files:
81
82
  - spec/is_paranoid_spec.rb
82
83
  - spec/models.rb