cache-machine 0.1.10 → 0.2.0

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 (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