cache-machine 0.1.10 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.gitignore +3 -1
  2. data/.travis.yml +6 -0
  3. data/Gemfile +0 -2
  4. data/Gemfile.lock +0 -2
  5. data/README.md +258 -0
  6. data/VERSION +1 -1
  7. data/cache-machine.gemspec +7 -22
  8. data/db/.gitignore +1 -0
  9. data/lib/cache-machine.rb +6 -2
  10. data/lib/cache_machine/adapter.rb +127 -0
  11. data/lib/cache_machine/adapters/rails.rb +58 -0
  12. data/lib/cache_machine/cache.rb +34 -85
  13. data/lib/cache_machine/cache/associations.rb +29 -0
  14. data/lib/cache_machine/cache/class_timestamp.rb +31 -0
  15. data/lib/cache_machine/cache/collection.rb +131 -0
  16. data/lib/cache_machine/cache/map.rb +97 -241
  17. data/lib/cache_machine/cache/mapper.rb +135 -0
  18. data/lib/cache_machine/cache/resource.rb +108 -0
  19. data/lib/cache_machine/cache/scope.rb +24 -0
  20. data/lib/cache_machine/cache/timestamp_builder.rb +58 -0
  21. data/lib/cache_machine/helpers/cache_helper.rb +3 -3
  22. data/lib/cache_machine/logger.rb +9 -8
  23. data/lib/cache_machine/railtie.rb +11 -0
  24. data/lib/cache_machine/tasks.rb +10 -0
  25. data/spec/fixtures.rb +11 -6
  26. data/spec/lib/cache_machine/adapters/rails_spec.rb +149 -0
  27. data/spec/lib/cache_machine/cache/associations_spec.rb +32 -0
  28. data/spec/lib/cache_machine/cache/class_timestam_spec.rb +29 -0
  29. data/spec/lib/cache_machine/cache/collection_spec.rb +106 -0
  30. data/spec/lib/cache_machine/cache/map_spec.rb +34 -0
  31. data/spec/lib/cache_machine/cache/mapper_spec.rb +61 -0
  32. data/spec/lib/cache_machine/cache/resource_spec.rb +86 -0
  33. data/spec/lib/cache_machine/cache/scope_spec.rb +50 -0
  34. data/spec/lib/cache_machine/cache/timestamp_builder_spec.rb +52 -0
  35. data/spec/spec_helper.rb +9 -3
  36. metadata +35 -61
  37. data/README.rdoc +0 -186
  38. data/spec/lib/cache_machine_spec.rb +0 -161
