paranoid_create 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
data/spec/models.rb ADDED
@@ -0,0 +1,159 @@
1
+ class Person < ActiveRecord::Base #:nodoc:
2
+ validates_uniqueness_of :name
3
+ has_many :androids, :foreign_key => :owner_id, :dependent => :destroy
4
+ end
5
+
6
+ class Android < ActiveRecord::Base #:nodoc:
7
+ paranoid
8
+ validates_uniqueness_of :name
9
+ has_many :components, :dependent => :destroy
10
+ has_one :sticker
11
+ has_many :memories, :foreign_key => 'parent_id'
12
+ has_many :dents
13
+ has_many :dings, :through => :dents
14
+ has_many :scratches, :through => :dents
15
+ has_and_belongs_to_many :places
16
+
17
+ # this code is to ensure that our destroy and restore methods
18
+ # work without triggering before/after_update callbacks
19
+ before_update :raise_hell
20
+ def raise_hell
21
+ raise "hell"
22
+ end
23
+ end
24
+
25
+ class Biped < Android
26
+ end
27
+
28
+ class Dent < ActiveRecord::Base #:nodoc:
29
+ paranoid
30
+ belongs_to :android
31
+ has_many :dings
32
+ has_many :scratches
33
+ end
34
+
35
+ class Ding < ActiveRecord::Base #:nodoc:
36
+ paranoid :field => :not_deleted, :destroyed_value => true, :not_destroyed_value => false
37
+ belongs_to :dent
38
+ end
39
+
40
+ class Scratch < ActiveRecord::Base #:nodoc:
41
+ paranoid
42
+ belongs_to :dent
43
+ end
44
+
45
+ class Component < ActiveRecord::Base #:nodoc:
46
+ paranoid
47
+ belongs_to :android, :dependent => :destroy
48
+ has_many :sub_components, :dependent => :destroy
49
+ NEW_NAME = 'Something Else!'
50
+
51
+ after_destroy :change_name
52
+ def change_name
53
+ self.update_attribute(:name, NEW_NAME)
54
+ end
55
+ end
56
+
57
+ class SubComponent < ActiveRecord::Base #:nodoc:
58
+ paranoid
59
+ belongs_to :component, :dependent => :destroy
60
+ end
61
+
62
+ class Memory < ActiveRecord::Base #:nodoc:
63
+ paranoid
64
+ belongs_to :android, :class_name => "Android", :foreign_key => "parent_id"
65
+ end
66
+
67
+ class Sticker < ActiveRecord::Base #:nodoc:
68
+ MM_NAME = "You've got method_missing"
69
+
70
+ # this simply serves to ensure that we don't break method_missing
71
+ # if it is implemented on a class and called before is_paranoid
72
+ def method_missing name, *args, &block
73
+ self.name = MM_NAME
74
+ end
75
+
76
+ paranoid
77
+ belongs_to :android
78
+ end
79
+
80
+ class AndroidWithScopedUniqueness < ActiveRecord::Base #:nodoc:
81
+ set_table_name :androids
82
+ validates_uniqueness_of :name, :scope => :deleted_at
83
+ paranoid
84
+ end
85
+
86
+ class Place < ActiveRecord::Base #:nodoc:
87
+ paranoid
88
+ has_and_belongs_to_many :androids
89
+
90
+ # this code is to ensure that our destroy and restore methods
91
+ # work without triggering before/after_update callbacks
92
+ before_update :raise_hell
93
+ def raise_hell
94
+ raise "hell"
95
+ end
96
+ end
97
+
98
+ class AndroidsPlaces < ActiveRecord::Base #:nodoc:
99
+ end
100
+
101
+ class Ninja < ActiveRecord::Base #:nodoc:
102
+ validates_uniqueness_of :name, :scope => :visible
103
+ paranoid :field => [:visible, false, true]
104
+
105
+ alias_method :vanish, :destroy
106
+
107
+ # this code is to ensure that our destroy and restore methods
108
+ # work without triggering before/after_update callbacks
109
+ before_update :raise_hell
110
+ def raise_hell
111
+ raise "hell"
112
+ end
113
+ end
114
+
115
+ class Pirate < ActiveRecord::Base #:nodoc:
116
+ paranoid :field => :alive, :destroyed_value => false, :not_destroyed_value => true
117
+
118
+ # this code is to ensure that our destroy and restore methods
119
+ # work without triggering before/after_update callbacks
120
+ before_update :raise_hell
121
+ def raise_hell
122
+ raise "hell"
123
+ end
124
+ end
125
+
126
+ class DeadPirate < ActiveRecord::Base #:nodoc:
127
+ set_table_name :pirates
128
+ paranoid :field => :alive, :destroyed_value => true, :not_destroyed_value => false
129
+ end
130
+
131
+ class RandomPirate < ActiveRecord::Base #:nodoc:
132
+ set_table_name :pirates
133
+
134
+ after_destroy :raise_an_error
135
+ def raise_an_error
136
+ raise 'after_destroy works'
137
+ end
138
+ end
139
+
140
+ class UndestroyablePirate < ActiveRecord::Base #:nodoc:
141
+ set_table_name :pirates
142
+ paranoid :field => :alive, :destroyed_value => false, :not_destroyed_value => true
143
+
144
+ before_destroy :ret_false
145
+ def ret_false
146
+ false
147
+ end
148
+ end
149
+
150
+ class Uuid < ActiveRecord::Base #:nodoc:
151
+ set_primary_key "uuid"
152
+
153
+ before_create :set_uuid
154
+ def set_uuid
155
+ self.uuid = "295b3430-85b8-012c-cfe4-002332cf7d5e"
156
+ end
157
+
158
+ paranoid
159
+ end
@@ -0,0 +1,298 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/models')
3
+
4
+ LUKE = 'Luke Skywalker'
5
+
6
+ describe Paranoid do
7
+ before(:each) do
8
+ # Sticker.delete_all
9
+ # Place.delete_all
10
+ # Android.delete_all
11
+ # Person.delete_all
12
+ # Component.delete_all
13
+ #
14
+ # @luke = Person.create(:name => LUKE)
15
+ # @r2d2 = Android.create(:name => 'R2D2', :owner_id => @luke.id)
16
+ # @c3p0 = Android.create(:name => 'C3P0', :owner_id => @luke.id)
17
+ #
18
+ # @r2d2.components.create(:name => 'Rotors')
19
+ #
20
+ # @r2d2.memories.create(:name => 'A pretty sunset')
21
+ # @c3p0.sticker = Sticker.create(:name => 'OMG, PONIES!')
22
+ # @tatooine = Place.create(:name => "Tatooine")
23
+ # @r2d2.places << @tatooine
24
+ end
25
+
26
+ describe 'basic functionality' do
27
+ before(:each) do
28
+ Place.delete_all
29
+ @tatooine, @mos_eisley, @coruscant = Place.create([{:name => "Tatooine"}, {:name => 'Mos Eisley'}, {:name => 'Coruscant'}])
30
+ end
31
+
32
+ it 'should recognize a class as paranoid' do
33
+ Person.paranoid?.should be_false
34
+ Place.paranoid?.should be_true
35
+ end
36
+
37
+ it 'should recognize an STI class as paranoid' do
38
+ Biped.paranoid?.should be_true
39
+ end
40
+
41
+ it 'should hide destroyed records' do
42
+ @tatooine.destroy
43
+ Place.first(:conditions => {:name => 'Tatooine'}).should be_nil
44
+ end
45
+
46
+ it 'should reveal destroyed records when with_destroyed' do
47
+ @tatooine.destroy
48
+ Place.with_destroyed.first(:conditions => {:name => 'Tatooine'}).should_not be_nil
49
+ end
50
+
51
+ it 'should restore the destroyed record' do
52
+ @tatooine.destroy
53
+
54
+ @tatooine = Place.with_destroyed.first(:conditions => {:name => 'Tatooine'})
55
+ @tatooine.restore
56
+
57
+ Place.first(:conditions => {:name => 'Tatooine'}).should_not be_nil
58
+ end
59
+
60
+ it 'should soft delete paranoid records' do
61
+ @tatooine.destroy
62
+
63
+ record = Place.with_destroyed.first(:conditions => {:name => 'Tatooine'})
64
+ record.should_not be_nil
65
+ record.deleted_at.should_not be_nil
66
+ end
67
+
68
+ it 'should mark the record destroyed' do
69
+ @tatooine.destroy
70
+ @tatooine.destroyed?.should be_true
71
+ end
72
+
73
+ it 'should set the deleted_field' do
74
+ @tatooine.destroy
75
+ @tatooine.deleted_at.should_not be_nil
76
+ end
77
+
78
+ it 'should show deleted_only' do
79
+ @tatooine.destroy
80
+ destroyed = Place.with_destroyed_only.all
81
+ destroyed.size.should == 1
82
+ destroyed[0].should == @tatooine
83
+ end
84
+
85
+ it 'should properly count records' do
86
+ Place.count.should == 3
87
+
88
+ @tatooine.destroy
89
+ Place.count.should == 2
90
+ Place.with_destroyed.count.should == 3
91
+ Place.with_destroyed_only.count.should == 1
92
+ end
93
+ end
94
+
95
+ describe 'for alternate field information' do
96
+ before(:each) do
97
+ Pirate.delete_all
98
+ @roberts, @jack, @hook = Pirate.create([{:name => 'Roberts'}, {:name => 'Jack'}, {:name => 'Hook'}])
99
+ end
100
+
101
+ it 'should have 3 alive pirates' do
102
+ Pirate.all.size.should == 3
103
+ end
104
+
105
+ it 'should kill the pirate' do
106
+ @roberts.destroy
107
+
108
+ record = Pirate.first(:conditions => {:name => 'Roberts'})
109
+ record.should be_nil
110
+ end
111
+
112
+ it 'should not remove the pirate record' do
113
+ @roberts.destroy
114
+
115
+ record = Pirate.with_destroyed.first(:conditions => {:name => 'Roberts'})
116
+ record.should_not be_nil
117
+ record.alive.should be_false
118
+ end
119
+
120
+ it 'should mark the pirate dead' do
121
+ @roberts.destroy
122
+ @roberts.destroyed?.should be_true
123
+ end
124
+
125
+ it 'should set alive to false' do
126
+ @roberts.destroy
127
+ @roberts.alive.should be_false
128
+ end
129
+
130
+ it 'should show deleted_only' do
131
+ @roberts.destroy
132
+ destroyed = Pirate.with_destroyed_only.all
133
+ destroyed.size.should == 1
134
+ destroyed[0].should == @roberts
135
+ end
136
+
137
+ it 'should properly count records' do
138
+ Pirate.count.should == 3
139
+
140
+ @roberts.destroy
141
+ Pirate.count.should == 2
142
+ Pirate.with_destroyed.count.should == 3
143
+ Pirate.with_destroyed_only.count.should == 1
144
+ end
145
+ end
146
+
147
+ describe 'for alternate field information with is_paranoid format field information' do
148
+ before(:each) do
149
+ Ninja.delete_all
150
+ @steve, @bob, @tim = Ninja.create([{:name => 'Steve', :visible => true}, {:name => 'Bob', :visible => true}, {:name => 'Tim', :visible => true}])
151
+ end
152
+
153
+ it 'should have 3 visible ninjas' do
154
+ Ninja.all.size.should == 3
155
+ end
156
+
157
+ it 'should vanish the ninja' do
158
+ @steve.destroy
159
+
160
+ record = Ninja.first(:conditions => {:name => 'Steve'})
161
+ record.should be_nil
162
+ end
163
+
164
+ it 'should not delete the ninja' do
165
+ @steve.destroy
166
+
167
+ record = Ninja.with_destroyed.first(:conditions => {:name => 'Steve'})
168
+ record.should_not be_nil
169
+ record.visible.should be_false
170
+ end
171
+
172
+ it 'should mark the ninja vanished' do
173
+ @steve.destroy
174
+ @steve.destroyed?.should be_true
175
+ end
176
+
177
+ it 'should set visible to false' do
178
+ @steve.destroy
179
+ @steve.visible.should be_false
180
+ end
181
+
182
+ it 'should show deleted_only' do
183
+ @steve.destroy
184
+ destroyed = Ninja.with_destroyed_only.all
185
+ destroyed.size.should == 1
186
+ destroyed[0].should == @steve
187
+ end
188
+
189
+ it 'should properly count records' do
190
+ Ninja.count.should == 3
191
+
192
+ @steve.destroy
193
+ Ninja.count.should == 2
194
+ Ninja.with_destroyed.count.should == 3
195
+ Ninja.with_destroyed_only.count.should == 1
196
+ end
197
+ end
198
+
199
+ describe 'has_many association' do
200
+ before(:each) do
201
+ Android.delete_all
202
+ Dent.delete_all
203
+
204
+ @r2d2 = Android.create(:name => 'R2D2')
205
+ @dents = Dent.create([
206
+ {:android => @r2d2, :description => 'Hit by debris'},
207
+ {:android => @r2d2, :description => 'Dropped while loading into X-Wing'},
208
+ {:android => @r2d2, :description => 'Blaster hit'}
209
+ ])
210
+
211
+ @dents[2].destroy
212
+ end
213
+
214
+ it 'should hide the soft deleted dent' do
215
+ @r2d2.dents.to_a.should == @dents[0,2]
216
+ end
217
+
218
+ it 'should show all dents with_destroyed' do
219
+ @r2d2.dents.with_destroyed.to_a.should == @dents
220
+ end
221
+
222
+ it 'should show only soft deleted with destroyed_only' do
223
+ @r2d2.dents.with_destroyed_only.to_a.should == [@dents[2]]
224
+ end
225
+
226
+ it 'should show the correct counts' do
227
+ @r2d2.dents.size.should == 2
228
+ @r2d2.dents.with_destroyed.size.should == 3
229
+ @r2d2.dents.with_destroyed_only.size.should == 1
230
+ end
231
+
232
+ it 'should load correctly through an eager load' do
233
+ @r2d2 = Android.eager_load(:dents).first
234
+ @r2d2.dents.loaded?.should be_true
235
+ @r2d2.dents.size.should == 2
236
+ @r2d2.dents.to_a.should == @dents[0,2]
237
+ end
238
+
239
+ it 'should load correctly through an include' do
240
+ @r2d2 = Android.includes(:dents).first
241
+ @r2d2.dents.loaded?.should be_true
242
+ @r2d2.dents.size.should == 2
243
+ @r2d2.dents.to_a.should == @dents[0,2]
244
+ end
245
+
246
+ it 'should work correctly for a include dependency' do
247
+ @nil = Android.includes(:dents).where('dents.description' => 'Blaster hit').first
248
+ @nil.should be_nil
249
+ end
250
+ end
251
+
252
+ describe 'callbacks' do
253
+ before(:each) do
254
+ Pirate.delete_all
255
+ end
256
+
257
+ it 'should not destroy the record when before_destroy returns false' do
258
+ pirate = UndestroyablePirate.create!(:name => 'Roberts')
259
+ lambda { pirate.destroy }.should_not change(UndestroyablePirate, :count)
260
+ end
261
+
262
+ it 'should run after_destroy callbacks' do
263
+ pirate = RandomPirate.create!(:name => 'Roberts')
264
+ lambda { pirate.destroy }.should raise_error(/after_destroy works/)
265
+ end
266
+
267
+ it 'should rollback on after_destroy error' do
268
+ pirate = RandomPirate.create!(:name => 'Roberts')
269
+ lambda { pirate.destroy rescue nil }.should_not change(RandomPirate, :count)
270
+ end
271
+ end
272
+
273
+ describe 'association destroy' do
274
+ before(:each) do
275
+ Android.delete_all
276
+ Component.delete_all
277
+ @r2d2 = Android.create(:name => 'R2D2')
278
+ @components = Component.create([{ :android => @r2d2, :name => 'Rotors' }, { :android => @r2d2, :name => 'Wheels' }])
279
+ end
280
+
281
+ it 'should delete associated models' do
282
+ Android.count.should == 1
283
+ @r2d2.components.count.should == 2
284
+ @r2d2.destroy
285
+ Android.count.should == 0
286
+ @r2d2.components.count.should == 0
287
+ end
288
+
289
+ it 'should restore associated models' do
290
+ @r2d2.destroy
291
+ Android.count.should == 0
292
+ @r2d2.components.count.should == 0
293
+ Android.with_destroyed_only.first.restore
294
+ Android.count.should == 1
295
+ @r2d2.components.count.should == 2
296
+ end
297
+ end
298
+ end
data/spec/schema.rb ADDED
@@ -0,0 +1,86 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table "androids", :force => true do |t|
3
+ t.string "name"
4
+ t.integer "owner_id"
5
+ t.datetime "deleted_at"
6
+ t.datetime "created_at"
7
+ t.datetime "updated_at"
8
+ end
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
+
38
+ create_table "people", :force => true do |t|
39
+ t.string "name"
40
+ t.datetime "created_at"
41
+ t.datetime "updated_at"
42
+ end
43
+
44
+ create_table "components", :force => true do |t|
45
+ t.string "name"
46
+ t.integer "android_id"
47
+ t.datetime "deleted_at"
48
+ t.datetime "created_at"
49
+ t.datetime "updated_at"
50
+ end
51
+
52
+ create_table "sub_components", :force => true do |t|
53
+ t.string "name"
54
+ t.integer "component_id"
55
+ t.datetime "deleted_at"
56
+ end
57
+
58
+ create_table "memories", :force => true do |t|
59
+ t.string "name"
60
+ t.integer "parent_id"
61
+ t.datetime "deleted_at"
62
+ end
63
+
64
+ create_table "stickers", :force => true do |t|
65
+ t.string "name"
66
+ t.integer "android_id"
67
+ t.datetime "deleted_at"
68
+ end
69
+
70
+ create_table "ninjas", :force => true do |t|
71
+ t.string "name"
72
+ t.boolean "visible", :default => false
73
+ end
74
+
75
+ create_table "pirates", :force => true do |t|
76
+ t.string "name"
77
+ t.boolean "alive", :default => true
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
+
86
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,27 @@
1
+ require 'paranoid'
2
+
3
+ $enabled = false
4
+
5
+ ActiveSupport::Notifications.subscribe(/sql/i) do |*args|
6
+ if $enabled
7
+ event = ActiveSupport::Notifications::Event.new(*args)
8
+ puts "SQL #{event.duration}: #{event.payload[:sql]}"
9
+ end
10
+ end
11
+
12
+ def connect(environment)
13
+ conf = YAML::load(File.open(File.dirname(__FILE__) + '/database.yml'))
14
+ ActiveRecord::Base.establish_connection(conf[environment])
15
+ end
16
+
17
+ # Open ActiveRecord connection
18
+ connect('test')
19
+
20
+ original_stdout = $stdout
21
+ $stdout = StringIO.new
22
+
23
+ begin
24
+ load(File.dirname(__FILE__) + "/schema.rb")
25
+ ensure
26
+ $stdout = original_stdout
27
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: paranoid_create
3
+ version: !ruby/object:Gem::Version
4
+ hash: 11
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 10
10
+ version: 0.0.10
11
+ platform: ruby
12
+ authors:
13
+ - Philipp Ullmann
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-01 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 27
30
+ segments:
31
+ - 2
32
+ - 5
33
+ - 0
34
+ version: 2.5.0
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: sqlite3-ruby
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 29
46
+ segments:
47
+ - 1
48
+ - 3
49
+ - 3
50
+ version: 1.3.3
51
+ type: :development
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: activerecord
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 13
62
+ segments:
63
+ - 3
64
+ - 0
65
+ - 5
66
+ version: 3.0.5
67
+ type: :development
68
+ version_requirements: *id003
69
+ description:
70
+ email: philipp.ullmann@create.at
71
+ executables: []
72
+
73
+ extensions: []
74
+
75
+ extra_rdoc_files: []
76
+
77
+ files:
78
+ - .gitignore
79
+ - Gemfile
80
+ - MIT-LICENSE
81
+ - README.textile
82
+ - Rakefile
83
+ - init.rb
84
+ - lib/paranoid.rb
85
+ - lib/paranoid/base.rb
86
+ - lib/paranoid/join_association.rb
87
+ - lib/paranoid/paranoid_methods.rb
88
+ - lib/paranoid/relation.rb
89
+ - lib/paranoid/version.rb
90
+ - paranoid.gemspec
91
+ - rdoc/template.rb
92
+ - spec/database.yml
93
+ - spec/models.rb
94
+ - spec/paranoid_spec.rb
95
+ - spec/schema.rb
96
+ - spec/spec.opts
97
+ - spec/spec_helper.rb
98
+ has_rdoc: true
99
+ homepage: http://github.com/create-philipp-ullmann/paranoid/
100
+ licenses: []
101
+
102
+ post_install_message:
103
+ rdoc_options: []
104
+
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ hash: 3
113
+ segments:
114
+ - 0
115
+ version: "0"
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ hash: 3
122
+ segments:
123
+ - 0
124
+ version: "0"
125
+ requirements: []
126
+
127
+ rubyforge_project: paranoid_create
128
+ rubygems_version: 1.3.7
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: Enable soft delete of ActiveRecord records. Based off defunct ActsAsParanoid and IsParanoid
132
+ test_files:
133
+ - spec/database.yml
134
+ - spec/models.rb
135
+ - spec/paranoid_spec.rb
136
+ - spec/schema.rb
137
+ - spec/spec.opts
138
+ - spec/spec_helper.rb