immortal 1.0.4 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -0
- data/immortal.gemspec +1 -1
- data/lib/immortal/singular_association.rb +126 -12
- data/spec/immortal_spec.rb +48 -4
- metadata +8 -8
data/README.md
CHANGED
@@ -41,6 +41,7 @@ If you want to improve immortal
|
|
41
41
|
|
42
42
|
## CHANGELOG
|
43
43
|
|
44
|
+
- 1.0.5 Use separate internal accessors for with/only_deleted singular association readers
|
44
45
|
- 1.0.4 Extract with_deleted singular assoc readers to separate module
|
45
46
|
- 1.0.3 Added back feature where using immortal finders doesn't unscope association scopes.
|
46
47
|
- 1.0.2 Added with/only_deleted singular association readers (see specs)
|
data/immortal.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "immortal"
|
6
|
-
s.version = '1.0.
|
6
|
+
s.version = '1.0.5'
|
7
7
|
s.authors = ["Jordi Romero", "Saimon Moore"]
|
8
8
|
s.email = ["jordi@jrom.net", "saimon@saimonmoore.net"]
|
9
9
|
s.homepage = "http://github.com/teambox/immortal"
|
@@ -1,33 +1,147 @@
|
|
1
1
|
module Immortal
|
2
2
|
module SingularAssociation
|
3
|
+
attr_reader :with_deleted_target, :only_deleted_target
|
3
4
|
|
4
5
|
def with_deleted_reader(force_reload = false)
|
5
|
-
|
6
|
+
reader_with_deleted(force_reload)
|
6
7
|
end
|
7
8
|
|
8
9
|
def only_deleted_reader(force_reload = false)
|
9
|
-
|
10
|
+
reader_only_deleted(force_reload)
|
10
11
|
end
|
11
12
|
|
12
13
|
private
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
def reset_with_deleted
|
16
|
+
@with_deleted_loaded = false
|
17
|
+
ActiveRecord::IdentityMap.remove(with_deleted_target) if ActiveRecord::IdentityMap.enabled? && with_deleted_target
|
18
|
+
@with_deleted_target = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset_only_deleted
|
22
|
+
@only_deleted_loaded = false
|
23
|
+
ActiveRecord::IdentityMap.remove(only_deleted_target) if ActiveRecord::IdentityMap.enabled? && only_deleted_target
|
24
|
+
@only_deleted_target = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def with_deleted_loaded!
|
28
|
+
@with_deleted_loaded = true
|
29
|
+
@with_deleted_stale_state = stale_state
|
30
|
+
end
|
31
|
+
|
32
|
+
def only_deleted_loaded!
|
33
|
+
@only_deleted_loaded = true
|
34
|
+
@only_deleted_stale_state = stale_state
|
35
|
+
end
|
36
|
+
|
37
|
+
def with_deleted_loaded?
|
38
|
+
@with_deleted_loaded
|
39
|
+
end
|
40
|
+
|
41
|
+
def only_deleted_loaded?
|
42
|
+
@only_deleted_loaded
|
43
|
+
end
|
44
|
+
|
45
|
+
def stale_with_deleted_target?
|
46
|
+
with_deleted_loaded? && @with_deleted_stale_state != stale_state
|
47
|
+
end
|
48
|
+
|
49
|
+
def stale_only_deleted_target?
|
50
|
+
only_deleted_loaded? && @only_deleted_stale_state != stale_state
|
51
|
+
end
|
52
|
+
|
53
|
+
def reload_only_deleted
|
54
|
+
reset_only_deleted
|
55
|
+
reset_scope
|
56
|
+
load_only_deleted_target
|
57
|
+
self unless only_deleted_target.nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
def reload_with_deleted
|
61
|
+
reset_with_deleted
|
62
|
+
reset_scope
|
63
|
+
load_with_deleted_target
|
64
|
+
self unless with_deleted_target.nil?
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_with_deleted_target?
|
68
|
+
!with_deleted_loaded? && (!owner.new_record? || foreign_key_present?) && klass
|
69
|
+
end
|
70
|
+
|
71
|
+
def find_only_deleted_target?
|
72
|
+
!only_deleted_loaded? && (!owner.new_record? || foreign_key_present?) && klass
|
73
|
+
end
|
74
|
+
|
75
|
+
def load_with_deleted_target
|
76
|
+
if find_with_deleted_target?
|
77
|
+
begin
|
78
|
+
if ActiveRecord::IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class)
|
79
|
+
@with_deleted_target = ActiveRecord::IdentityMap.get(association_class, owner[reflection.foreign_key])
|
80
|
+
end
|
81
|
+
rescue NameError
|
82
|
+
nil
|
83
|
+
ensure
|
84
|
+
@with_deleted_target ||= find_with_deleted_target
|
17
85
|
end
|
18
86
|
end
|
87
|
+
with_deleted_loaded! unless with_deleted_loaded?
|
88
|
+
with_deleted_target
|
89
|
+
rescue ActiveRecord::RecordNotFound
|
90
|
+
with_deleted_reset
|
91
|
+
end
|
19
92
|
|
20
|
-
|
21
|
-
|
22
|
-
|
93
|
+
def load_only_deleted_target
|
94
|
+
if find_only_deleted_target?
|
95
|
+
begin
|
96
|
+
if ActiveRecord::IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class)
|
97
|
+
@only_deleted_target = ActiveRecord::IdentityMap.get(association_class, owner[reflection.foreign_key])
|
98
|
+
end
|
99
|
+
rescue NameError
|
100
|
+
nil
|
101
|
+
ensure
|
102
|
+
@only_deleted_target ||= find_only_deleted_target
|
23
103
|
end
|
24
104
|
end
|
105
|
+
only_deleted_loaded! unless only_deleted_loaded?
|
106
|
+
only_deleted_target
|
107
|
+
rescue ActiveRecord::RecordNotFound
|
108
|
+
only_deleted_reset
|
109
|
+
end
|
25
110
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
111
|
+
def reader_with_deleted(force_reload = false)
|
112
|
+
if force_reload
|
113
|
+
klass.uncached { reload_with_deleted } if klass
|
114
|
+
elsif !with_deleted_loaded? || stale_with_deleted_target?
|
115
|
+
reload_with_deleted
|
116
|
+
end
|
117
|
+
|
118
|
+
with_deleted_target
|
119
|
+
end
|
120
|
+
|
121
|
+
def reader_only_deleted(force_reload = false)
|
122
|
+
|
123
|
+
if force_reload
|
124
|
+
klass.uncached { reload_only_deleted } if klass
|
125
|
+
elsif !only_deleted_loaded? || stale_only_deleted_target?
|
126
|
+
reload_only_deleted
|
127
|
+
end
|
128
|
+
|
129
|
+
only_deleted_target
|
130
|
+
end
|
131
|
+
|
132
|
+
def find_with_deleted_target
|
133
|
+
return nil unless klass
|
134
|
+
klass.unscoped do
|
135
|
+
scoped.first.tap { |record| set_inverse_instance(record) }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def find_only_deleted_target
|
140
|
+
return nil unless klass
|
141
|
+
klass.unscoped do
|
142
|
+
scoped.where(:deleted => true).first.tap { |record| set_inverse_instance(record) }
|
30
143
|
end
|
144
|
+
end
|
31
145
|
|
32
146
|
end
|
33
147
|
end
|
data/spec/immortal_spec.rb
CHANGED
@@ -234,12 +234,13 @@ describe Immortal do
|
|
234
234
|
#don't assign directly and destroy new target
|
235
235
|
target_2.destroy
|
236
236
|
|
237
|
-
#Respect what's expected
|
238
|
-
node.target.should be_nil
|
239
|
-
|
240
237
|
#Ask for deleted target (or not deleted). Will NOT cache
|
238
|
+
# Run this before default accessor to test scope has been reset.
|
241
239
|
node.target_with_deleted.should == target_2
|
242
240
|
|
241
|
+
#Respect what's expected
|
242
|
+
node.target.should be_nil
|
243
|
+
|
243
244
|
#Ask only for deleted target. Will NOT cache
|
244
245
|
node.target_only_deleted.should == target_2
|
245
246
|
|
@@ -260,7 +261,7 @@ describe Immortal do
|
|
260
261
|
node.target = target_1
|
261
262
|
node.target.should == target_1
|
262
263
|
|
263
|
-
#switch target
|
264
|
+
#switch target directly
|
264
265
|
node.target = target_2
|
265
266
|
|
266
267
|
node.target.should == target_2
|
@@ -279,6 +280,49 @@ describe Immortal do
|
|
279
280
|
node.target.should be_nil
|
280
281
|
end
|
281
282
|
|
283
|
+
it "deleted readers should respect staleness" do
|
284
|
+
#setup
|
285
|
+
node = ImmortalNode.create! :title => 'testing association 1'
|
286
|
+
target_1 = ImmortalSomeTarget.create! :title => 'target 1'
|
287
|
+
target_2 = ImmortalSomeOtherTarget.create! :title => 'target 2'
|
288
|
+
|
289
|
+
#confirm initial state
|
290
|
+
node.target.should be_nil
|
291
|
+
node.target_with_deleted.should be_nil
|
292
|
+
node.target_only_deleted.should be_nil
|
293
|
+
|
294
|
+
#load target & confirm
|
295
|
+
node.target = target_1
|
296
|
+
node.target.should == target_1
|
297
|
+
node.target_with_deleted.should == target_1
|
298
|
+
node.target_only_deleted.should be_nil
|
299
|
+
|
300
|
+
#switch target directly
|
301
|
+
node.target = target_2
|
302
|
+
|
303
|
+
node.target.should == target_2
|
304
|
+
node.target_with_deleted.should == target_2
|
305
|
+
|
306
|
+
#don't assign directly and destroy new target
|
307
|
+
target_2.destroy
|
308
|
+
|
309
|
+
#Respect what's expected
|
310
|
+
node.target(true).should be_nil
|
311
|
+
|
312
|
+
#Ask for deleted target (or not deleted).
|
313
|
+
node.target_with_deleted.should == target_2
|
314
|
+
node.target_only_deleted.should == target_2
|
315
|
+
|
316
|
+
#Confirm we haven't invaded the target namespace
|
317
|
+
node.target.should be_nil
|
318
|
+
|
319
|
+
node.target_id = target_1.id
|
320
|
+
node.target_type = target_1.class.name
|
321
|
+
node.target.should == target_1
|
322
|
+
node.target_with_deleted.should == target_1
|
323
|
+
node.target_only_deleted.should be_nil
|
324
|
+
end
|
325
|
+
|
282
326
|
it "should not unscope associations when using with_deleted scope" do
|
283
327
|
m1 = ImmortalModel.create! :title => 'previously created model'
|
284
328
|
n1 = ImmortalNode.create! :title => 'previously created association'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: immortal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2011-10-
|
13
|
+
date: 2011-10-18 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
17
|
-
requirement: &
|
17
|
+
requirement: &70196367376140 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: 3.1.1
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70196367376140
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: rspec
|
28
|
-
requirement: &
|
28
|
+
requirement: &70196367375660 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ~>
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: 2.6.0
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70196367375660
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: sqlite3
|
39
|
-
requirement: &
|
39
|
+
requirement: &70196367375280 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,7 +44,7 @@ dependencies:
|
|
44
44
|
version: '0'
|
45
45
|
type: :development
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *70196367375280
|
48
48
|
description: Typical paranoid gem built for Rails 3 and with the minimum code needed
|
49
49
|
to satisfy acts_as_paranoid's API
|
50
50
|
email:
|