immortal 1.0.4 → 1.0.5
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/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:
|