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.
Files changed (49) hide show
  1. data/LICENSE +201 -0
  2. data/README +210 -0
  3. data/README.markdown +210 -0
  4. data/TODO +17 -0
  5. data/UNSUPPORTED_FEATURES +13 -0
  6. data/config/environment.rb +16 -0
  7. data/config/memcached.yml +6 -0
  8. data/db/schema.rb +18 -0
  9. data/init.rb +1 -0
  10. data/lib/cache_money.rb +91 -0
  11. data/lib/cash/accessor.rb +83 -0
  12. data/lib/cash/buffered.rb +129 -0
  13. data/lib/cash/config.rb +75 -0
  14. data/lib/cash/fake.rb +83 -0
  15. data/lib/cash/finders.rb +38 -0
  16. data/lib/cash/index.rb +214 -0
  17. data/lib/cash/local.rb +76 -0
  18. data/lib/cash/lock.rb +63 -0
  19. data/lib/cash/mock.rb +154 -0
  20. data/lib/cash/query/abstract.rb +210 -0
  21. data/lib/cash/query/calculation.rb +45 -0
  22. data/lib/cash/query/primary_key.rb +50 -0
  23. data/lib/cash/query/select.rb +16 -0
  24. data/lib/cash/request.rb +3 -0
  25. data/lib/cash/transactional.rb +43 -0
  26. data/lib/cash/util/array.rb +9 -0
  27. data/lib/cash/util/marshal.rb +19 -0
  28. data/lib/cash/write_through.rb +69 -0
  29. data/lib/mem_cached_session_store.rb +49 -0
  30. data/lib/mem_cached_support_store.rb +135 -0
  31. data/lib/memcached_wrapper.rb +263 -0
  32. data/rails/init.rb +38 -0
  33. data/spec/cash/accessor_spec.rb +159 -0
  34. data/spec/cash/active_record_spec.rb +224 -0
  35. data/spec/cash/buffered_spec.rb +9 -0
  36. data/spec/cash/calculations_spec.rb +78 -0
  37. data/spec/cash/finders_spec.rb +430 -0
  38. data/spec/cash/local_buffer_spec.rb +9 -0
  39. data/spec/cash/local_spec.rb +9 -0
  40. data/spec/cash/lock_spec.rb +108 -0
  41. data/spec/cash/marshal_spec.rb +60 -0
  42. data/spec/cash/order_spec.rb +138 -0
  43. data/spec/cash/shared.rb +62 -0
  44. data/spec/cash/transactional_spec.rb +578 -0
  45. data/spec/cash/window_spec.rb +195 -0
  46. data/spec/cash/write_through_spec.rb +245 -0
  47. data/spec/memcached_wrapper_test.rb +209 -0
  48. data/spec/spec_helper.rb +60 -0
  49. 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
@@ -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
+