rails-cache-tags 1.0.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.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +54 -0
- data/Rakefile +10 -0
- data/lib/rails-cache-tags.rb +27 -0
- data/lib/rails/cache/tag.rb +60 -0
- data/lib/rails/cache/tags/store.rb +78 -0
- data/lib/rails/cache/tags/version.rb +7 -0
- data/rails-cache-tags.gemspec +25 -0
- data/test/cache_tags_test.rb +80 -0
- data/test/caching_test.rb +846 -0
- data/test/test_helper.rb +28 -0
- metadata +138 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 amikhailov
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# Synopsys
|
2
|
+
|
3
|
+
Tagged caching support within your Rails application.
|
4
|
+
|
5
|
+
# Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'rails-cache-tags'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install rails-cache-tags
|
18
|
+
|
19
|
+
# Usage
|
20
|
+
|
21
|
+
Anywhere:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
cache = Rails.cache
|
25
|
+
|
26
|
+
cache.write "foo", "bar", :tags => %w(baz foo)
|
27
|
+
cache.read "foo" # => "bar"
|
28
|
+
|
29
|
+
cache.delete_tag "baz"
|
30
|
+
cache.read "foo" => nil
|
31
|
+
```
|
32
|
+
|
33
|
+
In your controller:
|
34
|
+
```ruby
|
35
|
+
class PostController < ActionController::Base
|
36
|
+
def update
|
37
|
+
@post = Post.find_by_id(params[:id])
|
38
|
+
|
39
|
+
if @post.update_attributes(params)
|
40
|
+
expire_fragments_by_tags :post => @post.id
|
41
|
+
else
|
42
|
+
render :edit
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
# Contributing
|
49
|
+
|
50
|
+
1. Fork it
|
51
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
52
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
53
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
54
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "active_support/cache"
|
2
|
+
|
3
|
+
require "rails/cache/tag"
|
4
|
+
require "rails/cache/tags/store"
|
5
|
+
|
6
|
+
module ActiveSupport
|
7
|
+
module Cache
|
8
|
+
class Store
|
9
|
+
extend Rails::Cache::Tags::Store
|
10
|
+
end
|
11
|
+
|
12
|
+
class Entry
|
13
|
+
attr_accessor :tags
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
if defined?(ActionController::Base)
|
19
|
+
class ActionController::Base < ActionController::Metal
|
20
|
+
def expire_fragments_by_tags *args
|
21
|
+
return unless cache_configured?
|
22
|
+
|
23
|
+
cache_store.delete_tag *args
|
24
|
+
end
|
25
|
+
alias expire_fragments_by_tag expire_fragments_by_tags
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Rails
|
2
|
+
module Cache
|
3
|
+
class Tag #:nodoc:all:
|
4
|
+
KEY_PREFIX = '_tags'
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# {:post => ['1', '2', '3']} => [Tag(post:1), Tag(post:2), Tag(post:3)]
|
8
|
+
# {:post => 1, :user => 2} => [Tag(post:1), Tag(user:2)]
|
9
|
+
# ['post:1', 'post:2', 'post:3'] => [Tag(post:1), Tag(post:2), Tag(post:3)]
|
10
|
+
def build_tags(names)
|
11
|
+
case names
|
12
|
+
when NilClass then nil
|
13
|
+
when Hash then names.map do |key, value|
|
14
|
+
Array.wrap(value).map { |v| new([key, v]) }
|
15
|
+
end.flatten
|
16
|
+
when Enumerable then names.map { |v| build_tags(v) }.flatten
|
17
|
+
when self then names
|
18
|
+
else [new(names)]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :name
|
24
|
+
|
25
|
+
# Tag constructor, accepts String, Symbol and Array
|
26
|
+
def initialize(name)
|
27
|
+
@name = case name
|
28
|
+
when String, Symbol then name
|
29
|
+
when Array then name.join(':')
|
30
|
+
else raise ArgumentError
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# real cache key
|
35
|
+
def to_key
|
36
|
+
[KEY_PREFIX, name].join('/')
|
37
|
+
end
|
38
|
+
|
39
|
+
# read tag's version from +store+
|
40
|
+
def fetch(store)
|
41
|
+
store.read(to_key)
|
42
|
+
end
|
43
|
+
|
44
|
+
# increment tag's version inside +store+
|
45
|
+
def increment(store)
|
46
|
+
current = fetch(store)
|
47
|
+
|
48
|
+
version = if current.is_a?(Fixnum)
|
49
|
+
current + 1
|
50
|
+
else
|
51
|
+
1
|
52
|
+
end
|
53
|
+
|
54
|
+
store.write(to_key, version, :expires_in => nil)
|
55
|
+
|
56
|
+
version
|
57
|
+
end
|
58
|
+
end # class Tag
|
59
|
+
end # module Cache
|
60
|
+
end # module Rails
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Rails
|
2
|
+
module Cache
|
3
|
+
module Tags
|
4
|
+
module Store
|
5
|
+
# patched +new+ method
|
6
|
+
def new(*args, &block) #:nodoc:
|
7
|
+
unless acts_like?(:cached_tags)
|
8
|
+
extend ClassMethods
|
9
|
+
include InstanceMethods
|
10
|
+
|
11
|
+
alias_method_chain :read_entry, :tags
|
12
|
+
alias_method_chain :write_entry, :tags
|
13
|
+
end
|
14
|
+
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods #:nodoc:all:
|
19
|
+
def acts_like_cached_tags?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
# Increment the version of tags, so all entries referring to the tags become invalid
|
25
|
+
def delete_tag *names
|
26
|
+
tags = Rails::Cache::Tag.build_tags(names)
|
27
|
+
|
28
|
+
tags.each { |tag| tag.increment(self) } unless tags.empty?
|
29
|
+
end
|
30
|
+
alias delete_by_tag delete_tag
|
31
|
+
alias delete_by_tags delete_tag
|
32
|
+
alias expire_tag delete_tag
|
33
|
+
|
34
|
+
protected
|
35
|
+
def read_entry_with_tags(key, options) #:nodoc
|
36
|
+
entry = read_entry_without_tags(key, options)
|
37
|
+
|
38
|
+
if entry && entry.tags.present?
|
39
|
+
current_versions = fetch_tags(entry.tags.keys)
|
40
|
+
saved_versions = entry.tags.values
|
41
|
+
|
42
|
+
if current_versions != saved_versions
|
43
|
+
delete_entry(key, options)
|
44
|
+
|
45
|
+
return nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
entry
|
50
|
+
end # def read_entry_with_tags
|
51
|
+
|
52
|
+
def write_entry_with_tags(key, entry, options) #:nodoc:
|
53
|
+
tags = Rails::Cache::Tag.build_tags Array.wrap(options[:tags]).flatten.compact
|
54
|
+
|
55
|
+
unless tags.empty?
|
56
|
+
current_versions = fetch_tags(tags) # => [1, 2, 3]
|
57
|
+
entry.tags = Hash[tags.zip(current_versions).map { |tag, v| [tag.name, v || tag.increment(self)] }]
|
58
|
+
end
|
59
|
+
|
60
|
+
write_entry_without_tags(key, entry, options)
|
61
|
+
end # def write_entry_without_tags
|
62
|
+
|
63
|
+
private
|
64
|
+
# fetch tags versions from store
|
65
|
+
# fetch ['user:1', 'post:2', 'country:2'] => [3, 4, nil]
|
66
|
+
def fetch_tags(names) #:nodoc:
|
67
|
+
tags = Rails::Cache::Tag.build_tags names
|
68
|
+
keys = tags.map(&:to_key)
|
69
|
+
stored = read_multi(*keys)
|
70
|
+
|
71
|
+
# we should save order
|
72
|
+
keys.collect { |k| stored[k] }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end # module Store
|
76
|
+
end # module Tags
|
77
|
+
end # module Cache
|
78
|
+
end # module Rails
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/rails/cache/tags/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Alexei Mikhailov"]
|
6
|
+
gem.email = %W(amikhailov83@gmail.com)
|
7
|
+
gem.description = %q{Tagged caching support for Rails}
|
8
|
+
gem.summary = %q{Tagged caching support for Rails}
|
9
|
+
gem.homepage = "https://github.com/take-five/rails-cache-tags"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
13
|
+
gem.name = "rails-cache-tags"
|
14
|
+
gem.require_paths = %W(lib)
|
15
|
+
gem.version = Rails::Cache::Tags::VERSION
|
16
|
+
|
17
|
+
gem.add_dependency "activesupport", ">= 3.0"
|
18
|
+
gem.add_dependency "actionpack", ">= 3.0"
|
19
|
+
|
20
|
+
gem.add_development_dependency 'minitest', '~> 3.2'
|
21
|
+
gem.add_development_dependency 'memcache-client'
|
22
|
+
gem.add_development_dependency 'rack'
|
23
|
+
gem.add_development_dependency 'mocha'
|
24
|
+
gem.add_development_dependency 'simplecov'
|
25
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require "test_helper"
|
4
|
+
require "caching_test"
|
5
|
+
|
6
|
+
module CacheTagsBehavior
|
7
|
+
def test_read_and_write_with_tags
|
8
|
+
@cache.write("foo", "bar", :tags => "baz")
|
9
|
+
assert_equal 'bar', @cache.read('foo')
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_read_after_tag_deleted
|
13
|
+
@cache.write("foo", "bar", :tags => "baz")
|
14
|
+
@cache.delete_tag("baz")
|
15
|
+
|
16
|
+
assert_nil @cache.read("foo")
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_read_after_another_tag_deleted
|
20
|
+
@cache.write("foo", "bar", :tags => "baz")
|
21
|
+
@cache.delete_tag("fu")
|
22
|
+
|
23
|
+
assert_equal 'bar', @cache.read('foo')
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_read_and_write_with_multiple_tags
|
27
|
+
@cache.write("foo", "bar", :tags => [:baz, :kung])
|
28
|
+
assert_equal 'bar', @cache.read('foo')
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_read_after_one_of_tags_deleted
|
32
|
+
@cache.write("foo", "bar", :tags => [:baz, :kung])
|
33
|
+
@cache.delete_tag :kung
|
34
|
+
|
35
|
+
assert_nil @cache.read("foo")
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_read_after_another_of_multiple_tags_deleted
|
39
|
+
@cache.write("foo", "bar", :tags => [:baz, :kung])
|
40
|
+
@cache.delete_tag("fu")
|
41
|
+
|
42
|
+
assert_equal 'bar', @cache.read('foo')
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_read_with_small_default_expiration_time
|
46
|
+
cache = if is_a?(FileStoreTest)
|
47
|
+
@cache.class.new @cache.cache_path, :expires_in => 0.001
|
48
|
+
else
|
49
|
+
@cache.class.new :expires_in => 0.001
|
50
|
+
end
|
51
|
+
|
52
|
+
cache.write("foo", "bar", :tags => "baz", :expires_in => 2)
|
53
|
+
sleep 0.02
|
54
|
+
|
55
|
+
assert_equal 'bar', cache.read('foo')
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_exists_with_tags
|
59
|
+
@cache.write("foo", "bar", :tags => "baz")
|
60
|
+
@cache.delete_tag("baz")
|
61
|
+
|
62
|
+
assert_equal @cache.exist?("foo"), false
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_read_and_write_with_tags_hash
|
66
|
+
@cache.write("foo", "bar", :tags => {:baz => 1})
|
67
|
+
assert_equal 'bar', @cache.read('foo')
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_read_and_write_with_tags_hash_after_expiration
|
71
|
+
@cache.write("foo", "bar", :tags => {:baz => 1})
|
72
|
+
@cache.delete_tag :baz => 1
|
73
|
+
|
74
|
+
assert_nil @cache.read('foo')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
[FileStoreTest, MemoryStoreTest, MemCacheStoreTest].each do |klass|
|
79
|
+
klass.send :include, CacheTagsBehavior
|
80
|
+
end
|
@@ -0,0 +1,846 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
class CacheKeyTest < ActiveSupport::TestCase
|
7
|
+
def test_expand_cache_key
|
8
|
+
assert_equal '1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true])
|
9
|
+
assert_equal 'name/1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true], :name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_expand_cache_key_with_rails_cache_id
|
13
|
+
begin
|
14
|
+
ENV['RAILS_CACHE_ID'] = 'c99'
|
15
|
+
assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo)
|
16
|
+
assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key([:foo])
|
17
|
+
assert_equal 'c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar])
|
18
|
+
assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key(:foo, :nm)
|
19
|
+
assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key([:foo], :nm)
|
20
|
+
assert_equal 'nm/c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar], :nm)
|
21
|
+
ensure
|
22
|
+
ENV['RAILS_CACHE_ID'] = nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_expand_cache_key_with_rails_app_version
|
27
|
+
begin
|
28
|
+
ENV['RAILS_APP_VERSION'] = 'rails3'
|
29
|
+
assert_equal 'rails3/foo', ActiveSupport::Cache.expand_cache_key(:foo)
|
30
|
+
ensure
|
31
|
+
ENV['RAILS_APP_VERSION'] = nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_expand_cache_key_rails_cache_id_should_win_over_rails_app_version
|
36
|
+
begin
|
37
|
+
ENV['RAILS_CACHE_ID'] = 'c99'
|
38
|
+
ENV['RAILS_APP_VERSION'] = 'rails3'
|
39
|
+
assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo)
|
40
|
+
ensure
|
41
|
+
ENV['RAILS_CACHE_ID'] = nil
|
42
|
+
ENV['RAILS_APP_VERSION'] = nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_expand_cache_key_respond_to_cache_key
|
47
|
+
key = 'foo'
|
48
|
+
def key.cache_key
|
49
|
+
:foo_key
|
50
|
+
end
|
51
|
+
assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key(key)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_expand_cache_key_array_with_something_that_responds_to_cache_key
|
55
|
+
key = 'foo'
|
56
|
+
def key.cache_key
|
57
|
+
:foo_key
|
58
|
+
end
|
59
|
+
assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key([key])
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_expand_cache_key_of_nil
|
63
|
+
assert_equal '', ActiveSupport::Cache.expand_cache_key(nil)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_expand_cache_key_of_false
|
67
|
+
assert_equal 'false', ActiveSupport::Cache.expand_cache_key(false)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_expand_cache_key_of_true
|
71
|
+
assert_equal 'true', ActiveSupport::Cache.expand_cache_key(true)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class CacheStoreSettingTest < ActiveSupport::TestCase
|
76
|
+
def test_file_fragment_cache_store
|
77
|
+
store = ActiveSupport::Cache.lookup_store :file_store, "/path/to/cache/directory"
|
78
|
+
assert_kind_of(ActiveSupport::Cache::FileStore, store)
|
79
|
+
assert_equal "/path/to/cache/directory", store.cache_path
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_mem_cache_fragment_cache_store
|
83
|
+
MemCache.expects(:new).with(%w[localhost], {})
|
84
|
+
store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost"
|
85
|
+
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_mem_cache_fragment_cache_store_with_given_mem_cache
|
89
|
+
mem_cache = MemCache.new
|
90
|
+
MemCache.expects(:new).never
|
91
|
+
store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache
|
92
|
+
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_mem_cache_fragment_cache_store_with_given_mem_cache_like_object
|
96
|
+
MemCache.expects(:new).never
|
97
|
+
memcache = Object.new
|
98
|
+
def memcache.get() true end
|
99
|
+
store = ActiveSupport::Cache.lookup_store :mem_cache_store, memcache
|
100
|
+
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_mem_cache_fragment_cache_store_with_multiple_servers
|
104
|
+
MemCache.expects(:new).with(%w[localhost 192.168.1.1], {})
|
105
|
+
store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1'
|
106
|
+
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_mem_cache_fragment_cache_store_with_options
|
110
|
+
MemCache.expects(:new).with(%w[localhost 192.168.1.1], { :timeout => 10 })
|
111
|
+
store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo', :timeout => 10
|
112
|
+
assert_kind_of(ActiveSupport::Cache::MemCacheStore, store)
|
113
|
+
assert_equal 'foo', store.options[:namespace]
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_object_assigned_fragment_cache_store
|
117
|
+
store = ActiveSupport::Cache.lookup_store ActiveSupport::Cache::FileStore.new("/path/to/cache/directory")
|
118
|
+
assert_kind_of(ActiveSupport::Cache::FileStore, store)
|
119
|
+
assert_equal "/path/to/cache/directory", store.cache_path
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class CacheStoreNamespaceTest < ActiveSupport::TestCase
|
124
|
+
def test_static_namespace
|
125
|
+
cache = ActiveSupport::Cache.lookup_store(:memory_store, :namespace => "tester")
|
126
|
+
cache.write("foo", "bar")
|
127
|
+
assert_equal "bar", cache.read("foo")
|
128
|
+
assert_equal "bar", cache.instance_variable_get(:@data)["tester:foo"].value
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_proc_namespace
|
132
|
+
test_val = "tester"
|
133
|
+
proc = lambda{test_val}
|
134
|
+
cache = ActiveSupport::Cache.lookup_store(:memory_store, :namespace => proc)
|
135
|
+
cache.write("foo", "bar")
|
136
|
+
assert_equal "bar", cache.read("foo")
|
137
|
+
assert_equal "bar", cache.instance_variable_get(:@data)["tester:foo"].value
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_delete_matched_key_start
|
141
|
+
cache = ActiveSupport::Cache.lookup_store(:memory_store, :namespace => "tester")
|
142
|
+
cache.write("foo", "bar")
|
143
|
+
cache.write("fu", "baz")
|
144
|
+
cache.delete_matched(/^fo/)
|
145
|
+
assert !cache.exist?("foo")
|
146
|
+
assert cache.exist?("fu")
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_delete_matched_key
|
150
|
+
cache = ActiveSupport::Cache.lookup_store(:memory_store, :namespace => "foo")
|
151
|
+
cache.write("foo", "bar")
|
152
|
+
cache.write("fu", "baz")
|
153
|
+
cache.delete_matched(/OO/i)
|
154
|
+
assert !cache.exist?("foo")
|
155
|
+
assert cache.exist?("fu")
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# Tests the base functionality that should be identical across all cache stores.
|
160
|
+
module CacheStoreBehavior
|
161
|
+
def test_should_read_and_write_strings
|
162
|
+
assert @cache.write('foo', 'bar')
|
163
|
+
assert_equal 'bar', @cache.read('foo')
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_should_overwrite
|
167
|
+
@cache.write('foo', 'bar')
|
168
|
+
@cache.write('foo', 'baz')
|
169
|
+
assert_equal 'baz', @cache.read('foo')
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_fetch_without_cache_miss
|
173
|
+
@cache.write('foo', 'bar')
|
174
|
+
@cache.expects(:write).never
|
175
|
+
assert_equal 'bar', @cache.fetch('foo') { 'baz' }
|
176
|
+
end
|
177
|
+
|
178
|
+
def test_fetch_with_cache_miss
|
179
|
+
@cache.expects(:write).with('foo', 'baz', @cache.options)
|
180
|
+
assert_equal 'baz', @cache.fetch('foo') { 'baz' }
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_fetch_with_forced_cache_miss
|
184
|
+
@cache.write('foo', 'bar')
|
185
|
+
@cache.expects(:read).never
|
186
|
+
@cache.expects(:write).with('foo', 'bar', @cache.options.merge(:force => true))
|
187
|
+
@cache.fetch('foo', :force => true) { 'bar' }
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_fetch_with_cached_nil
|
191
|
+
@cache.write('foo', nil)
|
192
|
+
@cache.expects(:write).never
|
193
|
+
assert_nil @cache.fetch('foo') { 'baz' }
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_should_read_and_write_hash
|
197
|
+
assert @cache.write('foo', {:a => "b"})
|
198
|
+
assert_equal({:a => "b"}, @cache.read('foo'))
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_should_read_and_write_integer
|
202
|
+
assert @cache.write('foo', 1)
|
203
|
+
assert_equal 1, @cache.read('foo')
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_should_read_and_write_nil
|
207
|
+
assert @cache.write('foo', nil)
|
208
|
+
assert_equal nil, @cache.read('foo')
|
209
|
+
end
|
210
|
+
|
211
|
+
def test_should_read_and_write_false
|
212
|
+
assert @cache.write('foo', false)
|
213
|
+
assert_equal false, @cache.read('foo')
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_should_read_cached_numeric_from_previous_rails_versions
|
217
|
+
@old_cache = ActiveSupport::Cache::Entry.create( 1, Time.now )
|
218
|
+
assert_equal( 1, @old_cache.value )
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_should_read_cached_hash_from_previous_rails_versions
|
222
|
+
@old_cache = ActiveSupport::Cache::Entry.create( {}, Time.now )
|
223
|
+
assert_equal( {}, @old_cache.value )
|
224
|
+
end
|
225
|
+
|
226
|
+
def test_should_read_cached_string_from_previous_rails_versions
|
227
|
+
@old_cache = ActiveSupport::Cache::Entry.create( 'string', Time.now )
|
228
|
+
assert_equal( 'string', @old_cache.value )
|
229
|
+
end
|
230
|
+
|
231
|
+
def test_read_multi
|
232
|
+
@cache.write('foo', 'bar')
|
233
|
+
@cache.write('fu', 'baz')
|
234
|
+
@cache.write('fud', 'biz')
|
235
|
+
assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_read_multi_with_expires
|
239
|
+
@cache.write('foo', 'bar', :expires_in => 0.001)
|
240
|
+
@cache.write('fu', 'baz')
|
241
|
+
@cache.write('fud', 'biz')
|
242
|
+
sleep(0.002)
|
243
|
+
assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
|
244
|
+
end
|
245
|
+
|
246
|
+
def test_read_and_write_compressed_small_data
|
247
|
+
@cache.write('foo', 'bar', :compress => true)
|
248
|
+
raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
|
249
|
+
assert_equal 'bar', @cache.read('foo')
|
250
|
+
assert_equal 'bar', Marshal.load(raw_value)
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_read_and_write_compressed_large_data
|
254
|
+
@cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
|
255
|
+
raw_value = @cache.send(:read_entry, 'foo', {}).raw_value
|
256
|
+
assert_equal 'bar', @cache.read('foo')
|
257
|
+
assert_equal 'bar', Marshal.load(Zlib::Inflate.inflate(raw_value))
|
258
|
+
end
|
259
|
+
|
260
|
+
def test_read_and_write_compressed_nil
|
261
|
+
@cache.write('foo', nil, :compress => true)
|
262
|
+
assert_nil @cache.read('foo')
|
263
|
+
end
|
264
|
+
|
265
|
+
def test_cache_key
|
266
|
+
obj = Object.new
|
267
|
+
def obj.cache_key
|
268
|
+
:foo
|
269
|
+
end
|
270
|
+
@cache.write(obj, "bar")
|
271
|
+
assert_equal "bar", @cache.read("foo")
|
272
|
+
end
|
273
|
+
|
274
|
+
def test_param_as_cache_key
|
275
|
+
obj = Object.new
|
276
|
+
def obj.to_param
|
277
|
+
"foo"
|
278
|
+
end
|
279
|
+
@cache.write(obj, "bar")
|
280
|
+
assert_equal "bar", @cache.read("foo")
|
281
|
+
end
|
282
|
+
|
283
|
+
def test_array_as_cache_key
|
284
|
+
@cache.write([:fu, "foo"], "bar")
|
285
|
+
assert_equal "bar", @cache.read("fu/foo")
|
286
|
+
end
|
287
|
+
|
288
|
+
def test_hash_as_cache_key
|
289
|
+
@cache.write({:foo => 1, :fu => 2}, "bar")
|
290
|
+
assert_equal "bar", @cache.read("foo=1/fu=2")
|
291
|
+
end
|
292
|
+
|
293
|
+
def test_keys_are_case_sensitive
|
294
|
+
@cache.write("foo", "bar")
|
295
|
+
assert_nil @cache.read("FOO")
|
296
|
+
end
|
297
|
+
|
298
|
+
def test_exist
|
299
|
+
@cache.write('foo', 'bar')
|
300
|
+
assert @cache.exist?('foo')
|
301
|
+
assert !@cache.exist?('bar')
|
302
|
+
end
|
303
|
+
|
304
|
+
def test_nil_exist
|
305
|
+
@cache.write('foo', nil)
|
306
|
+
assert @cache.exist?('foo')
|
307
|
+
end
|
308
|
+
|
309
|
+
def test_delete
|
310
|
+
@cache.write('foo', 'bar')
|
311
|
+
assert @cache.exist?('foo')
|
312
|
+
assert @cache.delete('foo')
|
313
|
+
assert !@cache.exist?('foo')
|
314
|
+
end
|
315
|
+
|
316
|
+
def test_read_should_return_a_different_object_id_each_time_it_is_called
|
317
|
+
@cache.write('foo', 'bar')
|
318
|
+
assert_not_equal @cache.read('foo').object_id, @cache.read('foo').object_id
|
319
|
+
value = @cache.read('foo')
|
320
|
+
value << 'bingo'
|
321
|
+
assert_not_equal value, @cache.read('foo')
|
322
|
+
end
|
323
|
+
|
324
|
+
def test_original_store_objects_should_not_be_immutable
|
325
|
+
bar = 'bar'
|
326
|
+
@cache.write('foo', bar)
|
327
|
+
assert_nothing_raised { bar.gsub!(/.*/, 'baz') }
|
328
|
+
end
|
329
|
+
|
330
|
+
def test_expires_in
|
331
|
+
time = Time.local(2008, 4, 24)
|
332
|
+
Time.stubs(:now).returns(time)
|
333
|
+
|
334
|
+
@cache.write('foo', 'bar')
|
335
|
+
assert_equal 'bar', @cache.read('foo')
|
336
|
+
|
337
|
+
Time.stubs(:now).returns(time + 30)
|
338
|
+
assert_equal 'bar', @cache.read('foo')
|
339
|
+
|
340
|
+
Time.stubs(:now).returns(time + 61)
|
341
|
+
assert_nil @cache.read('foo')
|
342
|
+
end
|
343
|
+
|
344
|
+
def test_race_condition_protection
|
345
|
+
time = Time.now
|
346
|
+
@cache.write('foo', 'bar', :expires_in => 60)
|
347
|
+
Time.stubs(:now).returns(time + 61)
|
348
|
+
result = @cache.fetch('foo', :race_condition_ttl => 10) do
|
349
|
+
assert_equal 'bar', @cache.read('foo')
|
350
|
+
"baz"
|
351
|
+
end
|
352
|
+
assert_equal "baz", result
|
353
|
+
end
|
354
|
+
|
355
|
+
def test_race_condition_protection_is_limited
|
356
|
+
time = Time.now
|
357
|
+
@cache.write('foo', 'bar', :expires_in => 60)
|
358
|
+
Time.stubs(:now).returns(time + 71)
|
359
|
+
result = @cache.fetch('foo', :race_condition_ttl => 10) do
|
360
|
+
assert_equal nil, @cache.read('foo')
|
361
|
+
"baz"
|
362
|
+
end
|
363
|
+
assert_equal "baz", result
|
364
|
+
end
|
365
|
+
|
366
|
+
def test_race_condition_protection_is_safe
|
367
|
+
time = Time.now
|
368
|
+
@cache.write('foo', 'bar', :expires_in => 60)
|
369
|
+
Time.stubs(:now).returns(time + 61)
|
370
|
+
begin
|
371
|
+
@cache.fetch('foo', :race_condition_ttl => 10) do
|
372
|
+
assert_equal 'bar', @cache.read('foo')
|
373
|
+
raise ArgumentError.new
|
374
|
+
end
|
375
|
+
rescue ArgumentError
|
376
|
+
end
|
377
|
+
assert_equal "bar", @cache.read('foo')
|
378
|
+
Time.stubs(:now).returns(time + 71)
|
379
|
+
assert_nil @cache.read('foo')
|
380
|
+
end
|
381
|
+
|
382
|
+
def test_crazy_key_characters
|
383
|
+
crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
|
384
|
+
assert @cache.write(crazy_key, "1", :raw => true)
|
385
|
+
assert_equal "1", @cache.read(crazy_key)
|
386
|
+
assert_equal "1", @cache.fetch(crazy_key)
|
387
|
+
assert @cache.delete(crazy_key)
|
388
|
+
assert_equal "2", @cache.fetch(crazy_key, :raw => true) { "2" }
|
389
|
+
assert_equal 3, @cache.increment(crazy_key)
|
390
|
+
assert_equal 2, @cache.decrement(crazy_key)
|
391
|
+
end
|
392
|
+
|
393
|
+
def test_really_long_keys
|
394
|
+
key = ""
|
395
|
+
900.times{key << "x"}
|
396
|
+
assert @cache.write(key, "bar")
|
397
|
+
assert_equal "bar", @cache.read(key)
|
398
|
+
assert_equal "bar", @cache.fetch(key)
|
399
|
+
assert_nil @cache.read("#{key}x")
|
400
|
+
assert_equal({key => "bar"}, @cache.read_multi(key))
|
401
|
+
assert @cache.delete(key)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
# https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters
|
406
|
+
# The error is caused by charcter encodings that can't be compared with ASCII-8BIT regular expressions and by special
|
407
|
+
# characters like the umlaut in UTF-8.
|
408
|
+
module EncodedKeyCacheBehavior
|
409
|
+
if defined?(Encoding)
|
410
|
+
Encoding.list.each do |encoding|
|
411
|
+
define_method "test_#{encoding.name.underscore}_encoded_values" do
|
412
|
+
key = "foo".force_encoding(encoding)
|
413
|
+
assert @cache.write(key, "1", :raw => true)
|
414
|
+
assert_equal "1", @cache.read(key)
|
415
|
+
assert_equal "1", @cache.fetch(key)
|
416
|
+
assert @cache.delete(key)
|
417
|
+
assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
|
418
|
+
assert_equal 3, @cache.increment(key)
|
419
|
+
assert_equal 2, @cache.decrement(key)
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
def test_common_utf8_values
|
424
|
+
key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8)
|
425
|
+
assert @cache.write(key, "1", :raw => true)
|
426
|
+
assert_equal "1", @cache.read(key)
|
427
|
+
assert_equal "1", @cache.fetch(key)
|
428
|
+
assert @cache.delete(key)
|
429
|
+
assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
|
430
|
+
assert_equal 3, @cache.increment(key)
|
431
|
+
assert_equal 2, @cache.decrement(key)
|
432
|
+
end
|
433
|
+
|
434
|
+
def test_retains_encoding
|
435
|
+
key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8)
|
436
|
+
assert @cache.write(key, "1", :raw => true)
|
437
|
+
assert_equal Encoding::UTF_8, key.encoding
|
438
|
+
end
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
module CacheDeleteMatchedBehavior
|
443
|
+
def test_delete_matched
|
444
|
+
@cache.write("foo", "bar")
|
445
|
+
@cache.write("fu", "baz")
|
446
|
+
@cache.write("foo/bar", "baz")
|
447
|
+
@cache.write("fu/baz", "bar")
|
448
|
+
@cache.delete_matched(/oo/)
|
449
|
+
assert !@cache.exist?("foo")
|
450
|
+
assert @cache.exist?("fu")
|
451
|
+
assert !@cache.exist?("foo/bar")
|
452
|
+
assert @cache.exist?("fu/baz")
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
module CacheIncrementDecrementBehavior
|
457
|
+
def test_increment
|
458
|
+
@cache.write('foo', 1, :raw => true)
|
459
|
+
assert_equal 1, @cache.read('foo').to_i
|
460
|
+
assert_equal 2, @cache.increment('foo')
|
461
|
+
assert_equal 2, @cache.read('foo').to_i
|
462
|
+
assert_equal 3, @cache.increment('foo')
|
463
|
+
assert_equal 3, @cache.read('foo').to_i
|
464
|
+
end
|
465
|
+
|
466
|
+
def test_decrement
|
467
|
+
@cache.write('foo', 3, :raw => true)
|
468
|
+
assert_equal 3, @cache.read('foo').to_i
|
469
|
+
assert_equal 2, @cache.decrement('foo')
|
470
|
+
assert_equal 2, @cache.read('foo').to_i
|
471
|
+
assert_equal 1, @cache.decrement('foo')
|
472
|
+
assert_equal 1, @cache.read('foo').to_i
|
473
|
+
end
|
474
|
+
end
|
475
|
+
|
476
|
+
module LocalCacheBehavior
|
477
|
+
def test_local_writes_are_persistent_on_the_remote_cache
|
478
|
+
retval = @cache.with_local_cache do
|
479
|
+
@cache.write('foo', 'bar')
|
480
|
+
end
|
481
|
+
assert retval
|
482
|
+
assert_equal 'bar', @cache.read('foo')
|
483
|
+
end
|
484
|
+
|
485
|
+
def test_clear_also_clears_local_cache
|
486
|
+
@cache.with_local_cache do
|
487
|
+
@cache.write('foo', 'bar')
|
488
|
+
@cache.clear
|
489
|
+
assert_nil @cache.read('foo')
|
490
|
+
end
|
491
|
+
|
492
|
+
assert_nil @cache.read('foo')
|
493
|
+
end
|
494
|
+
|
495
|
+
def test_local_cache_of_write
|
496
|
+
@cache.with_local_cache do
|
497
|
+
@cache.write('foo', 'bar')
|
498
|
+
@peek.delete('foo')
|
499
|
+
assert_equal 'bar', @cache.read('foo')
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
def test_local_cache_of_read
|
504
|
+
@cache.write('foo', 'bar')
|
505
|
+
@cache.with_local_cache do
|
506
|
+
assert_equal 'bar', @cache.read('foo')
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
def test_local_cache_of_write_nil
|
511
|
+
@cache.with_local_cache do
|
512
|
+
assert @cache.write('foo', nil)
|
513
|
+
assert_nil @cache.read('foo')
|
514
|
+
@peek.write('foo', 'bar')
|
515
|
+
assert_nil @cache.read('foo')
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
def test_local_cache_of_delete
|
520
|
+
@cache.with_local_cache do
|
521
|
+
@cache.write('foo', 'bar')
|
522
|
+
@cache.delete('foo')
|
523
|
+
assert_nil @cache.read('foo')
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
def test_local_cache_of_exist
|
528
|
+
@cache.with_local_cache do
|
529
|
+
@cache.write('foo', 'bar')
|
530
|
+
@peek.delete('foo')
|
531
|
+
assert @cache.exist?('foo')
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
def test_local_cache_of_increment
|
536
|
+
@cache.with_local_cache do
|
537
|
+
@cache.write('foo', 1, :raw => true)
|
538
|
+
@peek.write('foo', 2, :raw => true)
|
539
|
+
@cache.increment('foo')
|
540
|
+
assert_equal 3, @cache.read('foo')
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
def test_local_cache_of_decrement
|
545
|
+
@cache.with_local_cache do
|
546
|
+
@cache.write('foo', 1, :raw => true)
|
547
|
+
@peek.write('foo', 3, :raw => true)
|
548
|
+
@cache.decrement('foo')
|
549
|
+
assert_equal 2, @cache.read('foo')
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
def test_middleware
|
554
|
+
app = lambda { |env|
|
555
|
+
result = @cache.write('foo', 'bar')
|
556
|
+
assert_equal 'bar', @cache.read('foo') # make sure 'foo' was written
|
557
|
+
assert result
|
558
|
+
}
|
559
|
+
app = @cache.middleware.new(app)
|
560
|
+
app.call({})
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
class FileStoreTest < ActiveSupport::TestCase
|
565
|
+
def setup
|
566
|
+
Dir.mkdir(cache_dir) unless File.exist?(cache_dir)
|
567
|
+
@cache = ActiveSupport::Cache.lookup_store(:file_store, cache_dir, :expires_in => 60)
|
568
|
+
@peek = ActiveSupport::Cache.lookup_store(:file_store, cache_dir, :expires_in => 60)
|
569
|
+
@cache_with_pathname = ActiveSupport::Cache.lookup_store(:file_store, Pathname.new(cache_dir), :expires_in => 60)
|
570
|
+
end
|
571
|
+
|
572
|
+
def teardown
|
573
|
+
FileUtils.rm_r(cache_dir)
|
574
|
+
end
|
575
|
+
|
576
|
+
def cache_dir
|
577
|
+
File.join(Dir.pwd, 'tmp_cache')
|
578
|
+
end
|
579
|
+
|
580
|
+
include CacheStoreBehavior
|
581
|
+
include LocalCacheBehavior
|
582
|
+
include CacheDeleteMatchedBehavior
|
583
|
+
include CacheIncrementDecrementBehavior
|
584
|
+
|
585
|
+
def test_key_transformation
|
586
|
+
key = @cache.send(:key_file_path, "views/index?id=1")
|
587
|
+
assert_equal "views/index?id=1", @cache.send(:file_path_key, key)
|
588
|
+
end
|
589
|
+
|
590
|
+
def test_key_transformation_with_pathname
|
591
|
+
FileUtils.touch(File.join(cache_dir, "foo"))
|
592
|
+
key = @cache_with_pathname.send(:key_file_path, "views/index?id=1")
|
593
|
+
assert_equal "views/index?id=1", @cache_with_pathname.send(:file_path_key, key)
|
594
|
+
end
|
595
|
+
|
596
|
+
# Because file systems have a maximum filename size, filenames > max size should be split in to directories
|
597
|
+
# If filename is 'AAAAB', where max size is 4, the returned path should be AAAA/B
|
598
|
+
def test_key_transformation_max_filename_size
|
599
|
+
key = "#{'A' * ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}B"
|
600
|
+
path = @cache.send(:key_file_path, key)
|
601
|
+
assert path.split('/').all? { |dir_name| dir_name.size <= ActiveSupport::Cache::FileStore::FILENAME_MAX_SIZE}
|
602
|
+
assert_equal 'B', File.basename(path)
|
603
|
+
end
|
604
|
+
|
605
|
+
# If nothing has been stored in the cache, there is a chance the cache directory does not yet exist
|
606
|
+
# Ensure delete_matched gracefully handles this case
|
607
|
+
def test_delete_matched_when_cache_directory_does_not_exist
|
608
|
+
assert_nothing_raised(Exception) do
|
609
|
+
ActiveSupport::Cache::FileStore.new('/test/cache/directory').delete_matched(/does_not_exist/)
|
610
|
+
end
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
class MemoryStoreTest < ActiveSupport::TestCase
|
615
|
+
def setup
|
616
|
+
@record_size = Marshal.dump("aaaaaaaaaa").bytesize
|
617
|
+
@cache = ActiveSupport::Cache.lookup_store(:memory_store, :expires_in => 60, :size => @record_size * 10)
|
618
|
+
end
|
619
|
+
|
620
|
+
include CacheStoreBehavior
|
621
|
+
include CacheDeleteMatchedBehavior
|
622
|
+
include CacheIncrementDecrementBehavior
|
623
|
+
|
624
|
+
def test_prune_size
|
625
|
+
@cache.write(1, "aaaaaaaaaa") && sleep(0.001)
|
626
|
+
@cache.write(2, "bbbbbbbbbb") && sleep(0.001)
|
627
|
+
@cache.write(3, "cccccccccc") && sleep(0.001)
|
628
|
+
@cache.write(4, "dddddddddd") && sleep(0.001)
|
629
|
+
@cache.write(5, "eeeeeeeeee") && sleep(0.001)
|
630
|
+
@cache.read(2) && sleep(0.001)
|
631
|
+
@cache.read(4)
|
632
|
+
@cache.prune(@record_size * 3)
|
633
|
+
assert @cache.exist?(5)
|
634
|
+
assert @cache.exist?(4)
|
635
|
+
assert !@cache.exist?(3)
|
636
|
+
assert @cache.exist?(2)
|
637
|
+
assert !@cache.exist?(1)
|
638
|
+
end
|
639
|
+
|
640
|
+
def test_prune_size_on_write
|
641
|
+
@cache.write(1, "aaaaaaaaaa") && sleep(0.001)
|
642
|
+
@cache.write(2, "bbbbbbbbbb") && sleep(0.001)
|
643
|
+
@cache.write(3, "cccccccccc") && sleep(0.001)
|
644
|
+
@cache.write(4, "dddddddddd") && sleep(0.001)
|
645
|
+
@cache.write(5, "eeeeeeeeee") && sleep(0.001)
|
646
|
+
@cache.write(6, "ffffffffff") && sleep(0.001)
|
647
|
+
@cache.write(7, "gggggggggg") && sleep(0.001)
|
648
|
+
@cache.write(8, "hhhhhhhhhh") && sleep(0.001)
|
649
|
+
@cache.write(9, "iiiiiiiiii") && sleep(0.001)
|
650
|
+
@cache.write(10, "kkkkkkkkkk") && sleep(0.001)
|
651
|
+
@cache.read(2) && sleep(0.001)
|
652
|
+
@cache.read(4) && sleep(0.001)
|
653
|
+
@cache.write(11, "llllllllll")
|
654
|
+
assert @cache.exist?(11)
|
655
|
+
assert @cache.exist?(10)
|
656
|
+
assert @cache.exist?(9)
|
657
|
+
assert @cache.exist?(8)
|
658
|
+
assert @cache.exist?(7)
|
659
|
+
assert !@cache.exist?(6)
|
660
|
+
assert !@cache.exist?(5)
|
661
|
+
assert @cache.exist?(4)
|
662
|
+
assert !@cache.exist?(3)
|
663
|
+
assert @cache.exist?(2)
|
664
|
+
assert !@cache.exist?(1)
|
665
|
+
end
|
666
|
+
|
667
|
+
def test_pruning_is_capped_at_a_max_time
|
668
|
+
def @cache.delete_entry (*args)
|
669
|
+
sleep(0.01)
|
670
|
+
super
|
671
|
+
end
|
672
|
+
@cache.write(1, "aaaaaaaaaa") && sleep(0.001)
|
673
|
+
@cache.write(2, "bbbbbbbbbb") && sleep(0.001)
|
674
|
+
@cache.write(3, "cccccccccc") && sleep(0.001)
|
675
|
+
@cache.write(4, "dddddddddd") && sleep(0.001)
|
676
|
+
@cache.write(5, "eeeeeeeeee") && sleep(0.001)
|
677
|
+
@cache.prune(30, 0.001)
|
678
|
+
assert @cache.exist?(5)
|
679
|
+
assert @cache.exist?(4)
|
680
|
+
assert @cache.exist?(3)
|
681
|
+
assert @cache.exist?(2)
|
682
|
+
assert !@cache.exist?(1)
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
686
|
+
uses_memcached 'memcached backed store' do
|
687
|
+
class MemCacheStoreTest < ActiveSupport::TestCase
|
688
|
+
def setup
|
689
|
+
@cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :expires_in => 60)
|
690
|
+
@peek = ActiveSupport::Cache.lookup_store(:mem_cache_store)
|
691
|
+
@data = @cache.instance_variable_get(:@data)
|
692
|
+
@cache.clear
|
693
|
+
@cache.silence!
|
694
|
+
@cache.logger = Logger.new("/dev/null")
|
695
|
+
end
|
696
|
+
|
697
|
+
include CacheStoreBehavior
|
698
|
+
include LocalCacheBehavior
|
699
|
+
include CacheIncrementDecrementBehavior
|
700
|
+
include EncodedKeyCacheBehavior
|
701
|
+
|
702
|
+
def test_raw_values
|
703
|
+
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
|
704
|
+
cache.clear
|
705
|
+
cache.write("foo", 2)
|
706
|
+
assert_equal "2", cache.read("foo")
|
707
|
+
end
|
708
|
+
|
709
|
+
def test_raw_values_with_marshal
|
710
|
+
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
|
711
|
+
cache.clear
|
712
|
+
cache.write("foo", Marshal.dump([]))
|
713
|
+
assert_equal [], cache.read("foo")
|
714
|
+
end
|
715
|
+
|
716
|
+
def test_local_cache_raw_values
|
717
|
+
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
|
718
|
+
cache.clear
|
719
|
+
cache.with_local_cache do
|
720
|
+
cache.write("foo", 2)
|
721
|
+
assert_equal "2", cache.read("foo")
|
722
|
+
end
|
723
|
+
end
|
724
|
+
|
725
|
+
def test_local_cache_raw_values_with_marshal
|
726
|
+
cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true)
|
727
|
+
cache.clear
|
728
|
+
cache.with_local_cache do
|
729
|
+
cache.write("foo", Marshal.dump([]))
|
730
|
+
assert_equal [], cache.read("foo")
|
731
|
+
end
|
732
|
+
end
|
733
|
+
end
|
734
|
+
end
|
735
|
+
|
736
|
+
class NullStoreTest < ActiveSupport::TestCase
|
737
|
+
def setup
|
738
|
+
@cache = ActiveSupport::Cache.lookup_store(:null_store)
|
739
|
+
end
|
740
|
+
|
741
|
+
def test_clear
|
742
|
+
@cache.clear
|
743
|
+
end
|
744
|
+
|
745
|
+
def test_cleanup
|
746
|
+
@cache.cleanup
|
747
|
+
end
|
748
|
+
|
749
|
+
def test_write
|
750
|
+
assert_equal true, @cache.write("name", "value")
|
751
|
+
end
|
752
|
+
|
753
|
+
def test_read
|
754
|
+
@cache.write("name", "value")
|
755
|
+
assert_nil @cache.read("name")
|
756
|
+
end
|
757
|
+
|
758
|
+
def test_delete
|
759
|
+
@cache.write("name", "value")
|
760
|
+
assert_equal false, @cache.delete("name")
|
761
|
+
end
|
762
|
+
|
763
|
+
def test_increment
|
764
|
+
@cache.write("name", 1, :raw => true)
|
765
|
+
assert_nil @cache.increment("name")
|
766
|
+
end
|
767
|
+
|
768
|
+
def test_decrement
|
769
|
+
@cache.write("name", 1, :raw => true)
|
770
|
+
assert_nil @cache.increment("name")
|
771
|
+
end
|
772
|
+
|
773
|
+
def test_delete_matched
|
774
|
+
@cache.write("name", "value")
|
775
|
+
@cache.delete_matched(/name/)
|
776
|
+
end
|
777
|
+
|
778
|
+
def test_local_store_strategy
|
779
|
+
@cache.with_local_cache do
|
780
|
+
@cache.write("name", "value")
|
781
|
+
assert_equal "value", @cache.read("name")
|
782
|
+
@cache.delete("name")
|
783
|
+
assert_nil @cache.read("name")
|
784
|
+
@cache.write("name", "value")
|
785
|
+
end
|
786
|
+
assert_nil @cache.read("name")
|
787
|
+
end
|
788
|
+
|
789
|
+
def test_setting_nil_cache_store
|
790
|
+
assert ActiveSupport::Cache.lookup_store.class.name, ActiveSupport::Cache::NullStore.name
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
class CacheStoreLoggerTest < ActiveSupport::TestCase
|
795
|
+
def setup
|
796
|
+
@cache = ActiveSupport::Cache.lookup_store(:memory_store)
|
797
|
+
|
798
|
+
@buffer = StringIO.new
|
799
|
+
@cache.logger = Logger.new(@buffer)
|
800
|
+
end
|
801
|
+
|
802
|
+
def test_logging
|
803
|
+
@cache.fetch('foo') { 'bar' }
|
804
|
+
assert_present @buffer.string
|
805
|
+
end
|
806
|
+
|
807
|
+
def test_mute_logging
|
808
|
+
@cache.mute { @cache.fetch('foo') { 'bar' } }
|
809
|
+
assert_blank @buffer.string
|
810
|
+
end
|
811
|
+
end
|
812
|
+
|
813
|
+
class CacheEntryTest < ActiveSupport::TestCase
|
814
|
+
def test_create_raw_entry
|
815
|
+
time = Time.now
|
816
|
+
entry = ActiveSupport::Cache::Entry.create("raw", time, :compress => false, :expires_in => 300)
|
817
|
+
assert_equal "raw", entry.raw_value
|
818
|
+
assert_equal time.to_f, entry.created_at
|
819
|
+
assert !entry.compressed?
|
820
|
+
assert_equal 300, entry.expires_in
|
821
|
+
end
|
822
|
+
|
823
|
+
def test_expired
|
824
|
+
entry = ActiveSupport::Cache::Entry.new("value")
|
825
|
+
assert !entry.expired?, 'entry not expired'
|
826
|
+
entry = ActiveSupport::Cache::Entry.new("value", :expires_in => 60)
|
827
|
+
assert !entry.expired?, 'entry not expired'
|
828
|
+
time = Time.now + 61
|
829
|
+
Time.stubs(:now).returns(time)
|
830
|
+
assert entry.expired?, 'entry is expired'
|
831
|
+
end
|
832
|
+
|
833
|
+
def test_compress_values
|
834
|
+
entry = ActiveSupport::Cache::Entry.new("value", :compress => true, :compress_threshold => 1)
|
835
|
+
assert_equal "value", entry.value
|
836
|
+
assert entry.compressed?
|
837
|
+
assert_equal "value", Marshal.load(Zlib::Inflate.inflate(entry.raw_value))
|
838
|
+
end
|
839
|
+
|
840
|
+
def test_non_compress_values
|
841
|
+
entry = ActiveSupport::Cache::Entry.new("value")
|
842
|
+
assert_equal "value", entry.value
|
843
|
+
assert_equal "value", Marshal.load(entry.raw_value)
|
844
|
+
assert !entry.compressed?
|
845
|
+
end
|
846
|
+
end
|