mguymon-cache-money 0.2.12

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,186 @@
1
+ require "spec_helper"
2
+ require 'ruby-debug'
3
+
4
+ module Cash
5
+ describe Accessor do
6
+ describe '#fetch' do
7
+ describe '#fetch("...")' do
8
+ describe 'when there is a cache miss' do
9
+ it 'returns nil' do
10
+ Story.fetch("yabba").should be_nil
11
+ end
12
+ end
13
+
14
+ describe 'when there is a cache hit' do
15
+ it 'returns the value of the cache' do
16
+ Story.set("yabba", "dabba")
17
+ Story.fetch("yabba").should == "dabba"
18
+ end
19
+ end
20
+ end
21
+
22
+ describe '#fetch([...])', :shared => true do
23
+ describe '#fetch([])' do
24
+ it 'returns the empty hash' do
25
+ Story.fetch([]).should == {}
26
+ end
27
+ end
28
+
29
+ describe 'when there is a total cache miss' do
30
+ it 'yields the keys to the block' do
31
+ Story.fetch(["yabba", "dabba"]) { |*missing_ids| ["doo", "doo"] }.should == {
32
+ "Story:1/yabba" => "doo",
33
+ "Story:1/dabba" => "doo"
34
+ }
35
+ end
36
+ end
37
+
38
+ describe 'when there is a partial cache miss' do
39
+ it 'yields just the missing ids to the block' do
40
+ Story.set("yabba", "dabba")
41
+ Story.fetch(["yabba", "dabba"]) { |*missing_ids| "doo" }.should == {
42
+ "Story:1/yabba" => "dabba",
43
+ "Story:1/dabba" => "doo"
44
+ }
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ describe '#get' do
51
+ describe '#get("...")' do
52
+ describe 'when there is a cache miss' do
53
+ it 'returns the value of the block' do
54
+ Story.get("yabba") { "dabba" }.should == "dabba"
55
+ end
56
+
57
+ it 'adds to the cache' do
58
+ Story.get("yabba") { "dabba" }
59
+ Story.get("yabba").should == "dabba"
60
+ end
61
+ end
62
+
63
+ describe 'when there is a cache hit' do
64
+ before do
65
+ Story.set("yabba", "dabba")
66
+ end
67
+
68
+ it 'returns the value of the cache' do
69
+ Story.get("yabba") { "doo" }.should == "dabba"
70
+ end
71
+
72
+ it 'does nothing to the cache' do
73
+ Story.get("yabba") { "doo" }
74
+ Story.get("yabba").should == "dabba"
75
+ end
76
+ end
77
+ end
78
+
79
+ describe '#get([...])' do
80
+ it_should_behave_like "#fetch([...])"
81
+ end
82
+ end
83
+
84
+ describe '#add' do
85
+ describe 'when the value already exists' do
86
+ describe 'when a block is given' do
87
+ it 'yields to the block' do
88
+ Story.set("count", 1)
89
+ Story.add("count", 1) { "yield me" }.should == "yield me"
90
+ end
91
+ end
92
+
93
+ describe 'when no block is given' do
94
+ it 'does not error' do
95
+ Story.set("count", 1)
96
+ lambda { Story.add("count", 1) }.should_not raise_error
97
+ end
98
+ end
99
+ end
100
+
101
+ describe 'when the value does not already exist' do
102
+ it 'adds the key to the cache' do
103
+ Story.add("count", 1)
104
+ Story.get("count").should == 1
105
+ end
106
+ end
107
+ end
108
+
109
+ describe '#set' do
110
+ end
111
+
112
+ describe '#incr' do
113
+ describe 'when there is a cache hit' do
114
+ before do
115
+ Story.set("count", 0, :raw => true)
116
+ end
117
+
118
+ it 'increments the value of the cache' do
119
+ Story.incr("count", 2)
120
+ Story.get("count", :raw => true).should =~ /2/
121
+ end
122
+
123
+ it 'returns the new cache value' do
124
+ Story.incr("count", 2).should == 2
125
+ end
126
+ end
127
+
128
+ describe 'when there is a cache miss' do
129
+ it 'initializes the value of the cache to the value of the block' do
130
+ Story.incr("count", 1) { 5 }
131
+ Story.get("count", :raw => true).should =~ /5/
132
+ end
133
+
134
+ it 'returns the new cache value' do
135
+ Story.incr("count", 1) { 2 }.should == 2
136
+ end
137
+ end
138
+ end
139
+
140
+ describe '#decr' do
141
+ describe 'when there is a cache hit' do
142
+ before do
143
+ Story.incr("count", 1) { 10 }
144
+ end
145
+
146
+ it 'decrements the value of the cache' do
147
+ Story.decr("count", 2)
148
+ Story.get("count", :raw => true).should =~ /8/
149
+ end
150
+
151
+ it 'returns the new cache value' do
152
+ Story.decr("count", 2).should == 8
153
+ end
154
+ end
155
+
156
+ describe 'when there is a cache miss' do
157
+ it 'initializes the value of the cache to the value of the block' do
158
+ Story.decr("count", 1) { 5 }
159
+ Story.get("count", :raw => true).should =~ /5/
160
+ end
161
+
162
+ it 'returns the new cache value' do
163
+ Story.decr("count", 1) { 2 }.should == 2
164
+ end
165
+ end
166
+ end
167
+
168
+ describe '#expire' do
169
+ it 'deletes the key' do
170
+ Story.set("bobo", 1)
171
+ Story.expire("bobo")
172
+ Story.get("bobo").should == nil
173
+ end
174
+ end
175
+
176
+ describe '#cache_key' do
177
+ it 'uses the version number' do
178
+ Story.version 1
179
+ Story.cache_key("foo").should == "Story:1/foo"
180
+
181
+ Story.version 2
182
+ Story.cache_key("foo").should == "Story:2/foo"
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,224 @@
1
+ require "spec_helper"
2
+
3
+ module Cash
4
+ describe Finders do
5
+ describe 'when the cache is populated' do
6
+ describe "#find" do
7
+ describe '#find(id...)' do
8
+ describe '#find(id)' do
9
+ it "returns an active record" do
10
+ story = Story.create!(:title => 'a story')
11
+ Story.find(story.id).should == story
12
+ end
13
+ end
14
+
15
+ describe 'when the object is destroyed' do
16
+ describe '#find(id)' do
17
+ it "raises an error" do
18
+ story = Story.create!(:title => "I am delicious")
19
+ story.destroy
20
+ lambda { Story.find(story.id) }.should raise_error(ActiveRecord::RecordNotFound)
21
+ end
22
+ end
23
+ end
24
+
25
+ describe '#find(id1, id2, ...)' do
26
+ it "returns an array" do
27
+ story1, story2 = Story.create!, Story.create!
28
+ Story.find(story1.id, story2.id).should == [story1, story2]
29
+ end
30
+
31
+ describe "#find(id, nil)" do
32
+ it "ignores the nils" do
33
+ story = Story.create!
34
+ Story.find(story.id, nil).should == story
35
+ end
36
+ end
37
+ end
38
+
39
+ describe 'when given nonexistent ids' do
40
+ describe 'when given one nonexistent id' do
41
+ it 'raises an error' do
42
+ lambda { Story.find(1) }.should raise_error(ActiveRecord::RecordNotFound)
43
+ end
44
+ end
45
+
46
+ describe 'when given multiple nonexistent ids' do
47
+ it "raises an error" do
48
+ lambda { Story.find(1, 2, 3) }.should raise_error(ActiveRecord::RecordNotFound)
49
+ end
50
+ end
51
+
52
+
53
+ describe '#find(nil)' do
54
+ it 'raises an error' do
55
+ lambda { Story.find(nil) }.should raise_error(ActiveRecord::RecordNotFound)
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#find(object)' do
62
+ it "coerces arguments to integers" do
63
+ story = Story.create!
64
+ Story.find(story.id.to_s).should == story
65
+ end
66
+ end
67
+
68
+ describe '#find([...])' do
69
+ describe 'when given an array with valid ids' do
70
+ it "#finds the object with that id" do
71
+ story = Story.create!
72
+ Story.find([story.id]).should == [story]
73
+ end
74
+ end
75
+
76
+ describe '#find([])' do
77
+ it 'returns the empty array' do
78
+ Story.find([]).should == []
79
+ end
80
+ end
81
+
82
+ describe 'when given nonexistent ids' do
83
+ it 'raises an error' do
84
+ lambda { Story.find([1, 2, 3]) }.should raise_error(ActiveRecord::RecordNotFound)
85
+ end
86
+ end
87
+
88
+ describe 'when given limits and offsets' do
89
+ describe '#find([1, 2, ...], :limit => ..., :offset => ...)' do
90
+ it "returns the correct slice of objects" do
91
+ character1 = Character.create!(:name => "Sam", :story_id => 1)
92
+ character2 = Character.create!(:name => "Sam", :story_id => 1)
93
+ character3 = Character.create!(:name => "Sam", :story_id => 1)
94
+ Character.find(
95
+ [character1.id, character2.id, character3.id],
96
+ :conditions => { :name => "Sam", :story_id => 1 }, :limit => 2
97
+ ).should == [character1, character2]
98
+ end
99
+ end
100
+
101
+ describe '#find([1], :limit => 0)' do
102
+ it "raises an error" do
103
+ character = Character.create!(:name => "Sam", :story_id => 1)
104
+ lambda do
105
+ Character.find([character.id], :conditions => { :name => "Sam", :story_id => 1 }, :limit => 0)
106
+ end.should raise_error(ActiveRecord::RecordNotFound)
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ describe '#find(:first, ...)' do
113
+ describe '#find(:first, ..., :offset => ...)' do
114
+ it "#finds the object in the correct order" do
115
+ story1 = Story.create!(:title => 'title1')
116
+ story2 = Story.create!(:title => story1.title)
117
+ Story.find(:first, :conditions => { :title => story1.title }, :offset => 1).should == story2
118
+ end
119
+ end
120
+
121
+ describe '#find(:first, :conditions => [])' do
122
+ it 'finds the object in the correct order' do
123
+ story = Story.create!
124
+ Story.find(:first, :conditions => []).should == story
125
+ end
126
+ end
127
+
128
+ describe "#find(:first, :conditions => '...')" do
129
+ it "coerces ruby values to the appropriate database values" do
130
+ story1 = Story.create! :title => 'a story', :published => true
131
+ story2 = Story.create! :title => 'another story', :published => false
132
+ Story.find(:first, :conditions => 'published = 0').should == story2
133
+ end
134
+ end
135
+ end
136
+ end
137
+
138
+ describe '#find_by_attr' do
139
+ describe '#find_by_attr(nil)' do
140
+ it 'returns nil' do
141
+ Story.find_by_id(nil).should == nil
142
+ end
143
+ end
144
+
145
+ describe 'when given non-existent ids' do
146
+ it 'returns nil' do
147
+ Story.find_by_id(-1).should == nil
148
+ end
149
+ end
150
+ end
151
+
152
+ describe '#find_all_by_attr' do
153
+ describe 'when given non-existent ids' do
154
+ it "does not raise an error" do
155
+ lambda { Story.find_all_by_id([-1, -2, -3]) }.should_not raise_error
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ describe 'when the cache is partially populated' do
162
+ describe '#find(:all, :conditions => ...)' do
163
+ it "returns the correct records" do
164
+ story1 = Story.create!(:title => title = 'once upon a time...')
165
+ $memcache.flush_all
166
+ story2 = Story.create!(:title => title)
167
+ Story.find(:all, :conditions => { :title => story1.title }).should == [story1, story2]
168
+ end
169
+ end
170
+
171
+ describe '#find(id1, id2, ...)' do
172
+ it "returns the correct records" do
173
+ story1 = Story.create!(:title => 'story 1')
174
+ $memcache.flush_all
175
+ story2 = Story.create!(:title => 'story 2')
176
+ Story.find(story1.id, story2.id).should == [story1, story2]
177
+ end
178
+ end
179
+ end
180
+
181
+ describe 'when the cache is not populated' do
182
+ describe '#find(id)' do
183
+ it "returns the correct records" do
184
+ story = Story.create!(:title => 'a story')
185
+ $memcache.flush_all
186
+ Story.find(story.id).should == story
187
+ end
188
+
189
+ it "handles after_find on model" do
190
+ class AfterFindStory < Story
191
+ def after_find
192
+ self.title
193
+ end
194
+ end
195
+ lambda do
196
+ AfterFindStory.create!(:title => 'a story')
197
+ end.should_not raise_error(ActiveRecord::MissingAttributeError)
198
+ end
199
+ end
200
+
201
+ describe '#find(id1, id2, ...)' do
202
+ it "handles finds with multiple ids correctly" do
203
+ story1 = Story.create!
204
+ story2 = Story.create!
205
+ $memcache.flush_all
206
+ Story.find(story1.id, story2.id).should == [story1, story2]
207
+ end
208
+ end
209
+ end
210
+
211
+ describe 'loading' do
212
+ it "should be able to create a record for an ar subclass that was loaded before cache money" do
213
+ $debug = true
214
+ session = ActiveRecord::SessionStore::Session.new
215
+ session.session_id = "12345"
216
+ session.data = "foobarbaz"
217
+
218
+ lambda {
219
+ session.save!
220
+ }.should_not raise_error
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,67 @@
1
+ require "spec_helper"
2
+
3
+ module Cash
4
+ describe Finders do
5
+ describe 'Calculations' do
6
+ describe 'when the cache is populated' do
7
+ before do
8
+ @stories = [Story.create!(:title => @title = 'asdf'), Story.create!(:title => @title)]
9
+ end
10
+
11
+ describe '#count(:all, :conditions => ...)' do
12
+ it "does not use the database" do
13
+ Story.count(:all, :conditions => { :title => @title }).should == @stories.size
14
+ end
15
+ end
16
+
17
+ describe '#count(:column, :conditions => ...)' do
18
+ it "uses the database, not the cache" do
19
+ mock(Story).get.never
20
+ Story.count(:title, :conditions => { :title => @title }).should == @stories.size
21
+ end
22
+ end
23
+
24
+ describe '#count(:all, :distinct => ..., :select => ...)' do
25
+ it 'uses the database, not the cache' do
26
+ mock(Story).get.never
27
+ Story.count(:all, :distinct => true, :select => :title, :conditions => { :title => @title }).should == @stories.collect(&:title).uniq.size
28
+ end
29
+ end
30
+
31
+ describe 'association proxies' do
32
+ describe '#count(:all, :conditions => ...)' do
33
+ it 'does not use the database' do
34
+ story = Story.create!
35
+ characters = [story.characters.create!(:name => name = 'name'), story.characters.create!(:name => name)]
36
+ mock(Story.connection).execute.never
37
+ story.characters.count(:all, :conditions => { :name => name }).should == characters.size
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ describe 'when the cache is not populated' do
44
+ describe '#count(:all, ...)' do
45
+ describe '#count(:all)' do
46
+ it 'uses the database, not the cache' do
47
+ mock(Story).get.never
48
+ Story.count
49
+ end
50
+ end
51
+
52
+ describe '#count(:all, :conditions => ...)' do
53
+ before do
54
+ Story.create!(:title => @title = 'title')
55
+ $memcache.flush_all
56
+ end
57
+
58
+ it "populates the count correctly" do
59
+ Story.count(:all, :conditions => { :title => @title }).should == 1
60
+ Story.fetch("title/#{@title}/count", :raw => true).should =~ /\s*1\s*/
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end