sreeix-cache-money 0.2.24.1
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/LICENSE +201 -0
- data/README +210 -0
- data/README.markdown +210 -0
- data/TODO +17 -0
- data/UNSUPPORTED_FEATURES +13 -0
- data/config/environment.rb +16 -0
- data/config/memcached.yml +6 -0
- data/db/schema.rb +18 -0
- data/init.rb +1 -0
- data/lib/cache_money.rb +91 -0
- data/lib/cash/accessor.rb +83 -0
- data/lib/cash/buffered.rb +129 -0
- data/lib/cash/config.rb +75 -0
- data/lib/cash/fake.rb +83 -0
- data/lib/cash/finders.rb +38 -0
- data/lib/cash/index.rb +214 -0
- data/lib/cash/local.rb +76 -0
- data/lib/cash/lock.rb +63 -0
- data/lib/cash/mock.rb +154 -0
- data/lib/cash/query/abstract.rb +210 -0
- data/lib/cash/query/calculation.rb +45 -0
- data/lib/cash/query/primary_key.rb +50 -0
- data/lib/cash/query/select.rb +16 -0
- data/lib/cash/request.rb +3 -0
- data/lib/cash/transactional.rb +43 -0
- data/lib/cash/util/array.rb +9 -0
- data/lib/cash/util/marshal.rb +19 -0
- data/lib/cash/write_through.rb +69 -0
- data/lib/mem_cached_session_store.rb +49 -0
- data/lib/mem_cached_support_store.rb +135 -0
- data/lib/memcached_wrapper.rb +263 -0
- data/rails/init.rb +38 -0
- data/spec/cash/accessor_spec.rb +159 -0
- data/spec/cash/active_record_spec.rb +224 -0
- data/spec/cash/buffered_spec.rb +9 -0
- data/spec/cash/calculations_spec.rb +78 -0
- data/spec/cash/finders_spec.rb +430 -0
- data/spec/cash/local_buffer_spec.rb +9 -0
- data/spec/cash/local_spec.rb +9 -0
- data/spec/cash/lock_spec.rb +108 -0
- data/spec/cash/marshal_spec.rb +60 -0
- data/spec/cash/order_spec.rb +138 -0
- data/spec/cash/shared.rb +62 -0
- data/spec/cash/transactional_spec.rb +578 -0
- data/spec/cash/window_spec.rb +195 -0
- data/spec/cash/write_through_spec.rb +245 -0
- data/spec/memcached_wrapper_test.rb +209 -0
- data/spec/spec_helper.rb +60 -0
- metadata +151 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
describe Marshal do
|
4
|
+
describe '#load' do
|
5
|
+
before do
|
6
|
+
class Constant; end
|
7
|
+
@reference_to_constant = Constant
|
8
|
+
@object = @reference_to_constant.new
|
9
|
+
@marshaled_object = Marshal.dump(@object)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'when the constant is not yet loaded' do
|
13
|
+
it 'loads the constant' do
|
14
|
+
Object.send(:remove_const, :Constant)
|
15
|
+
stub(Marshal).constantize(@reference_to_constant.name) { Object.send(:const_set, :Constant, @reference_to_constant) }
|
16
|
+
Marshal.load(@marshaled_object).class.should == @object.class
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'loads the constant with the scope operator' do
|
20
|
+
module Foo; class Bar; end; end
|
21
|
+
|
22
|
+
reference_to_module = Foo
|
23
|
+
reference_to_constant = Foo::Bar
|
24
|
+
object = reference_to_constant.new
|
25
|
+
marshaled_object = Marshal.dump(object)
|
26
|
+
|
27
|
+
Foo.send(:remove_const, :Bar)
|
28
|
+
Object.send(:remove_const, :Foo)
|
29
|
+
stub(Marshal).constantize(reference_to_module.name) { Object.send(:const_set, :Foo, reference_to_module) }
|
30
|
+
stub(Marshal).constantize(reference_to_constant.name) { Foo.send(:const_set, :Bar, reference_to_constant) }
|
31
|
+
|
32
|
+
Marshal.load(marshaled_object).class.should == object.class
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'when the constant does not exist' do
|
37
|
+
it 'raises a LoadError' do
|
38
|
+
Object.send(:remove_const, :Constant)
|
39
|
+
stub(Marshal).constantize { raise NameError }
|
40
|
+
lambda { Marshal.load(@marshaled_object) }.should raise_error(NameError)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'when there are recursive constants to load' do
|
45
|
+
it 'loads all constants recursively' do
|
46
|
+
class Constant1; end
|
47
|
+
class Constant2; end
|
48
|
+
reference_to_constant1 = Constant1
|
49
|
+
reference_to_constant2 = Constant2
|
50
|
+
object = [reference_to_constant1.new, reference_to_constant2.new]
|
51
|
+
marshaled_object = Marshal.dump(object)
|
52
|
+
Object.send(:remove_const, :Constant1)
|
53
|
+
Object.send(:remove_const, :Constant2)
|
54
|
+
stub(Marshal).constantize(reference_to_constant1.name) { Object.send(:const_set, :Constant1, reference_to_constant1) }
|
55
|
+
stub(Marshal).constantize(reference_to_constant2.name) { Object.send(:const_set, :Constant2, reference_to_constant2) }
|
56
|
+
Marshal.load(marshaled_object).collect(&:class).should == object.collect(&:class)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Cash
|
4
|
+
describe 'Ordering' do
|
5
|
+
before :all do
|
6
|
+
FairyTale = Class.new(Story)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#create!' do
|
10
|
+
describe 'when the order is ascending' do
|
11
|
+
it_should_behave_like 'the records are written-through in sorted order'
|
12
|
+
|
13
|
+
before :all do
|
14
|
+
FairyTale.index :title, :order => :asc
|
15
|
+
end
|
16
|
+
|
17
|
+
def sorted_and_serialized_records(*records)
|
18
|
+
records.collect(&:id).sort
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'when the order is descending' do
|
23
|
+
it_should_behave_like 'the records are written-through in sorted order'
|
24
|
+
|
25
|
+
before :all do
|
26
|
+
FairyTale.index :title, :order => :desc
|
27
|
+
end
|
28
|
+
|
29
|
+
def sorted_and_serialized_records(*records)
|
30
|
+
records.collect(&:id).sort.reverse
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#find(..., :order => ...)" do
|
36
|
+
before :each do
|
37
|
+
@fairy_tales = [FairyTale.create!(:title => @title = 'title'), FairyTale.create!(:title => @title)]
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'when the order is ascending' do
|
41
|
+
before :all do
|
42
|
+
FairyTale.index :title, :order => :asc
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#find(..., :order => 'id ASC')" do
|
46
|
+
describe 'when the cache is populated' do
|
47
|
+
it 'does not use the database' do
|
48
|
+
mock(FairyTale.connection).execute.never
|
49
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id ASC').should == @fairy_tales
|
50
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id').should == @fairy_tales
|
51
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => '`id`').should == @fairy_tales
|
52
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'stories.id').should == @fairy_tales
|
53
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => '`stories`.id').should == @fairy_tales
|
54
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => '`stories`.`id`').should == @fairy_tales
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'when the order is passed as a symbol' do
|
58
|
+
it 'works' do
|
59
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => :id)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'when the cache is not populated' do
|
65
|
+
it 'populates the cache' do
|
66
|
+
$memcache.flush_all
|
67
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id ASC').should == @fairy_tales
|
68
|
+
FairyTale.get("title/#{@title}").should == @fairy_tales.collect(&:id)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#find(..., :order => 'id DESC')" do
|
74
|
+
describe 'when the cache is populated' do
|
75
|
+
it 'uses the database, not the cache' do
|
76
|
+
mock(FairyTale).get.never
|
77
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id DESC').should == @fairy_tales.reverse
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe 'when the cache is not populated' do
|
82
|
+
it 'does not populate the cache' do
|
83
|
+
$memcache.flush_all
|
84
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id DESC').should == @fairy_tales.reverse
|
85
|
+
FairyTale.get("title/#{@title}").should be_nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe 'when the order is descending' do
|
92
|
+
before :all do
|
93
|
+
FairyTale.index :title, :order => :desc
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#find(..., :order => 'id DESC')" do
|
97
|
+
describe 'when the cache is populated' do
|
98
|
+
it 'does not use the database' do
|
99
|
+
mock(FairyTale.connection).execute.never
|
100
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id DESC').should == @fairy_tales.reverse
|
101
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id DESC').should == @fairy_tales.reverse
|
102
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => '`id` DESC').should == @fairy_tales.reverse
|
103
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'stories.id DESC').should == @fairy_tales.reverse
|
104
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => '`stories`.id DESC').should == @fairy_tales.reverse
|
105
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => '`stories`.`id` DESC').should == @fairy_tales.reverse
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'when the cache is not populated' do
|
110
|
+
it 'populates the cache' do
|
111
|
+
$memcache.flush_all
|
112
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id DESC')
|
113
|
+
FairyTale.get("title/#{@title}").should == @fairy_tales.collect(&:id).reverse
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "#find(..., :order => 'id ASC')" do
|
119
|
+
describe 'when the cache is populated' do
|
120
|
+
it 'uses the database, not the cache' do
|
121
|
+
mock(FairyTale).get.never
|
122
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id ASC').should == @fairy_tales
|
123
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id').should == @fairy_tales
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe 'when the cache is not populated' do
|
128
|
+
it 'does not populate the cache' do
|
129
|
+
$memcache.flush_all
|
130
|
+
FairyTale.find(:all, :conditions => { :title => @title }, :order => 'id ASC').should == @fairy_tales
|
131
|
+
FairyTale.get("title/#{@title}").should be_nil
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/spec/cash/shared.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
module Cash
|
2
|
+
shared_examples_for "#fetch([...])" do
|
3
|
+
describe '#fetch([])' do
|
4
|
+
it 'returns the empty hash' do
|
5
|
+
Story.fetch([]).should == {}
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'when there is a total cache miss' do
|
10
|
+
it 'yields the keys to the block' do
|
11
|
+
Story.fetch(["yabba", "dabba"]) { |*missing_ids| ["doo", "doo"] }.should == {
|
12
|
+
"Story:1/yabba" => "doo",
|
13
|
+
"Story:1/dabba" => "doo"
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'when there is a partial cache miss' do
|
19
|
+
it 'yields just the missing ids to the block' do
|
20
|
+
Story.set("yabba", "dabba")
|
21
|
+
Story.fetch(["yabba", "dabba"]) { |*missing_ids| "doo" }.should == {
|
22
|
+
"Story:1/yabba" => "dabba",
|
23
|
+
"Story:1/dabba" => "doo"
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
shared_examples_for 'the records are written-through in sorted order' do
|
29
|
+
describe 'when there are not already records matching the index' do
|
30
|
+
it 'initializes the index' do
|
31
|
+
fairy_tale = FairyTale.create!(:title => 'title')
|
32
|
+
FairyTale.get("title/#{fairy_tale.title}").should == [fairy_tale.id]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'when there are already records matching the index' do
|
37
|
+
before do
|
38
|
+
@fairy_tale1 = FairyTale.create!(:title => 'title')
|
39
|
+
FairyTale.get("title/#{@fairy_tale1.title}").should == sorted_and_serialized_records(@fairy_tale1)
|
40
|
+
end
|
41
|
+
|
42
|
+
describe 'when the index is populated' do
|
43
|
+
it 'appends to the index' do
|
44
|
+
fairy_tale2 = FairyTale.create!(:title => @fairy_tale1.title)
|
45
|
+
FairyTale.get("title/#{@fairy_tale1.title}").should == sorted_and_serialized_records(@fairy_tale1, fairy_tale2)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'when the index is not populated' do
|
50
|
+
before do
|
51
|
+
$memcache.flush_all
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'initializes the index' do
|
55
|
+
fairy_tale2 = FairyTale.create!(:title => @fairy_tale1.title)
|
56
|
+
FairyTale.get("title/#{@fairy_tale1.title}").should == sorted_and_serialized_records(@fairy_tale1, fairy_tale2)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|