cachy 0.1.4 → 0.1.5

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.
@@ -8,7 +8,7 @@ Caching library to simplify and organize caching.
8
8
  - Global cache_version (expire everything Cachy cached, but not e.g. sessions)
9
9
  - ...
10
10
  - works out of the box with Rails
11
- - works with pure Memcache and [Moneta](http://github.com/wycats/moneta/tree/master)(-> Tokyo Cabinet / CouchDB / S3 / Berkeley DB / DataMapper / Memory store)
11
+ - works with pure Memcache, Redis and [Moneta](http://github.com/wycats/moneta/tree/master)(-> Tokyo Cabinet / CouchDB / S3 / Berkeley DB / DataMapper / Memory store)
12
12
 
13
13
  Install
14
14
  =======
@@ -104,6 +104,9 @@ Give me something that responds to read/write(Rails style) or []/store([Moneta](
104
104
  No I18n.available_locales ?
105
105
  Cachy.locales = [:de, :en, :fr]
106
106
 
107
+ ### Memcache timeout protection
108
+ If Memcache timeouts keep killing your pages -> [catch MemCache timeouts](http://github.com/grosser/cachy/blob/master/lib/cachy/memcache_timeout_protection)
109
+
107
110
  TODO
108
111
  ====
109
112
  - optionally store dependent keys (:keys=>xxx), so that they can be setup up once and do not need to be remembered
@@ -114,6 +117,6 @@ Authors
114
117
  ###Contributors
115
118
  - [mindreframer](http://www.simplewebapp.de/roman)
116
119
 
117
- [Michael Grosser](http://pragmatig.wordpress.com)
120
+ [Michael Grosser](http://grosser.it)
118
121
  grosser.michael@gmail.com
119
- Hereby placed under public domain, do what you want, just do not hold me accountable...
122
+ Hereby placed under public domain, do what you want, just do not hold me accountable...
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.4
1
+ 0.1.5
@@ -5,26 +5,26 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{cachy}
8
- s.version = "0.1.4"
8
+ s.version = "0.1.5"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Michael Grosser"]
12
- s.date = %q{2010-08-14}
12
+ s.date = %q{2010-10-29}
13
13
  s.email = %q{grosser.michael@gmail.com}
14
- s.extra_rdoc_files = [
15
- "README.markdown"
16
- ]
17
14
  s.files = [
18
- "README.markdown",
19
- "Rakefile",
15
+ "Rakefile",
16
+ "Readme.md",
20
17
  "VERSION",
21
18
  "cachy.gemspec",
22
19
  "lib/cachy.rb",
20
+ "lib/cachy/memcache_timeout_protection.rb",
23
21
  "lib/cachy/memcached_wrapper.rb",
24
22
  "lib/cachy/moneta_wrapper.rb",
23
+ "lib/cachy/redis_wrapper.rb",
25
24
  "lib/cachy/wrapper.rb",
26
25
  "spec/cachy/memcached_wrapper_spec.rb",
27
26
  "spec/cachy/moneta_wrapper_spec.rb",
27
+ "spec/cachy/redis_wrapper_spec.rb",
28
28
  "spec/cachy_spec.rb",
29
29
  "spec/spec_helper.rb",
30
30
  "spec/test_cache.rb"
@@ -37,6 +37,7 @@ Gem::Specification.new do |s|
37
37
  s.test_files = [
38
38
  "spec/cachy/memcached_wrapper_spec.rb",
39
39
  "spec/cachy/moneta_wrapper_spec.rb",
40
+ "spec/cachy/redis_wrapper_spec.rb",
40
41
  "spec/cachy_spec.rb",
41
42
  "spec/test_cache.rb",
42
43
  "spec/spec_helper.rb"
@@ -116,18 +116,8 @@ class Cachy
116
116
 
117
117
  # Wrap non ActiveSupport style cache stores,
118
118
  # to get the same interface for all
119
- def self.cache_store=(x)
120
- if x.respond_to? :read and x.respond_to? :write
121
- @cache_store=x
122
- elsif x.respond_to? "[]" and x.respond_to? :set
123
- require 'cachy/memcached_wrapper'
124
- @cache_store = MemcachedWrapper.new(x)
125
- elsif x.respond_to? "[]" and x.respond_to? :store
126
- require 'cachy/moneta_wrapper'
127
- @cache_store = MonetaWrapper.new(x)
128
- else
129
- raise "This cache_store type is not usable for Cachy!"
130
- end
119
+ def self.cache_store=(cache)
120
+ @cache_store = wrap_cache(cache)
131
121
  @cache_store.write HEALTH_CHECK_KEY, 'yes'
132
122
  end
133
123
 
@@ -135,7 +125,13 @@ class Cachy
135
125
  @cache_store || raise("Use: Cachy.cache_store = your_cache_store")
136
126
  end
137
127
 
138
- self.cache_store = ActionController::Base.cache_store if defined? ActionController::Base
128
+ def self.key_versions_cache_store=(cache)
129
+ @key_versions_cache_store = wrap_cache(cache)
130
+ end
131
+
132
+ def self.key_versions_cache_store
133
+ @key_versions_cache_store || cache_store
134
+ end
139
135
 
140
136
  # locales
141
137
  @@locales = nil
@@ -154,19 +150,39 @@ class Cachy
154
150
 
155
151
  private
156
152
 
157
- def self.read_versions
158
- data = cache_store.instance_variable_get('@data')
159
- if data.respond_to? :read_error_occured # its a MemCache with read_error detection !
160
- result = data[KEY_VERSIONS_KEY] || {}
161
- @@cache_error = data.read_error_occured
162
- result
153
+ def self.wrap_cache(cache)
154
+ if cache.respond_to? :read and cache.respond_to? :write
155
+ cache
156
+ elsif cache.class.to_s == 'Redis'
157
+ require 'cachy/redis_wrapper'
158
+ RedisWrapper.new(cache)
159
+ elsif cache.respond_to? "[]" and cache.respond_to? :set
160
+ require 'cachy/memcached_wrapper'
161
+ MemcachedWrapper.new(cache)
162
+ elsif cache.respond_to? "[]" and cache.respond_to? :store
163
+ require 'cachy/moneta_wrapper'
164
+ MonetaWrapper.new(cache)
163
165
  else
164
- cache_store.read(KEY_VERSIONS_KEY) || {}
166
+ raise "This cache_store type is not usable for Cachy!"
165
167
  end
166
168
  end
167
169
 
170
+ def self.read_versions
171
+ store = key_versions_cache_store
172
+ result = store.read(KEY_VERSIONS_KEY) || {}
173
+ detect_cache_error(store)
174
+ result
175
+ end
176
+
168
177
  def self.write_version(data)
169
- cache_store.write(KEY_VERSIONS_KEY, data) unless @@cache_error
178
+ key_versions_cache_store.write(KEY_VERSIONS_KEY, data) unless @@cache_error
179
+ end
180
+
181
+ def self.detect_cache_error(store)
182
+ data = store.instance_variable_get('@data')
183
+ if data.respond_to? :read_error_occurred
184
+ @@cache_error = data.read_error_occurred
185
+ end
170
186
  end
171
187
 
172
188
  def self.cache_healthy?
@@ -233,4 +249,6 @@ class Cachy
233
249
  {}
234
250
  end
235
251
  end
236
- end
252
+ end
253
+
254
+ Cachy.cache_store = ActionController::Base.cache_store if defined? ActionController::Base
@@ -0,0 +1,30 @@
1
+ # do not let MemCache timeouts kill your app,
2
+ # mark as error and return read_error_callback (e.g. nil -> cache miss)
3
+ require 'memcache'
4
+
5
+ class MemCache
6
+ # Add your callback to stop timeouts from raising
7
+ #
8
+ # MemCache.read_error_callback = lambda{|error|
9
+ # error.message << ' -- catched'
10
+ # HoptoadNotifier.notify error
11
+ # nil
12
+ # }
13
+
14
+ cattr_accessor :read_error_callback, :read_error_occurred
15
+
16
+ def cache_get_with_timeout_protection(*args)
17
+ begin
18
+ @read_error_occurred = false
19
+ cache_get_without_timeout_protection(*args)
20
+ rescue Exception => error
21
+ @read_error_occurred = true
22
+ if error.to_s == 'IO timeout' and self.class.read_error_callback
23
+ self.class.read_error_callback.call error
24
+ else
25
+ raise error
26
+ end
27
+ end
28
+ end
29
+ alias_method_chain :cache_get, :timeout_protection
30
+ end
@@ -1,6 +1,5 @@
1
1
  require 'cachy/wrapper'
2
2
 
3
- # Wrapper for Memcached
4
3
  class Cachy::MemcachedWrapper < Cachy::Wrapper
5
4
  def write(key, result, options={})
6
5
  @wrapped.set(key, result, options[:expires_in].to_i)
@@ -0,0 +1,20 @@
1
+ require 'cachy/wrapper'
2
+ require 'yaml'
3
+
4
+ class Cachy::RedisWrapper < Cachy::Wrapper
5
+ def read(key)
6
+ result = @wrapped[key]
7
+ return if result.nil?
8
+ YAML.load(result)
9
+ end
10
+
11
+ def write(key, value, options={})
12
+ result = @wrapped.set(key, value.to_yaml)
13
+ @wrapped.expire(key, options[:expires_in].to_i) if options[:expires_in]
14
+ result
15
+ end
16
+
17
+ def delete(key)
18
+ @wrapped.del(key)
19
+ end
20
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec/spec_helper'
2
+
3
+ class TestRedis
4
+ def self.to_s
5
+ 'Redis'
6
+ end
7
+
8
+ def initialize
9
+ @wrapped = {}
10
+ end
11
+
12
+ def set(key, object)
13
+ @wrapped[key] = object
14
+ end
15
+
16
+ def expire(key, seconds)
17
+ end
18
+
19
+ def [](x)
20
+ @wrapped[x]
21
+ end
22
+
23
+ def clear
24
+ @wrapped.clear
25
+ end
26
+
27
+ def del(key)
28
+ @wrapped.delete(key)
29
+ end
30
+ end
31
+
32
+ describe "Cachy::RedisWrapper" do
33
+ before :all do
34
+ @cache = TestRedis.new
35
+ Cachy.cache_store = @cache
36
+ end
37
+
38
+ before do
39
+ @cache.clear
40
+ end
41
+
42
+ it "is wrapped" do
43
+ Cachy.cache_store.class.should == Cachy::RedisWrapper
44
+ end
45
+
46
+ it "can cache" do
47
+ Cachy.cache(:x){ 'SUCCESS' }
48
+ Cachy.cache(:x){ 'FAIL' }.should == 'SUCCESS'
49
+ end
50
+
51
+ it "can cache without expires" do
52
+ @cache.should_receive(:set).with('x_v1', "--- SUCCESS\n")
53
+ @cache.should_not_receive(:expire)
54
+ Cachy.cache(:x){ 'SUCCESS' }.should == 'SUCCESS'
55
+ end
56
+
57
+ it "can cache with expires" do
58
+ @cache.should_receive(:set).with('x_v1', "--- SUCCESS\n")
59
+ @cache.should_receive(:expire).with('x_v1', 1)
60
+ Cachy.cache(:x, :expires_in=>1){ 'SUCCESS' }.should == 'SUCCESS'
61
+ end
62
+
63
+ it "can expire" do
64
+ @cache.should_receive(:del).with('x_v1')
65
+ Cachy.expire(:x)
66
+ end
67
+ end
@@ -285,7 +285,7 @@ describe Cachy do
285
285
  before do
286
286
  @mock = mock = {}
287
287
  @cache.instance_eval{@data = mock}
288
- def @mock.read_error_occured
288
+ def @mock.read_error_occurred
289
289
  false
290
290
  end
291
291
  def @mock.[](x)
@@ -300,13 +300,13 @@ describe Cachy do
300
300
 
301
301
  it "reads empty when it crashes" do
302
302
  @mock.should_receive(:[]).and_return nil # e.g. Timout happended
303
- @mock.should_receive(:read_error_occured).and_return true
303
+ @mock.should_receive(:read_error_occurred).and_return true
304
304
  Cachy.send(:read_versions).should == {}
305
305
  end
306
306
 
307
307
  it "marks as error when it crashes" do
308
308
  Cachy.send(:class_variable_get, '@@cache_error').should == false
309
- @mock.should_receive(:read_error_occured).and_return true
309
+ @mock.should_receive(:read_error_occurred).and_return true
310
310
  Cachy.send(:read_versions)
311
311
  Cachy.send(:class_variable_get, '@@cache_error').should == true
312
312
  end
@@ -330,6 +330,23 @@ describe Cachy do
330
330
  end
331
331
  end
332
332
 
333
+ describe :key_versions, 'with separate store' do
334
+ let(:key_cache){ TestCache.new }
335
+
336
+ it "reads from separate store" do
337
+ key_cache.write(Cachy::KEY_VERSIONS_KEY, :x => 1)
338
+ Cachy.key_versions_cache_store = key_cache
339
+ Cachy.key_versions.should == {:x => 1}
340
+ end
341
+
342
+ it "writes to separate store" do
343
+ key_cache.write(Cachy::KEY_VERSIONS_KEY, :x => 1)
344
+ Cachy.key_versions_cache_store = key_cache
345
+ Cachy.increment_key :x
346
+ Cachy.key_versions.should == {:x => 2}
347
+ end
348
+ end
349
+
333
350
  describe :delete_key do
334
351
  it "removes a key from key versions" do
335
352
  Cachy.cache(:xxx){1}
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 4
9
- version: 0.1.4
8
+ - 5
9
+ version: 0.1.5
10
10
  platform: ruby
11
11
  authors:
12
12
  - Michael Grosser
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-14 00:00:00 +02:00
17
+ date: 2010-10-29 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
@@ -24,19 +24,22 @@ executables: []
24
24
 
25
25
  extensions: []
26
26
 
27
- extra_rdoc_files:
28
- - README.markdown
27
+ extra_rdoc_files: []
28
+
29
29
  files:
30
- - README.markdown
31
30
  - Rakefile
31
+ - Readme.md
32
32
  - VERSION
33
33
  - cachy.gemspec
34
34
  - lib/cachy.rb
35
+ - lib/cachy/memcache_timeout_protection.rb
35
36
  - lib/cachy/memcached_wrapper.rb
36
37
  - lib/cachy/moneta_wrapper.rb
38
+ - lib/cachy/redis_wrapper.rb
37
39
  - lib/cachy/wrapper.rb
38
40
  - spec/cachy/memcached_wrapper_spec.rb
39
41
  - spec/cachy/moneta_wrapper_spec.rb
42
+ - spec/cachy/redis_wrapper_spec.rb
40
43
  - spec/cachy_spec.rb
41
44
  - spec/spec_helper.rb
42
45
  - spec/test_cache.rb
@@ -73,6 +76,7 @@ summary: Caching library for projects that have many processes or many caches
73
76
  test_files:
74
77
  - spec/cachy/memcached_wrapper_spec.rb
75
78
  - spec/cachy/moneta_wrapper_spec.rb
79
+ - spec/cachy/redis_wrapper_spec.rb
76
80
  - spec/cachy_spec.rb
77
81
  - spec/test_cache.rb
78
82
  - spec/spec_helper.rb