-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ExpiringMemoryStore inherits from Store, not from MemoryStore (hopefully that's alright)
- Loading branch information
0 parents
commit c1c2dea
Showing
6 changed files
with
253 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
Copyright (c) 2008 [Matthew Rudy Jacobs] | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining | ||
a copy of this software and associated documentation files (the | ||
"Software"), to deal in the Software without restriction, including | ||
without limitation the rights to use, copy, modify, merge, publish, | ||
distribute, sublicense, and/or sell copies of the Software, and to | ||
permit persons to whom the Software is furnished to do so, subject to | ||
the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be | ||
included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
ExpiringMemoryStore | ||
=================== | ||
|
||
Expiring MemoryStore is a modification to the default Rails MemoryStore which adds the :expires_in functionality that is so useful with Memcache. | ||
Although this increases the memory overhead slightly, | ||
it ensures that caches can be expired easily, without restarting your webserver. | ||
|
||
Just add; | ||
config.cache_store = :expiring_memory_store | ||
to your environment.rb. | ||
|
||
Example | ||
======= | ||
|
||
>> Rails.cache.write('are you there?', :im_still_here, :expires_in => 30) | ||
=> :im_still_here | ||
|
||
>> Rails.cache.read('are you there?') | ||
=> :im_still_here | ||
|
||
wait 30 seconds | ||
|
||
>> Rails.cache.read('are you there?') | ||
=> nil | ||
|
||
WICKED!!! | ||
|
||
Copyright (c) 2008 [Matthew Rudy Jacobs], released under the MIT license |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
require 'rake' | ||
require 'rake/testtask' | ||
require 'rake/rdoctask' | ||
|
||
desc 'Default: run unit tests.' | ||
task :default => :test | ||
|
||
desc 'Test the expiring_memory_store plugin.' | ||
Rake::TestTask.new(:test) do |t| | ||
t.libs << 'lib' | ||
t.pattern = 'test/**/*_test.rb' | ||
t.verbose = true | ||
end | ||
|
||
desc 'Generate documentation for the expiring_memory_store plugin.' | ||
Rake::RDocTask.new(:rdoc) do |rdoc| | ||
rdoc.rdoc_dir = 'rdoc' | ||
rdoc.title = 'ExpiringMemoryStore' | ||
rdoc.options << '--line-numbers' << '--inline-source' | ||
rdoc.rdoc_files.include('README') | ||
rdoc.rdoc_files.include('lib/**/*.rb') | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
require 'active_support/cache/expiring_memory_store' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
require 'activesupport' | ||
module ActiveSupport | ||
module Cache | ||
# Like MemoryStore, but caches are expired after the period specified in the :expires_in option. | ||
class ExpiringMemoryStore < Store | ||
def initialize | ||
@data = {} | ||
end | ||
|
||
def read(name, options = nil) | ||
super | ||
value, expiry = @data[name] | ||
if expiry && expiry < Time.now | ||
delete(name) | ||
return nil | ||
end | ||
return value | ||
end | ||
|
||
def write(name, value, options = nil) | ||
super | ||
@data[name] = [value.freeze, expires_at(options)].freeze | ||
return value | ||
end | ||
|
||
def delete(name, options = nil) | ||
super | ||
@data.delete(name) | ||
end | ||
|
||
def delete_matched(matcher, options = nil) | ||
super | ||
@data.delete_if { |k,v| k =~ matcher } | ||
end | ||
|
||
def exist?(name,options = nil) | ||
super | ||
@data.has_key?(name) | ||
end | ||
|
||
def clear | ||
@data.clear | ||
end | ||
|
||
private | ||
|
||
def expires_at(options) | ||
if expires_in = options && options[:expires_in] | ||
return expires_in.from_now if expires_in != 0 | ||
end | ||
return nil | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
require 'rubygems' | ||
require 'test/unit' | ||
require 'mocha' | ||
require File.dirname(__FILE__) + '/../init' | ||
|
||
class CacheStoreTest < Test::Unit::TestCase | ||
def setup | ||
@cache = ActiveSupport::Cache.lookup_store(:expiring_memory_store) | ||
end | ||
|
||
def test_fetch_without_cache_miss | ||
@cache.stubs(:read).with('foo', {}).returns('bar') | ||
@cache.expects(:write).never | ||
assert_equal 'bar', @cache.fetch('foo') { 'baz' } | ||
end | ||
|
||
def test_fetch_with_cache_miss | ||
@cache.stubs(:read).with('foo', {}).returns(nil) | ||
@cache.expects(:write).with('foo', 'baz', {}) | ||
assert_equal 'baz', @cache.fetch('foo') { 'baz' } | ||
end | ||
|
||
def test_fetch_with_forced_cache_miss | ||
@cache.expects(:read).never | ||
@cache.expects(:write).with('foo', 'bar', :force => true) | ||
@cache.fetch('foo', :force => true) { 'bar' } | ||
end | ||
end | ||
|
||
# Tests the base functionality that should be identical across all cache stores. | ||
module CacheStoreBehavior | ||
def test_should_read_and_write_strings | ||
@cache.write('foo', 'bar') | ||
assert_equal 'bar', @cache.read('foo') | ||
end | ||
|
||
def test_should_read_and_write_hash | ||
@cache.write('foo', {:a => "b"}) | ||
assert_equal({:a => "b"}, @cache.read('foo')) | ||
end | ||
|
||
def test_should_read_and_write_nil | ||
@cache.write('foo', nil) | ||
assert_equal nil, @cache.read('foo') | ||
end | ||
|
||
def test_fetch_without_cache_miss | ||
@cache.write('foo', 'bar') | ||
assert_equal 'bar', @cache.fetch('foo') { 'baz' } | ||
end | ||
|
||
def test_fetch_with_cache_miss | ||
assert_equal 'baz', @cache.fetch('foo') { 'baz' } | ||
end | ||
|
||
def test_fetch_with_forced_cache_miss | ||
@cache.fetch('foo', :force => true) { 'bar' } | ||
end | ||
|
||
def test_increment | ||
@cache.write('foo', 1, :raw => true) | ||
assert_equal 1, @cache.read('foo', :raw => true).to_i | ||
assert_equal 2, @cache.increment('foo') | ||
assert_equal 2, @cache.read('foo', :raw => true).to_i | ||
assert_equal 3, @cache.increment('foo') | ||
assert_equal 3, @cache.read('foo', :raw => true).to_i | ||
end | ||
|
||
def test_decrement | ||
@cache.write('foo', 3, :raw => true) | ||
assert_equal 3, @cache.read('foo', :raw => true).to_i | ||
assert_equal 2, @cache.decrement('foo') | ||
assert_equal 2, @cache.read('foo', :raw => true).to_i | ||
assert_equal 1, @cache.decrement('foo') | ||
assert_equal 1, @cache.read('foo', :raw => true).to_i | ||
end | ||
end | ||
|
||
class ExpiringMemoryStoreTest < Test::Unit::TestCase | ||
def setup | ||
@cache = ActiveSupport::Cache.lookup_store(:expiring_memory_store) | ||
end | ||
|
||
include CacheStoreBehavior | ||
|
||
def test_store_objects_should_be_immutable | ||
@cache.write('foo', 'bar') | ||
assert_raise(ActiveSupport::FrozenObjectError) { @cache.read('foo').gsub!(/.*/, 'baz') } | ||
assert_equal 'bar', @cache.read('foo') | ||
end | ||
|
||
def test_by_default_values_should_not_expire | ||
time_now = Time.now | ||
Time.stubs(:now).returns(time_now) | ||
|
||
@cache.write('foo', 'bar') | ||
assert_equal 'bar', @cache.read('foo') | ||
|
||
Time.stubs(:now).returns(time_now + 5.years ) | ||
assert_equal 'bar', @cache.read('foo') | ||
end | ||
|
||
def test_values_should_expire_is_param_is_set | ||
time_now = Time.now | ||
Time.stubs(:now).returns(time_now) | ||
|
||
@cache.write('foo', 'bar', :expires_in => 1.year) | ||
assert_equal 'bar', @cache.read('foo') | ||
|
||
Time.stubs(:now).returns(time_now + 5.years ) | ||
assert_equal nil, @cache.read('foo') | ||
end | ||
|
||
def test_values_should_expire_when_the_param_tells_it_to | ||
time_now = Time.now | ||
Time.stubs(:now).returns(time_now) | ||
|
||
@cache.write('foo', 'bar', :expires_in => 1.year) | ||
assert_equal 'bar', @cache.read('foo') | ||
|
||
Time.stubs(:now).returns(time_now + 1.year - 1 ) | ||
assert_equal 'bar', @cache.read('foo') | ||
|
||
Time.stubs(:now).returns(time_now + 1.year + 1 ) | ||
assert_equal nil, @cache.read('foo') | ||
end | ||
end |