@@ -0,0 +1,149 @@
1
+ require "spec_helper"
2
+
3
+ if ENV["ADAPTER"] != 'redis'
4
+
5
+ describe CacheMachine::Adapters::Rails do
6
+ subject { CacheMachine::Cache::map_adapter }
7
+
8
+ let(:cacher) { Cacher.create }
9
+
10
+ before :each do
11
+ CacheMachine::Cache::Mapper.new do
12
+ resource Cacher do
13
+ collection :has_many_cacheables
14
+ end
15
+ end
16
+ end
17
+
18
+ describe "#append_id_to_map" do
19
+ let(:key) { subject.get_map_key(cacher, :joins) }
20
+
21
+ it "works" do
22
+ ::Rails.cache.should_receive(:write).with(key, [1])
23
+ subject.append_id_to_map(cacher, :joins, 1)
24
+ end
25
+
26
+ it "appends keys" do
27
+ subject.append_id_to_map(cacher, :joins, 1)
28
+ ::Rails.cache.should_receive(:write).with(key, [1, 2])
29
+ subject.append_id_to_map(cacher, :joins, 2)
30
+ end
31
+
32
+ it "does not append duplicate key" do
33
+ subject.append_id_to_map(cacher, :joins, 1)
34
+ ::Rails.cache.should_receive(:write).with(key, [1])
35
+ subject.append_id_to_map(cacher, :joins, 1)
36
+ end
37
+ end
38
+
39
+ describe "#append_id_to_reverse_map" do
40
+ let(:join) { cacher.joins.create }
41
+ let(:key) { subject.get_reverse_map_key(Cacher, :joins, join) }
42
+
43
+ it "works" do
44
+ ::Rails.cache.should_receive(:write).with(key, [1])
45
+ subject.append_id_to_reverse_map(Cacher, :joins, join, 1)
46
+ end
47
+
48
+ it "appends keys" do
49
+ subject.append_id_to_reverse_map(Cacher, :joins, join, 1)
50
+ ::Rails.cache.should_receive(:write).with(key, [1, 2])
51
+ subject.append_id_to_reverse_map(Cacher, :joins, join, 2)
52
+ end
53
+
54
+ it "does not append duplicate key" do
55
+ subject.append_id_to_reverse_map(Cacher, :joins, join, 1)
56
+ ::Rails.cache.should_receive(:write).with(key, [1])
57
+ subject.append_id_to_reverse_map(Cacher, :joins, join, 1)
58
+ end
59
+ end
60
+
61
+ describe "#association_ids" do
62
+ let(:cacher) { Cacher.create(:name => 'foo') }
63
+
64
+ before :each do
65
+ Rails.cache.clear
66
+ cacher.has_many_cacheables.create :id => 1
67
+ cacher.has_many_cacheables.create :id => 2
68
+ end
69
+
70
+ context "with clear cache" do
71
+ it("returns ids of an association") do
72
+ subject.association_ids(cacher, :has_many_cacheables).should =~ [1, 2]
73
+ end
74
+ end
75
+
76
+ context "filled cache" do
77
+ it "returns ids from cache if has already requested before" do
78
+ Rails.cache.should_receive(:fetch).and_return [1, 2]
79
+ subject.association_ids(cacher, :has_many_cacheables).should =~ [1, 2]
80
+ end
81
+ end
82
+ end
83
+
84
+ describe "#fetch" do
85
+ let(:cache_key) { 'test' }
86
+ let(:key) { subject.get_content_key(cache_key) }
87
+
88
+ it "works" do
89
+ ::Rails.cache.should_receive(:fetch).with(key, {})
90
+ subject.fetch(cache_key) { 'cached' }
91
+ end
92
+ end
93
+
94
+ describe "#fetch_timestamp" do
95
+ let(:cache_key) { 'test' }
96
+ let(:key) { subject.get_timestamp_key(cache_key) }
97
+
98
+ it "works" do
99
+ ::Rails.cache.should_receive(:fetch).with(key, {})
100
+ subject.fetch_timestamp(cache_key) { 'cached' }
101
+ end
102
+ end
103
+
104
+ describe "#delete" do
105
+ it "works" do
106
+ ::Rails.cache.should_receive(:delete).with('test')
107
+ subject.delete('test')
108
+ end
109
+ end
110
+
111
+ describe "#delete_content" do
112
+ let(:content_key) { 'test' }
113
+ let(:key) { subject.get_content_key(content_key) }
114
+
115
+ it "works" do
116
+ ::Rails.cache.should_receive(:delete).with(key)
117
+ subject.delete_content(content_key)
118
+ end
119
+ end
120
+
121
+ describe "#reset_timestamp" do
122
+ let(:timestamp) { 'test' }
123
+ let(:key) { subject.get_timestamp_key(timestamp) }
124
+
125
+ it "works" do
126
+ ::Rails.cache.should_receive(:delete).with(key)
127
+ subject.reset_timestamp(timestamp)
128
+ end
129
+ end
130
+
131
+ describe "#reverse_association_ids" do
132
+ let(:target) { cacher.has_many_cacheables.create }
133
+
134
+ context "with clear cache" do
135
+ it "returns ids of an association" do
136
+ subject.reverse_association_ids(Cacher, :has_many_cacheables, target).should == [cacher.id]
137
+ end
138
+ end
139
+
140
+ context "filled cache" do
141
+ it "returns ids from cache if has already requested before" do
142
+ subject.reverse_association_ids(Cacher, :has_many_cacheables, target)
143
+ target.should_not_receive(:cache_map_ids)
144
+ subject.reverse_association_ids(Cacher, :has_many_cacheables, target)
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ describe CacheMachine::Cache::Associations do
4
+
5
+ let(:cacher) { cacher = Cacher.create }
6
+ let(:join) { cacher.joins.create }
7
+ let(:hmt) { HasManyThroughCacheable.create(:cachers => [cacher]) }
8
+
9
+ before :each do
10
+ CacheMachine::Cache::Mapper.new do
11
+ resource Cacher do
12
+ collection :joins
13
+ collection :has_many_through_cacheables
14
+ end
15
+ end
16
+ end
17
+
18
+ describe "#association_ids" do
19
+ it "works" do
20
+ join
21
+ cacher.association_ids(:joins).should == [join.id]
22
+ end
23
+ end
24
+
25
+ describe "#associated_from_cache" do
26
+ it "works" do
27
+ hmt
28
+ cacher.associated_from_cache(:has_many_through_cacheables).should == [hmt]
29
+ cacher.associated_from_cache(:has_many_through_cacheables).should == [hmt]
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe CacheMachine::Cache::ClassTimestamp do
4
+ before :each do
5
+ CacheMachine::Cache::Mapper.new do
6
+ resource Cacher
7
+ end
8
+ end
9
+
10
+ describe "#timestamp" do
11
+ it "works" do
12
+ Cacher.timestamp
13
+ end
14
+
15
+ it "expires when collection changed" do
16
+ Cacher.should_receive(:reset_timestamp)
17
+ Cacher.create
18
+ end
19
+ end
20
+
21
+ describe "#reset_timestamp" do
22
+ it "works" do
23
+ old_timestamp = Cacher.timestamp
24
+ Time.stub("now").and_return('9999')
25
+ Cacher.create
26
+ Cacher.timestamp.should_not == old_timestamp
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+
3
+ describe CacheMachine::Cache::Collection do
4
+
5
+ let(:cacher) { cacher = Cacher.create }
6
+ let(:join) { cacher.joins.create }
7
+ let(:hm) { cacher.has_many_cacheables.create }
8
+ let(:hmt) { HasManyThroughCacheable.create(:cachers => [cacher]) }
9
+ let(:phm) { cacher.polymorphics.create }
10
+
11
+ before :each do
12
+ CacheMachine::Cache::Mapper.new do
13
+ resource Cacher do
14
+ collection :joins do
15
+ members :one, :two
16
+ end
17
+ collection :has_many_through_cacheables
18
+ collection :has_many_cacheables
19
+ collection :polymorphics
20
+ end
21
+ end
22
+ end
23
+
24
+ describe "#register_cache_dependency" do
25
+ let(:register_options) do
26
+ { :scopes => :scoped,
27
+ :on => :after_save,
28
+ :members => [:one, :two]
29
+ }
30
+ end
31
+
32
+ it "registers cache memebers" do
33
+ Join.cache_map_members.should == { Cacher => { :joins => register_options } }
34
+ end
35
+
36
+ it "appends callbacks" do
37
+ join.should_receive(:update_resource_collections_cache!).once.with(Cacher)
38
+ join.save
39
+ end
40
+
41
+ it "hooks update_map callback" do
42
+ Join.any_instance.should_receive(:update_cache_map!).once.with(cacher, :joins)
43
+ cacher.joins.create
44
+ end
45
+ end
46
+
47
+ describe "#update_map!" do
48
+ after :each do
49
+ cacher.joins.create
50
+ end
51
+
52
+ it "updates direct association map" do
53
+ CacheMachine::Cache.map_adapter.should_receive(:append_id_to_map).with(cacher, :joins, kind_of(Numeric))
54
+ end
55
+
56
+ it "updates reverse collection map" do
57
+ CacheMachine::Cache.map_adapter.should_receive(:append_id_to_reverse_map).with(Cacher, :joins, kind_of(Join), cacher.id)
58
+ end
59
+ end
60
+
61
+ describe "#update_resource_collections_cache!" do
62
+
63
+ context "on has one relation" do
64
+ after(:each) { join }
65
+
66
+ it "works" do
67
+ CacheMachine::Cache::Map.should_receive(:reset_cache_on_map).with(Cacher, [cacher.id], :one)
68
+ CacheMachine::Cache::Map.should_receive(:reset_cache_on_map).with(Cacher, [cacher.id], :two)
69
+ CacheMachine::Cache::Map.should_receive(:reset_cache_on_map).with(Cacher, [cacher.id], :joins)
70
+ end
71
+ end
72
+
73
+ context "on has many relation" do
74
+ it "works" do
75
+ CacheMachine::Cache::Map.should_receive(:reset_cache_on_map).with(Cacher, [cacher.id], :has_many_cacheables)
76
+ hm
77
+ end
78
+
79
+ it "works on after_add callback" do
80
+ pending "<<, concat do not call after_add by some reason" do
81
+ CacheMachine::Cache::Map.should_receive(:reset_cache_on_map).with(Cacher, [cacher.id], :has_many_cacheables)
82
+ hmc = HasManyCacheable.create
83
+ cacher.has_many_cacheables << hmc
84
+ end
85
+ end
86
+ end
87
+
88
+ context "on has many through relation" do
89
+ before(:each) { hmt }
90
+ after(:each) { hmt.save }
91
+
92
+ it "works" do
93
+ CacheMachine::Cache::Map.should_receive(:reset_cache_on_map).with(Cacher, [cacher.id], :has_many_through_cacheables)
94
+ end
95
+ end
96
+
97
+ context "on polymorphic relation" do
98
+ before(:each) { phm }
99
+ after(:each) { phm.save }
100
+
101
+ it "works" do
102
+ CacheMachine::Cache::Map.should_receive(:reset_cache_on_map).with(Cacher, [cacher.id], :polymorphics)
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe CacheMachine::Cache::Map do
4
+
5
+ let(:cacher) { cacher = Cacher.create }
6
+ let(:join1) { cacher.joins.create }
7
+ let(:join2) { cacher.joins.create }
8
+
9
+ before :each do
10
+ CacheMachine::Cache::Mapper.new do
11
+ resource Cacher do
12
+ collection :joins
13
+ end
14
+ end
15
+ end
16
+
17
+ describe "::fill_associations_map" do
18
+ before :each do
19
+ cacher and join1 and join2
20
+ CacheMachine::Cache::map_adapter.delete(CacheMachine::Cache::map_adapter.get_map_key(cacher, :joins))
21
+ end
22
+
23
+ it "really breaks" do
24
+ cacher.should_receive(:association_ids)
25
+ CacheMachine::Cache::map_adapter.association_ids(cacher, :joins)
26
+ end
27
+
28
+ it "works" do
29
+ CacheMachine::Cache::Map.fill_associations_map(Cacher)
30
+ cacher.should_not_receive(:association_ids)
31
+ CacheMachine::Cache::map_adapter.association_ids(cacher, :joins).should =~ [join1.id, join2.id]
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe CacheMachine::Cache::Mapper do
4
+ subject { CacheMachine::Cache::Mapper.new }
5
+
6
+ its(:scope) { should == :root }
7
+
8
+ describe "#resource" do
9
+ before :each do
10
+ subject.resource(Cacher)
11
+ end
12
+
13
+ it "makes module be a resource" do
14
+ Cacher.include?(CacheMachine::Cache::Resource).should be_true
15
+ end
16
+
17
+ it "changes scope back" do
18
+ subject.scope.should == :root
19
+ end
20
+
21
+ it "registers model" do
22
+ CacheMachine::Cache::Map.registered_models.should == [Cacher]
23
+ end
24
+ end
25
+
26
+ describe "#collection" do
27
+ let(:register_options) do
28
+ { :scopes => :scoped,
29
+ :on => :after_save,
30
+ :members => [:one, :two]
31
+ }
32
+ end
33
+
34
+ before :each do
35
+ Join.should_receive(:register_cache_dependency).with(Cacher, :joins, register_options)
36
+
37
+ subject.resource(Cacher) do
38
+ collection(:joins) do
39
+ member :one
40
+ member :two
41
+ end
42
+ end
43
+ end
44
+
45
+ it "includes collection module in associated class" do
46
+ Join.include?(CacheMachine::Cache::Collection).should be_true
47
+ end
48
+
49
+ it "changes scope back" do
50
+ subject.scope.should == :root
51
+ end
52
+
53
+ it "raises an exception when collection is not defined as an association" do
54
+ lambda {
55
+ subject.resource(Cacher) do
56
+ collection :unexisted_relation
57
+ end
58
+ }.should raise_error(ArgumentError, "Relation 'unexisted_relation' is not set on the class Cacher")
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe CacheMachine::Cache::Resource do
4
+
5
+ let(:cacher) { cacher = Cacher.create }
6
+ let(:join) { cacher.joins.create }
7
+ let(:hm) { cacher.has_many_cacheables.create }
8
+ let(:hmt) { HasManyThroughCacheable.create(:cachers => [cacher]) }
9
+ let(:phm) { cacher.polymorphics.create }
10
+
11
+ before :each do
12
+ CacheMachine::Cache::Mapper.new do
13
+ resource Cacher do
14
+ collection :joins do
15
+ member :one
16
+ member :two
17
+ end
18
+ collection :has_many_through_cacheables
19
+ collection :has_many_cacheables
20
+ collection :polymorphics
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "#fetch_cache_of" do
26
+ it "works" do
27
+ cacher.fetch_cache_of(:has_many_cacheables) { 'cached' }.should == 'cached'
28
+ cacher.fetch_cache_of(:has_many_cacheables) { 'non-cached' }.should == 'cached'
29
+ hm
30
+ cacher.fetch_cache_of(:has_many_cacheables) { 'updated' }.should == 'updated'
31
+ end
32
+
33
+ context "with timestamps" do
34
+ it "works" do
35
+ cacher.fetch_cache_of(:has_many_cacheables, :timestamp => :resource_test_timestamp) { 'cached' }.should == 'cached'
36
+ Time.stub(:now).and_return(1)
37
+ cacher.fetch_cache_of(:has_many_cacheables, :timestamp => :resource_test_timestamp) { 'non-cached' }.should == 'cached'
38
+ Time.stub(:now).and_return(2)
39
+ cacher.fetch_cache_of(:has_many_cacheables, :timestamp => :resource_test_timestamp2) { 'cached-2' }.should == 'cached-2'
40
+ end
41
+
42
+ it "updates when collection changed" do
43
+ cacher.fetch_cache_of(:has_many_cacheables, :timestamp => :resource_test_timestamp) { 'cached' }.should == 'cached'
44
+ hm
45
+ Time.stub("now").and_return('another time comes')
46
+ cacher.fetch_cache_of(:has_many_cacheables, :timestamp => :resource_test_timestamp) { 'updated' }.should == 'updated'
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#delete_cache_of_only" do
52
+ it "works" do
53
+ cacher.fetch_cache_of(:test) { 'cached' }
54
+ cacher.delete_cache_of_only(:test)
55
+ cacher.fetch_cache_of(:test) { 'updated' }.should == 'updated'
56
+ end
57
+ end
58
+
59
+ describe "#delete_cache_of" do
60
+ context "on collection member" do
61
+ it "works" do
62
+ HasManyCacheable.should_receive(:reset_resource_cache).once.with(cacher, :has_many_cacheables)
63
+ cacher.delete_cache_of(:has_many_cacheables)
64
+ end
65
+ end
66
+
67
+ context "on virtual member" do
68
+ it "works" do
69
+ cacher.should_receive(:delete_cache_of_only).with(:test)
70
+ cacher.delete_cache_of(:test)
71
+ end
72
+ end
73
+ end
74
+
75
+ describe "#delete_all_caches" do
76
+ it "works" do
77
+ cacher.fetch_cache_of(:has_many_cacheables) { 'cached' }
78
+ cacher.fetch_cache_of(:has_many_through_cacheables) { 'cached' }
79
+
80
+ cacher.delete_all_caches
81
+
82
+ cacher.fetch_cache_of(:has_many_cacheables) { 'updated' }.should == 'updated'
83
+ cacher.fetch_cache_of(:has_many_through_cacheables) { 'updated' }.should == 'updated'
84
+ end
85
+ end
86
+ end