coffee_table 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +0 -1
- data/.travis.yml +9 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +45 -0
- data/README.textile +79 -0
- data/coffee_table.gemspec +5 -2
- data/lib/coffee_table.rb +57 -28
- data/lib/coffee_table/coffee_table_block_missing_error.rb +3 -0
- data/lib/coffee_table/coffee_table_invalid_object_error.rb +3 -0
- data/lib/coffee_table/version.rb +1 -1
- data/spec/lib/coffee_table_spec.rb +234 -21
- data/spec/lib/sample_class.rb +4 -1
- data/spec/spec_helper.rb +2 -0
- metadata +52 -3
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
coffee_table (0.0.2)
|
5
|
+
activesupport
|
6
|
+
redis
|
7
|
+
rufus-scheduler
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
activesupport (3.2.11)
|
13
|
+
i18n (~> 0.6)
|
14
|
+
multi_json (~> 1.0)
|
15
|
+
awesome_print (1.1.0)
|
16
|
+
diff-lcs (1.1.3)
|
17
|
+
i18n (0.6.1)
|
18
|
+
mock_redis (0.6.3)
|
19
|
+
multi_json (1.0.4)
|
20
|
+
redis (3.0.2)
|
21
|
+
rspec (2.12.0)
|
22
|
+
rspec-core (~> 2.12.0)
|
23
|
+
rspec-expectations (~> 2.12.0)
|
24
|
+
rspec-mocks (~> 2.12.0)
|
25
|
+
rspec-core (2.12.2)
|
26
|
+
rspec-expectations (2.12.1)
|
27
|
+
diff-lcs (~> 1.1.3)
|
28
|
+
rspec-mocks (2.12.1)
|
29
|
+
rufus-scheduler (2.0.17)
|
30
|
+
tzinfo (>= 0.3.23)
|
31
|
+
spork (0.9.2)
|
32
|
+
tzinfo (0.3.35)
|
33
|
+
|
34
|
+
PLATFORMS
|
35
|
+
ruby
|
36
|
+
|
37
|
+
DEPENDENCIES
|
38
|
+
activesupport
|
39
|
+
awesome_print
|
40
|
+
coffee_table!
|
41
|
+
mock_redis
|
42
|
+
redis
|
43
|
+
rspec
|
44
|
+
rufus-scheduler
|
45
|
+
spork
|
data/README.textile
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
|
2
|
+
h1. CoffeeTable v0.0.3
|
3
|
+
|
4
|
+
!https://secure.travis-ci.org/stewartmckee/coffee_table.png?branch=master!
|
5
|
+
!https://gemnasium.com/stewartmckee/coffee_table.png!
|
6
|
+
|
7
|
+
h2. Intro
|
8
|
+
|
9
|
+
CoffeeTable was born out of a frustration with the standard caching methods around. Maintaining the cache keys constantly was a headache and 'bet its a caching issue' was a phrase uttered way too much. CoffeeTable maintains a list of its keys in a known format and when expiry is required for an object it knows which ones to expire
|
10
|
+
|
11
|
+
h3. Installation
|
12
|
+
|
13
|
+
bc. gem install coffee_table
|
14
|
+
|
15
|
+
h2. Usage
|
16
|
+
|
17
|
+
h3. CoffeeTable::Cache
|
18
|
+
|
19
|
+
h4. new(options)
|
20
|
+
|
21
|
+
Creates a new cache object. You can pass various options into this method to set configuration options for the cache.
|
22
|
+
|
23
|
+
* :enable_cache This defaults to true, but can be set to false to disable the cache
|
24
|
+
* :redis_namespace defaults to ":coffee_table" and is set to seperate out the keys from other redis users
|
25
|
+
* :redis_server defaults to "127.0.0.1"
|
26
|
+
* :redis_port defaults to 6789
|
27
|
+
|
28
|
+
|
29
|
+
h4. get_cache(initial_key, *related_objects, &block)
|
30
|
+
|
31
|
+
This is the main caching method. Pass into this method as a block the chunck of code you want cached. The first parameter is your key for this data and is then followed by as many objects as are valid for this block of code. You can even pass in arrays and they will be expanded. The only requirement for the objects being passed in is that they respond to an 'id' method. If the last parameter is a Hash, this will be used as per cache options. These options can be used for expiry of cache.
|
32
|
+
|
33
|
+
bc. user_details = @coffee_table.get_cache(:user_detail, @user, :expiry => 600) do
|
34
|
+
@user.get_expensive_user_details
|
35
|
+
end
|
36
|
+
|
37
|
+
Each time this is ran, a unique cache key is generated, assuming the @user.id is '1' the cache key would be "user_detail_user[1]". The more objects the longer the key. It is good practice to put in objects that are used within the block, as in order to expire the key you need to specify the objects you want to expire for. If this key contained one of those objects, it would be removed and the next time this was ran, fresh data would be placed in the cache.
|
38
|
+
|
39
|
+
If you wish to specify a whole model type, for example, all users from above, you would pass in the class, for example:
|
40
|
+
|
41
|
+
bc. user_details = @coffee_table.get_cache(:user_detail, User) do
|
42
|
+
@user.get_something_that_uses_all_users
|
43
|
+
end
|
44
|
+
|
45
|
+
In this case the key will be "user_detail_users", for which you can expire_for(User) which will clear regardless of the specific object id.
|
46
|
+
|
47
|
+
The only required field is the first parameter, so you can create keys and cache as you normally would, ignoring the objects.
|
48
|
+
|
49
|
+
h4. expire_key(key)
|
50
|
+
|
51
|
+
This method directly expires a known key.
|
52
|
+
|
53
|
+
bc. @coffee_table.expire_key("user_detail_user[1]")
|
54
|
+
|
55
|
+
The above code would expire the above example of cache.
|
56
|
+
|
57
|
+
h4. expire_all
|
58
|
+
|
59
|
+
This method clears the whole cache.
|
60
|
+
|
61
|
+
bc. @coffee_table.expire_all
|
62
|
+
|
63
|
+
h4. keys
|
64
|
+
|
65
|
+
This is a helper method to return the list of keys currently in the system. This list is maintained when cache is created and expired.
|
66
|
+
|
67
|
+
h4. expire_for(*objects)
|
68
|
+
|
69
|
+
This is the main expire method. In order to expire a cache, you only need to pass in any objects that would be invalidated. With the above example this would be as follows.
|
70
|
+
|
71
|
+
bc. @coffee_table.expire_for(@user)
|
72
|
+
|
73
|
+
This would search through the keys to find any that contain this particular user. If it finds any, it will invalidate that cache entry.
|
74
|
+
|
75
|
+
You can also expire for a whole class type
|
76
|
+
|
77
|
+
bc. @coffee_table.expire_for(User)
|
78
|
+
|
79
|
+
this would expire all keys that reference the user objects.
|
data/coffee_table.gemspec
CHANGED
@@ -19,6 +19,9 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
21
|
# specify any dependencies here; for example:
|
22
|
-
|
23
|
-
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
s.add_dependency "redis"
|
24
|
+
s.add_dependency "rufus-scheduler"
|
25
|
+
s.add_dependency "activesupport"
|
26
|
+
|
24
27
|
end
|
data/lib/coffee_table.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
require "coffee_table/version"
|
2
2
|
require "utility"
|
3
3
|
require "redis"
|
4
|
+
require 'rufus/scheduler'
|
5
|
+
require 'active_support/inflector'
|
4
6
|
|
5
7
|
module CoffeeTable
|
6
8
|
class Cache
|
7
9
|
|
8
10
|
include CoffeeTable::Utility
|
9
|
-
|
11
|
+
|
10
12
|
def initialize(options={})
|
11
13
|
@options = options
|
12
14
|
|
@@ -14,29 +16,38 @@ module CoffeeTable
|
|
14
16
|
default_redis_namespace_to :coffee_table
|
15
17
|
default_redis_server_to "127.0.0.1"
|
16
18
|
default_redis_port_to 6789
|
19
|
+
|
20
|
+
setup_redis
|
21
|
+
@scheduler = Rufus::Scheduler.start_new
|
17
22
|
|
18
23
|
end
|
19
24
|
|
20
25
|
def get_cache(initial_key, *related_objects, &block)
|
21
|
-
|
26
|
+
|
27
|
+
raise CoffeeTableBlockMissingError, "No block given to generate cache from" unless block_given?
|
28
|
+
|
29
|
+
# extract the options hash if it is present
|
30
|
+
options = {}
|
31
|
+
if related_objects[-1].instance_of? Hash
|
32
|
+
options = related_objects[-1]
|
33
|
+
related_objects = related_objects[0..-2]
|
34
|
+
end
|
22
35
|
|
23
36
|
# check objects are valid
|
24
|
-
related_objects.map{|o| raise "Objects passed in must have an id method" unless o
|
37
|
+
related_objects.flatten.map{|o| raise CoffeeTableInvalidObjectError, "Objects passed in must have an id method or be a class" unless object_valid?(o)}
|
25
38
|
|
26
39
|
# if first related_object is integer or fixnum it is used as an expiry time for the cache object
|
27
|
-
# TODO change this to use hash and be at the end as convention
|
28
40
|
if related_objects.empty?
|
29
41
|
key = "#{initial_key}"
|
30
42
|
else
|
31
|
-
key = "#{initial_key}
|
43
|
+
key = "#{initial_key}|#{related_objects.flatten.map{|o| key_for_object(o)}.join("|")}"
|
32
44
|
end
|
33
45
|
|
34
46
|
if @options[:enable_cache]
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
expiry =
|
39
|
-
related_objects = related_objects[1..-1]
|
47
|
+
if options.has_key?(:expiry)
|
48
|
+
expiry = options[:expiry]
|
49
|
+
else
|
50
|
+
expiry = nil
|
40
51
|
end
|
41
52
|
|
42
53
|
@redis.sadd "cache_keys", key unless @redis.sismember "cache_keys", key
|
@@ -50,20 +61,20 @@ module CoffeeTable
|
|
50
61
|
# result = result.all
|
51
62
|
#end
|
52
63
|
@redis.set key, Marshal.dump(result)
|
53
|
-
|
64
|
+
unless expiry.nil?
|
65
|
+
@redis.expire key, expiry
|
66
|
+
@scheduler.in "#{expiry}s" do
|
67
|
+
@redis.srem "cache_keys", key
|
68
|
+
end
|
69
|
+
end
|
54
70
|
end
|
55
71
|
else
|
56
|
-
|
57
|
-
raise "cache is disabled and no block is given"
|
58
|
-
else
|
59
|
-
result = yield
|
60
|
-
end
|
72
|
+
result = yield
|
61
73
|
end
|
62
74
|
result
|
63
75
|
end
|
64
76
|
|
65
77
|
def expire_key(key)
|
66
|
-
setup_redis
|
67
78
|
@redis.del(key)
|
68
79
|
@redis.srem "cache_keys", key
|
69
80
|
end
|
@@ -73,29 +84,37 @@ module CoffeeTable
|
|
73
84
|
end
|
74
85
|
|
75
86
|
def keys
|
76
|
-
setup_redis
|
77
87
|
@redis.smembers "cache_keys"
|
78
88
|
end
|
79
89
|
|
80
90
|
def expire_for(*objects)
|
81
|
-
|
82
|
-
|
83
|
-
|
91
|
+
if defined? Rails
|
92
|
+
perform_caching = Rails.application.configure do
|
93
|
+
config.action_controller.perform_caching
|
94
|
+
end
|
95
|
+
else
|
96
|
+
perform_caching = true
|
97
|
+
end
|
84
98
|
|
85
99
|
if perform_caching
|
86
100
|
deleted_keys = []
|
87
|
-
unless objects.
|
88
|
-
|
101
|
+
unless objects.count == 0
|
102
|
+
keys.each do |key|
|
89
103
|
expire = true
|
90
104
|
objects.each do |object|
|
91
|
-
mod_key = "
|
92
|
-
if object.class == String
|
93
|
-
unless mod_key.include?("
|
105
|
+
mod_key = "|#{key}|"
|
106
|
+
if object.class == String || object.class == Symbol
|
107
|
+
unless mod_key.include?("|#{object}|")
|
108
|
+
expire = false
|
109
|
+
end
|
110
|
+
elsif object.class == Class
|
111
|
+
object_type = underscore(object.to_s)
|
112
|
+
unless mod_key.include?("|#{object_type}[") or mod_key.include?("|#{ActiveSupport::Inflector.pluralize(object_type)}|")
|
94
113
|
expire = false
|
95
114
|
end
|
96
115
|
else
|
97
|
-
object_type = object.class.to_s
|
98
|
-
unless mod_key.include?("
|
116
|
+
object_type = underscore(object.class.to_s)
|
117
|
+
unless mod_key.include?("|#{object_type.to_sym}[#{object.id}]|") or mod_key.include?("|#{object_type}|")
|
99
118
|
expire = false
|
100
119
|
end
|
101
120
|
end
|
@@ -124,5 +143,15 @@ module CoffeeTable
|
|
124
143
|
def setup_redis
|
125
144
|
@redis = Redis.new #::Namespace.new(options[:redis_namespace], :redis => Redis.new({:server => @options[:redis_server], :port => @options[:redis_port]}))
|
126
145
|
end
|
146
|
+
def object_valid?(o)
|
147
|
+
o.respond_to?(:id) || o.class == Class
|
148
|
+
end
|
149
|
+
def key_for_object(o)
|
150
|
+
if o.class == Class
|
151
|
+
"#{ActiveSupport::Inflector.pluralize(underscore(o.to_s))}"
|
152
|
+
else
|
153
|
+
"#{underscore(o.class.to_s)}[#{o.id}]"
|
154
|
+
end
|
155
|
+
end
|
127
156
|
end
|
128
157
|
end
|
data/lib/coffee_table/version.rb
CHANGED
@@ -18,20 +18,13 @@ describe CoffeeTable do
|
|
18
18
|
CoffeeTable::Cache.new({:test => "asdf"})
|
19
19
|
end
|
20
20
|
it "should not raise exception when hash not given" do
|
21
|
-
lambda{CoffeeTable::Cache.new}.should_not raise_exception
|
21
|
+
lambda{CoffeeTable::Cache.new}.should_not raise_exception CoffeeTableBlockMissingError
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
describe "get_cache" do
|
26
26
|
it "should raise an exception when block not given" do
|
27
|
-
lambda{@coffee_table.get_cache("asdf")}.should raise_exception
|
28
|
-
end
|
29
|
-
it "should not raise an exception if cache value is available and no block given" do
|
30
|
-
result = @coffee_table.get_cache("test_key") do
|
31
|
-
"this is a valid result"
|
32
|
-
end
|
33
|
-
|
34
|
-
@coffee_table.get_cache("test_key").should == "this is a valid result"
|
27
|
+
lambda{@coffee_table.get_cache("asdf")}.should raise_exception CoffeeTableBlockMissingError
|
35
28
|
end
|
36
29
|
it "should execute block when cache value not available" do
|
37
30
|
result = @coffee_table.get_cache("asdf") do
|
@@ -64,54 +57,274 @@ describe CoffeeTable do
|
|
64
57
|
TESTVAR.should == "testvar"
|
65
58
|
|
66
59
|
end
|
67
|
-
|
68
|
-
|
60
|
+
|
61
|
+
context "keys" do
|
62
|
+
it "should create a key with just the initial key" do
|
69
63
|
result = @coffee_table.get_cache(:test_key) do
|
70
64
|
"this is a changed value"
|
71
65
|
end
|
72
|
-
|
73
|
-
|
74
|
-
|
66
|
+
@coffee_table.keys.should == ["test_key"]
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should create key from class" do
|
70
|
+
result = @coffee_table.get_cache(:test_key, SampleClass) do
|
71
|
+
"this is a changed value"
|
72
|
+
end
|
73
|
+
@coffee_table.keys.should == ["test_key|sample_classes"]
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should use class name for keys" do
|
77
|
+
result = @coffee_table.get_cache(:test_key, SampleClass.new(2)) do
|
78
|
+
"this is a changed value"
|
79
|
+
end
|
80
|
+
@coffee_table.keys.should == ["test_key|sample_class[2]"]
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should use id from class in key" do
|
84
|
+
result = @coffee_table.get_cache(:test_key, SampleClass.new(2)) do
|
85
|
+
"this is a changed value"
|
86
|
+
end
|
87
|
+
@coffee_table.keys.should == ["test_key|sample_class[2]"]
|
75
88
|
end
|
89
|
+
|
76
90
|
end
|
91
|
+
|
92
|
+
|
77
93
|
context "with related objects" do
|
78
94
|
it "should create a key from the id's of the related objects" do
|
79
|
-
test_object = SampleClass.new
|
95
|
+
test_object = SampleClass.new(9938)
|
80
96
|
result = @coffee_table.get_cache(:test_key, test_object) do
|
81
97
|
"this is a changed value"
|
82
98
|
end
|
83
99
|
|
84
|
-
|
85
|
-
@coffee_table.keys.should include "test_key_sample_class[9939]"
|
100
|
+
@coffee_table.keys.should include "test_key|sample_class[9938]"
|
86
101
|
|
87
102
|
end
|
88
103
|
it "should raise an exception if a related object does not respond_to id" do
|
89
104
|
test_object = SampleClassWithoutId.new
|
90
105
|
|
91
106
|
lambda {
|
92
|
-
|
107
|
+
result = @coffee_table.get_cache(:test_key, test_object) do
|
108
|
+
"this is a changed value"
|
109
|
+
end
|
110
|
+
}.should raise_exception CoffeeTableInvalidObjectError, "Objects passed in must have an id method or be a class"
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should create a universal key if the objects passed in are an uninitialised class" do
|
115
|
+
|
116
|
+
result = @coffee_table.get_cache(:test_key, SampleClass) do
|
93
117
|
"this is a changed value"
|
94
118
|
end
|
95
|
-
}.should raise_exception "Objects passed in must have an id method"
|
96
119
|
|
120
|
+
@coffee_table.keys.should include "test_key|sample_classes"
|
97
121
|
end
|
122
|
+
|
98
123
|
end
|
99
124
|
context "with expiry" do
|
100
|
-
it "should
|
101
|
-
|
125
|
+
it "keys should update when cache expires" do
|
126
|
+
@coffee_table.get_cache(:test_key, :expiry => 0.2) do
|
127
|
+
"object1"
|
128
|
+
end
|
129
|
+
@coffee_table.keys.count.should == 1
|
130
|
+
sleep 0.5
|
131
|
+
@coffee_table.keys.count.should == 0
|
132
|
+
end
|
133
|
+
it "should not execute block during cache period" do
|
134
|
+
@coffee_table.get_cache("asdf", :expiry => 1) do
|
135
|
+
"this is a value"
|
136
|
+
end
|
137
|
+
result = @coffee_table.get_cache("asdf") do
|
138
|
+
"this is a changed value"
|
139
|
+
end
|
140
|
+
result.should == "this is a value"
|
141
|
+
|
142
|
+
end
|
143
|
+
it "should execute block and return value when cache has expired" do
|
144
|
+
@coffee_table.get_cache("asdf", :expiry => 1) do
|
145
|
+
"this is a value"
|
146
|
+
end
|
147
|
+
sleep 2
|
148
|
+
result = @coffee_table.get_cache("asdf") do
|
149
|
+
"this is a changed value"
|
150
|
+
end
|
151
|
+
result.should == "this is a changed value"
|
152
|
+
end
|
102
153
|
end
|
103
154
|
end
|
104
155
|
|
105
156
|
describe "expire_key" do
|
157
|
+
it "should expire the specified key" do
|
158
|
+
@coffee_table.get_cache(:first_key) do
|
159
|
+
"object1"
|
160
|
+
end
|
161
|
+
@coffee_table.get_cache(:second_key) do
|
162
|
+
"object2"
|
163
|
+
end
|
164
|
+
@coffee_table.get_cache(:third_key) do
|
165
|
+
"object3"
|
166
|
+
end
|
167
|
+
|
168
|
+
@coffee_table.keys.sort.should == ["first_key", "second_key", "third_key"].sort
|
169
|
+
@coffee_table.expire_key("second_key")
|
170
|
+
@coffee_table.keys.sort.should == ["first_key", "third_key"].sort
|
171
|
+
|
172
|
+
end
|
173
|
+
it "should not expire anything if no matches" do
|
174
|
+
@coffee_table.get_cache(:first_key) do
|
175
|
+
"object1"
|
176
|
+
end
|
177
|
+
@coffee_table.get_cache(:second_key) do
|
178
|
+
"object2"
|
179
|
+
end
|
180
|
+
@coffee_table.get_cache(:third_key) do
|
181
|
+
"object3"
|
182
|
+
end
|
183
|
+
|
184
|
+
@coffee_table.keys.sort.should == ["first_key", "second_key", "third_key"].sort
|
185
|
+
@coffee_table.expire_key("fourth_key")
|
186
|
+
@coffee_table.keys.sort.should == ["first_key", "second_key", "third_key"].sort
|
187
|
+
|
188
|
+
end
|
106
189
|
end
|
107
190
|
|
108
191
|
describe "expire_all" do
|
192
|
+
before(:each) do
|
193
|
+
|
194
|
+
object1 = [SampleClass.new(1), SampleClass.new(2), SampleClass.new(3)]
|
195
|
+
object2 = [SampleClass.new(4), SampleClass.new(2), SampleClass.new(5)]
|
196
|
+
object3 = [SampleClass.new(7), SampleClass.new(2), SampleClass.new(8)]
|
197
|
+
|
198
|
+
@coffee_table.get_cache(:first_key) do
|
199
|
+
"object1"
|
200
|
+
end
|
201
|
+
@coffee_table.get_cache(:second_key) do
|
202
|
+
"object2"
|
203
|
+
end
|
204
|
+
@coffee_table.get_cache(:third_key) do
|
205
|
+
"object3"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should delete all keys" do
|
210
|
+
@coffee_table.keys.count.should == 3
|
211
|
+
@coffee_table.expire_all
|
212
|
+
@coffee_table.keys.count.should == 0
|
213
|
+
|
214
|
+
result = @coffee_table.get_cache(:first_key) do
|
215
|
+
"changed value"
|
216
|
+
end
|
217
|
+
|
218
|
+
result.should == "changed value"
|
219
|
+
|
220
|
+
end
|
109
221
|
end
|
110
222
|
|
111
223
|
describe "keys" do
|
224
|
+
before(:each) do
|
225
|
+
@object1 = [SampleClass.new(1), SampleClass.new(2), SampleClass.new(3)]
|
226
|
+
@object2 = [SampleClass.new(4), SampleClass.new(2), SampleClass.new(5)]
|
227
|
+
@object3 = [SampleClass.new(7), SampleClass.new(2), SampleClass.new(8)]
|
228
|
+
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should return an array of string" do
|
232
|
+
@coffee_table.keys.should be_an_instance_of Array
|
233
|
+
@coffee_table.keys.map{|key| key.should be_an_instance_of String}
|
234
|
+
end
|
235
|
+
it "should return key created without objects" do
|
236
|
+
@coffee_table.get_cache(:first_key) do
|
237
|
+
"object1"
|
238
|
+
end
|
239
|
+
@coffee_table.get_cache(:second_key) do
|
240
|
+
"object2"
|
241
|
+
end
|
242
|
+
@coffee_table.get_cache(:third_key) do
|
243
|
+
"object3"
|
244
|
+
end
|
245
|
+
|
246
|
+
@coffee_table.keys.sort.should == ["first_key",
|
247
|
+
"second_key",
|
248
|
+
"third_key"].sort
|
249
|
+
|
250
|
+
end
|
251
|
+
it "should return key created with objects and ids" do
|
252
|
+
@coffee_table.get_cache(:first_key, @object1) do
|
253
|
+
"object1"
|
254
|
+
end
|
255
|
+
@coffee_table.get_cache(:second_key, @object2) do
|
256
|
+
"object2"
|
257
|
+
end
|
258
|
+
@coffee_table.get_cache(:third_key, @object3) do
|
259
|
+
"object3"
|
260
|
+
end
|
261
|
+
@coffee_table.keys.sort.should == ["first_key|sample_class[1]|sample_class[2]|sample_class[3]",
|
262
|
+
"second_key|sample_class[4]|sample_class[2]|sample_class[5]",
|
263
|
+
"third_key|sample_class[7]|sample_class[2]|sample_class[8]"].sort
|
264
|
+
end
|
265
|
+
|
112
266
|
end
|
113
267
|
|
114
268
|
describe "expire_for" do
|
269
|
+
before(:each) do
|
270
|
+
object1 = [SampleClass.new(1), SampleClass.new(2), SampleClass.new(3)]
|
271
|
+
object2 = [SampleClass.new(4), SampleClass.new(2), SampleClass.new(5)]
|
272
|
+
object3 = [SampleClass.new(7), SampleClass.new(2), SampleClass.new(8)]
|
273
|
+
|
274
|
+
@coffee_table.get_cache(:first_key, object1) do
|
275
|
+
"object1"
|
276
|
+
end
|
277
|
+
@coffee_table.get_cache(:second_key, object2) do
|
278
|
+
"object2"
|
279
|
+
end
|
280
|
+
@coffee_table.get_cache(:third_key, object3) do
|
281
|
+
"object3"
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
it "should expire based on the initial key" do
|
286
|
+
@coffee_table.keys.count.should == 3
|
287
|
+
@coffee_table.expire_for(:second_key)
|
288
|
+
@coffee_table.keys.count.should == 2
|
289
|
+
end
|
290
|
+
|
291
|
+
it "should expire based on a simple string" do
|
292
|
+
@coffee_table.keys.count.should == 3
|
293
|
+
@coffee_table.expire_for("sample_class[4]")
|
294
|
+
@coffee_table.keys.count.should == 2
|
295
|
+
end
|
296
|
+
|
297
|
+
it "should not expire based on a part match" do
|
298
|
+
@coffee_table.keys.count.should == 3
|
299
|
+
@coffee_table.expire_for("impl")
|
300
|
+
@coffee_table.keys.count.should == 3
|
301
|
+
end
|
302
|
+
|
303
|
+
it "should not delete any keys if object is not present" do
|
304
|
+
@coffee_table.keys.count.should == 3
|
305
|
+
@coffee_table.expire_for(SampleClass.new(18))
|
306
|
+
@coffee_table.keys.count.should == 3
|
307
|
+
end
|
308
|
+
it "should only delete keys that object is present in" do
|
309
|
+
@coffee_table.keys.count.should == 3
|
310
|
+
@coffee_table.expire_for(SampleClass.new(1))
|
311
|
+
@coffee_table.keys.count.should == 2
|
312
|
+
end
|
313
|
+
|
314
|
+
it "should delete a key if the object is at the end of they key" do
|
315
|
+
@coffee_table.keys.count.should == 3
|
316
|
+
@coffee_table.expire_for(SampleClass.new(3))
|
317
|
+
@coffee_table.keys.count.should == 2
|
318
|
+
end
|
319
|
+
|
320
|
+
it "should expire all keys relating to a class if uninitialised class is passed in" do
|
321
|
+
@coffee_table.get_cache(:fourth_key) do
|
322
|
+
"object4"
|
323
|
+
end
|
324
|
+
@coffee_table.keys.count.should == 4
|
325
|
+
@coffee_table.expire_for(SampleClass)
|
326
|
+
@coffee_table.keys.count.should == 1
|
327
|
+
end
|
115
328
|
end
|
116
329
|
|
117
330
|
end
|
data/spec/lib/sample_class.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -6,6 +6,8 @@ require File.expand_path(File.dirname(__FILE__) + '/../../coffee_table/spec/lib/
|
|
6
6
|
|
7
7
|
Spork.prefork do
|
8
8
|
require File.expand_path(File.dirname(__FILE__) + '/../../coffee_table/lib/coffee_table.rb')
|
9
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../coffee_table/lib/coffee_table/coffee_table_block_missing_error.rb')
|
10
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../coffee_table/lib/coffee_table/coffee_table_invalid_object_error.rb')
|
9
11
|
end
|
10
12
|
|
11
13
|
Spork.each_run do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coffee_table
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,52 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
13
|
-
dependencies:
|
12
|
+
date: 2013-01-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70181746090020 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70181746090020
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: redis
|
27
|
+
requirement: &70181746089420 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70181746089420
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rufus-scheduler
|
38
|
+
requirement: &70181746088880 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70181746088880
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: activesupport
|
49
|
+
requirement: &70181746088280 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *70181746088280
|
14
58
|
description: rails cache gem to fragment cache with smart cache key management
|
15
59
|
email:
|
16
60
|
- stewart@theizone.co.uk
|
@@ -21,10 +65,15 @@ files:
|
|
21
65
|
- .gitignore
|
22
66
|
- .rspec
|
23
67
|
- .rvmrc
|
68
|
+
- .travis.yml
|
24
69
|
- Gemfile
|
70
|
+
- Gemfile.lock
|
71
|
+
- README.textile
|
25
72
|
- Rakefile
|
26
73
|
- coffee_table.gemspec
|
27
74
|
- lib/coffee_table.rb
|
75
|
+
- lib/coffee_table/coffee_table_block_missing_error.rb
|
76
|
+
- lib/coffee_table/coffee_table_invalid_object_error.rb
|
28
77
|
- lib/coffee_table/version.rb
|
29
78
|
- lib/utility.rb
|
30
79
|
- spec/lib/coffee_table_spec.rb
|