itrigga-cache 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +24 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +79 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/lib/itrigga/cache/cache.rb +195 -0
- data/lib/itrigga/cache/filecache.rb +63 -0
- data/lib/itrigga/cache/memcache.rb +55 -0
- data/lib/itrigga/cache.rb +1 -0
- data/spec/filecache_spec.rb +155 -0
- data/spec/itrigga-cache_spec.rb +412 -0
- data/spec/memcache_spec.rb +71 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +15 -0
- data/support/singleton_reset.rb +20 -0
- metadata +203 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
gem "memcached"
|
9
|
+
gem "itrigga-param_fu"
|
10
|
+
|
11
|
+
group :development do
|
12
|
+
gem "rspec", "1.3.0"
|
13
|
+
gem "bundler", "~> 1.0.0"
|
14
|
+
gem "jeweler", "~> 1.6.4"
|
15
|
+
gem "rcov", ">= 0"
|
16
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
git (1.2.5)
|
5
|
+
itrigga-param_fu (0.0.1)
|
6
|
+
jeweler (1.6.4)
|
7
|
+
bundler (~> 1.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
memcached (1.3.5)
|
11
|
+
rake (0.8.7)
|
12
|
+
rcov (0.9.11)
|
13
|
+
rspec (1.3.0)
|
14
|
+
|
15
|
+
PLATFORMS
|
16
|
+
ruby
|
17
|
+
|
18
|
+
DEPENDENCIES
|
19
|
+
bundler (~> 1.0.0)
|
20
|
+
itrigga-param_fu
|
21
|
+
jeweler (~> 1.6.4)
|
22
|
+
memcached
|
23
|
+
rcov
|
24
|
+
rspec (= 1.3.0)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Anson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
= itrigga-cache
|
2
|
+
|
3
|
+
Adds methods to models and controllers to help caching output.
|
4
|
+
|
5
|
+
== Models
|
6
|
+
With models use like below to cache the output of the block
|
7
|
+
|
8
|
+
with_cache(:key => "unique_key") do
|
9
|
+
Model.find(:all)
|
10
|
+
end
|
11
|
+
|
12
|
+
To use a different timeout use the below. (Only supported for memcached backend. Filecache uses the value from setup!)
|
13
|
+
|
14
|
+
with_cache(:key => "unique_key", :timeout => 42) do
|
15
|
+
Model.find(:all)
|
16
|
+
end
|
17
|
+
|
18
|
+
== Controllers
|
19
|
+
With controllers use like:
|
20
|
+
|
21
|
+
with_controller_cache do
|
22
|
+
@items = Item.new_on_site(:site_id=>@current_site.id, :page=>params[:page], :per_page=>params[:per_page])
|
23
|
+
end
|
24
|
+
|
25
|
+
This will
|
26
|
+
- call the block
|
27
|
+
- render the content
|
28
|
+
- cache the *rendered* output
|
29
|
+
- set the :content_type according to the pasted in value (defaults to 'text/html')
|
30
|
+
To bypass the cache just have the "freshen" => true key in the params (ie as a query string param). This will force fresh the cache
|
31
|
+
|
32
|
+
|
33
|
+
== Installation
|
34
|
+
In environment.rb:
|
35
|
+
config.gem 'itrigga-cache', :lib=>'itrigga/cache'
|
36
|
+
|
37
|
+
In application_controller
|
38
|
+
require 'itrigga/cache'
|
39
|
+
include Itrigga::Cache
|
40
|
+
Itrigga::Cache.setup!
|
41
|
+
|
42
|
+
Itrigga::Cache.setup! initializes a cache instance. Memcache and filecache are currently supported
|
43
|
+
|
44
|
+
Itrigga::Cache.setup! :backend => :memcached
|
45
|
+
Options are:
|
46
|
+
:servers - ip:port of the memcache instances to use
|
47
|
+
:timeout - the default life of the cache item
|
48
|
+
Any other options get passed to the memcached initializer - see https://github.com/fauna/memcached for more info
|
49
|
+
|
50
|
+
|
51
|
+
Itrigga::Cache.setup! :backend => :filecache
|
52
|
+
Options are:
|
53
|
+
:cache_dir - the file path to where the cache files will live
|
54
|
+
:timeout - how long the cache key is valid for
|
55
|
+
|
56
|
+
Itrigga::Cache.setup! can be called as many times as you like, for as many backends as you like.
|
57
|
+
The cache gem will use the last defined (setup) backend as the default backend.
|
58
|
+
|
59
|
+
Use the :backend option to force a backend
|
60
|
+
with_cache(:key => "unique_key", :backend => :memcached) do
|
61
|
+
Model.find(:all)
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
== Contributing to itrigga-cache
|
66
|
+
|
67
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
68
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
69
|
+
* Fork the project
|
70
|
+
* Start a feature/bugfix branch
|
71
|
+
* Commit and push until you are happy with your contribution
|
72
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
73
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
74
|
+
|
75
|
+
== Copyright
|
76
|
+
|
77
|
+
Copyright (c) 2011 Anson. See LICENSE.txt for
|
78
|
+
further details.
|
79
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "itrigga-cache"
|
18
|
+
gem.homepage = "http://gemcutter.com/gems/itrigga-cache"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Adds caching methods}
|
21
|
+
gem.description = %Q{Wraps memcached with helpful methods}
|
22
|
+
gem.email = "support@itrigga.com"
|
23
|
+
gem.authors = ["Al Davidson","Anson Kelly"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
gem.add_dependency "memcached"
|
26
|
+
gem.add_dependency "itrigga-param_fu"
|
27
|
+
end
|
28
|
+
Jeweler::RubygemsDotOrgTasks.new
|
29
|
+
|
30
|
+
require 'spec/version'
|
31
|
+
require 'spec/rake/spectask'
|
32
|
+
require 'spec/ruby'
|
33
|
+
|
34
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
35
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
36
|
+
spec.spec_opts = ['--options', 'spec/spec.opts']
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
desc "Run all specs with rcov"
|
42
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
43
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
44
|
+
t.spec_opts = ['--options', 'spec/spec.opts']
|
45
|
+
t.rcov = true
|
46
|
+
t.rcov_dir = 'coverage'
|
47
|
+
t.rcov_opts = ['--exclude', "features,kernel,load-diff-lcs\.rb,instance_exec\.rb,lib/spec.rb,lib/spec/runner.rb,^spec/*,bin/spec,examples,/gems,/Library/Ruby,\.autotest,#{ENV['GEM_HOME']}"]
|
48
|
+
t.rcov_opts << '--sort coverage --text-summary --aggregate coverage.data'
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.1
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'memcached'
|
2
|
+
require 'trigga/param_fu'
|
3
|
+
|
4
|
+
module Itrigga
|
5
|
+
module Cache
|
6
|
+
|
7
|
+
@@ITRIGGA_CACHE_TYPE = :filecache
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
base.send(:include, InstanceMethods)
|
12
|
+
base.send(:include, Trigga::ParamFu)
|
13
|
+
base.send(:include, ControllerMethods) if defined?(ActionController::Base) && base.ancestors.include?(ActionController::Base)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.setup!(opts ={})
|
17
|
+
if opts[:backend] == :memcached
|
18
|
+
@@ITRIGGA_CACHE_TYPE = :memcached
|
19
|
+
Itrigga::Cache::Memcache.setup!(opts)
|
20
|
+
else
|
21
|
+
Itrigga::Cache::Filecache.setup!(opts)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.instance(opts = {})
|
26
|
+
case ( opts[:backend] ||= @@ITRIGGA_CACHE_TYPE )
|
27
|
+
when :memcached
|
28
|
+
Itrigga::Cache::Memcache.instance
|
29
|
+
when :filecache
|
30
|
+
Itrigga::Cache::Filecache.instance
|
31
|
+
else
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
module InstanceMethods
|
38
|
+
|
39
|
+
def with_cache(opts = {}, &block)
|
40
|
+
self.class.with_cache(opts, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def caching_enabled?(opts = {})
|
44
|
+
self.class.caching_enabled?(opts)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
def get_from_cache(key, opts = {})
|
50
|
+
self.class.get_from_cache(key, opts)
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_to_cache(key, value, opts = {})
|
54
|
+
self.class.set_to_cache(key, value, opts)
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete_from_cache(key, opts = {})
|
58
|
+
self.class.delete_from_cache(key, opts)
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def cache_log(text, opts = {})
|
63
|
+
self.class.cache_log(text, opts)
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
module ClassMethods
|
69
|
+
|
70
|
+
def with_cache(opts = {}, &block)
|
71
|
+
require_param(opts, :key)
|
72
|
+
|
73
|
+
# if no cache then just return whatever the block gives us
|
74
|
+
unless caching_enabled?(opts)
|
75
|
+
cache_log "Cache not enabled!", opts
|
76
|
+
return block.call
|
77
|
+
end
|
78
|
+
|
79
|
+
# see if the key is already in cache
|
80
|
+
value = get_from_cache(opts[:key], opts)
|
81
|
+
|
82
|
+
# if no match then call the block and save result in cache
|
83
|
+
unless value
|
84
|
+
cache_log "Key '#{opts[:key]}' missing! Calling block and setting in cache", opts
|
85
|
+
value = block.call
|
86
|
+
set_to_cache(opts.delete(:key), value, opts) rescue value # incase memcache crashes or whateversolr1-internal-itrigga.dyndns-ip.com solr1-internal-itrigga.dyndns-ip.com
|
87
|
+
else
|
88
|
+
cache_log "Key '#{opts[:key]}' found in cache!", opts
|
89
|
+
end
|
90
|
+
|
91
|
+
value
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
def caching_enabled?(opts = {})
|
96
|
+
Itrigga::Cache.instance(opts).enabled == true
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
def get_from_cache(key, opts = {})
|
104
|
+
return nil unless caching_enabled?(opts)
|
105
|
+
cache_log "get_from_cache key: #{key}, opts: #{opts.inspect}" #if opts[:debug]
|
106
|
+
begin
|
107
|
+
Itrigga::Cache.instance(opts).get key
|
108
|
+
rescue Memcached::NotFound => not_found
|
109
|
+
# we dont care
|
110
|
+
nil
|
111
|
+
rescue Exception => e
|
112
|
+
cache_log "Exception in get_from_cache: #{e.message}"
|
113
|
+
nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
def set_to_cache(key, value, opts = {})
|
120
|
+
raise "Cache not Enabled" unless caching_enabled?(opts)
|
121
|
+
|
122
|
+
cache_log "set_to_cache key: #{key}, value: #{value}, opts: #{opts.inspect}"# if opts[:debug]
|
123
|
+
begin
|
124
|
+
Itrigga::Cache.instance(opts).set key, value, opts
|
125
|
+
rescue Exception => e
|
126
|
+
cache_log "Exception in set_to_cache: #{e.message}"
|
127
|
+
end
|
128
|
+
|
129
|
+
value
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
def delete_from_cache(key, opts = {})
|
134
|
+
return nil unless caching_enabled?(opts)
|
135
|
+
begin
|
136
|
+
cache_log "Deleting key #{key}", opts
|
137
|
+
Itrigga::Cache.instance(opts).delete key
|
138
|
+
rescue Exception => e
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def cache_log(text, opts = {})
|
145
|
+
message = "[CACHE] [#{opts[:backend] }] #{text}"
|
146
|
+
defined?(Rails.logger) && Rails.logger ? Rails.logger.info(message) : puts(message)
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
module ControllerMethods
|
153
|
+
|
154
|
+
def with_controller_cache( opts = {}, &block )
|
155
|
+
|
156
|
+
if caching_enabled?(opts)
|
157
|
+
|
158
|
+
opts[:key] = opts[:cache_key] || opts[:key] || (respond_to?(:cache_key) ? cache_key.to_s : nil) # backwards compat using :cache_key
|
159
|
+
raise ArgumentError("No cache key found") unless opts[:key]
|
160
|
+
|
161
|
+
begin
|
162
|
+
|
163
|
+
if params && params.kind_of?(Hash) && params["freshen"]
|
164
|
+
delete_from_cache opts[:key], opts
|
165
|
+
end
|
166
|
+
|
167
|
+
@content = with_cache(opts, &block)
|
168
|
+
@content ||= render_to_string unless performed?
|
169
|
+
|
170
|
+
rescue Exception => ex
|
171
|
+
cache_log "Error when rendering cache block: #{ex.message}", opts
|
172
|
+
@content ||= block.call
|
173
|
+
@content ||= render_to_string unless performed?
|
174
|
+
end
|
175
|
+
|
176
|
+
# flashes need to be rendered and replaced every request if we're in a html request
|
177
|
+
render_flashes if request.format.html? && respond_to?(:render_flashes)
|
178
|
+
|
179
|
+
else # cache is not enabled so just render the block
|
180
|
+
cache_log "Cache not enabled!", opts
|
181
|
+
@content ||= block.call
|
182
|
+
@content ||= render_to_string unless performed?
|
183
|
+
end
|
184
|
+
|
185
|
+
render(:text=>@content, :content_type=>( @content_type || "text/html")) unless performed?
|
186
|
+
|
187
|
+
@content
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# http://stackoverflow.com/questions/4217968/a-problem-about-singleton-in-ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
# To implement a backend it needs to respond to:
|
5
|
+
# - self.setup!
|
6
|
+
# - get
|
7
|
+
# - set
|
8
|
+
# - delete
|
9
|
+
#
|
10
|
+
|
11
|
+
|
12
|
+
require 'singleton'
|
13
|
+
|
14
|
+
module Itrigga
|
15
|
+
module Cache
|
16
|
+
|
17
|
+
class Filecache
|
18
|
+
include Singleton
|
19
|
+
|
20
|
+
attr_accessor :cache_dir, :timeout, :enabled
|
21
|
+
|
22
|
+
def self.setup!(opts = {})
|
23
|
+
instance.cache_dir = opts[:cache_dir] || "#{defined?(RAILS_ROOT) ? RAILS_ROOT : ''}/tmp/cache"
|
24
|
+
instance.timeout = opts[:timeout] || 3600
|
25
|
+
instance.enabled = true
|
26
|
+
instance
|
27
|
+
end
|
28
|
+
|
29
|
+
def get(key, opts = {})
|
30
|
+
if expired?(key, opts)
|
31
|
+
delete(key, opts)
|
32
|
+
nil
|
33
|
+
else
|
34
|
+
File.open( file_path(key, opts), 'r' ){ |f| f.read } if is_in_cache?(key, opts)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def set(key, content, opts = {})
|
39
|
+
File.open( file_path(key, opts), 'w' ){ |f| f.write(content) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete(key, opts = {})
|
43
|
+
File.delete( file_path(key, opts)) if is_in_cache?(key, opts)
|
44
|
+
end
|
45
|
+
|
46
|
+
def file_path(key, opts = {})
|
47
|
+
File.expand_path(File.join(opts[:cache_dir] || cache_dir, key ))
|
48
|
+
end
|
49
|
+
|
50
|
+
def is_in_cache?(key, opts={} )
|
51
|
+
File.exists?( file_path(key, opts) )
|
52
|
+
end
|
53
|
+
|
54
|
+
def expired?(key, opts={} )
|
55
|
+
return true unless is_in_cache?(key, opts)
|
56
|
+
opts[:timeout] ||= timeout
|
57
|
+
Time.now - File.mtime(file_path(key, opts)) > opts[:timeout].to_i
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# http://stackoverflow.com/questions/4217968/a-problem-about-singleton-in-ruby
|
2
|
+
#
|
3
|
+
# To implement a backend it needs to respond to:
|
4
|
+
# - self.setup!
|
5
|
+
# - get
|
6
|
+
# - set
|
7
|
+
# - delete
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'singleton'
|
11
|
+
|
12
|
+
module Itrigga
|
13
|
+
module Cache
|
14
|
+
|
15
|
+
class Memcache
|
16
|
+
include Singleton
|
17
|
+
|
18
|
+
attr_accessor :cache, :enabled
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@cache = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.setup!(opts = {})
|
25
|
+
servers = opts[:servers] || "localhost:11211"
|
26
|
+
opts.delete(:servers)
|
27
|
+
|
28
|
+
opts[:default_ttl] ||= opts[:timeout] if opts[:timeout] # rename default_ttl to something easier to use
|
29
|
+
opts.delete(:timeout)
|
30
|
+
instance.enabled = true
|
31
|
+
|
32
|
+
puts "Memcached init with: #{servers.inspect} - #{opts.inspect}"
|
33
|
+
instance.cache = ::Memcached.new(servers, opts)
|
34
|
+
end
|
35
|
+
|
36
|
+
def get(key, opts = {})
|
37
|
+
cache.get key
|
38
|
+
end
|
39
|
+
|
40
|
+
def set(key, value, opts = {})
|
41
|
+
if opts[:timeout]
|
42
|
+
cache.set key, value, opts[:timeout]
|
43
|
+
else
|
44
|
+
cache.set key, value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete(key, opts = {})
|
49
|
+
cache.delete key
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__),"cache","*.rb"))].each{|f| require f }
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Itrigga::Cache::Filecache do
|
4
|
+
before do
|
5
|
+
Itrigga::Cache::Filecache.reset_instance
|
6
|
+
@file = mock("File")
|
7
|
+
@file.stub!(:read).and_return("content")
|
8
|
+
@file.stub!(:write)
|
9
|
+
File.stub!(:open).and_yield(@file)
|
10
|
+
File.stub!(:delete)
|
11
|
+
File.stub!(:exists?).and_return(false)
|
12
|
+
@instance = Itrigga::Cache::Filecache.instance
|
13
|
+
@opts = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should include Singleton" do
|
17
|
+
Itrigga::Cache::Filecache.should include Singleton
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "setup!" do
|
21
|
+
describe "when given no options" do
|
22
|
+
it "should assign default cache_dir" do
|
23
|
+
Itrigga::Cache::Filecache.setup!
|
24
|
+
Itrigga::Cache::Filecache.instance.cache_dir.should == "#{RAILS_ROOT}/tmp/cache"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should assign default timeout" do
|
28
|
+
Itrigga::Cache::Filecache.setup!
|
29
|
+
Itrigga::Cache::Filecache.instance.timeout.should == 3600
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should assign the given cache_dir" do
|
34
|
+
Itrigga::Cache::Filecache.setup! :cache_dir => "monkeys"
|
35
|
+
Itrigga::Cache::Filecache.instance.cache_dir.should == "monkeys"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should assign the given timeout" do
|
39
|
+
Itrigga::Cache::Filecache.setup! :timeout => 42
|
40
|
+
Itrigga::Cache::Filecache.instance.timeout.should == 42
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should return the instance" do
|
44
|
+
Itrigga::Cache::Filecache.setup!().should be_a_kind_of Itrigga::Cache::Filecache
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
describe "instance methods" do
|
50
|
+
before do
|
51
|
+
Itrigga::Cache::Filecache.setup! # need to set the defaults on the instance
|
52
|
+
@file_path = "#{RAILS_ROOT}/tmp/cache/key"
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "get" do
|
56
|
+
describe "when expired" do
|
57
|
+
before do
|
58
|
+
@instance.stub!(:expired?).and_return(true)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should call delete" do
|
62
|
+
@instance.should_receive(:delete).with("key", @opts)
|
63
|
+
@instance.get("key", @opts)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should return nil" do
|
67
|
+
@instance.get("key", @opts).should == nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "when not expired" do
|
72
|
+
before do
|
73
|
+
@instance.stub!(:expired?).and_return(false)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should read the file when the file exists" do
|
77
|
+
@instance.stub!(:is_in_cache?).and_return(true)
|
78
|
+
File.should_receive(:open).with(@file_path,'r').and_yield(@file)
|
79
|
+
@file.should_receive(:read).and_return("content")
|
80
|
+
@instance.get("key", @opts).should == "content"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should not try to read the file if not exist" do
|
84
|
+
@instance.stub!(:is_in_cache?).and_return(false)
|
85
|
+
File.should_not_receive(:open).with(@file_path,'r')
|
86
|
+
@instance.get("key", @opts).should == nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
describe "set" do
|
94
|
+
it "should write the content to file" do
|
95
|
+
File.should_receive(:open).with(@file_path,'w').and_yield(@file)
|
96
|
+
@file.should_receive(:write).with("content")
|
97
|
+
@instance.set("key","content",@opts)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
describe "delete" do
|
104
|
+
describe "when the file exists" do
|
105
|
+
before do
|
106
|
+
@instance.stub!(:is_in_cache?).and_return(true)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should call File.delete" do
|
110
|
+
File.should_receive(:delete).with(@file_path)
|
111
|
+
@instance.delete("key")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
describe "when the file does not exists" do
|
116
|
+
before do
|
117
|
+
@instance.stub!(:is_in_cache?).and_return(false)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should call File.delete" do
|
121
|
+
File.should_not_receive(:delete)
|
122
|
+
@instance.delete("key")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
describe "expired?" do
|
129
|
+
describe "when file is not in cache" do
|
130
|
+
it "should return true" do
|
131
|
+
@instance.should_receive(:is_in_cache?).with("key",@opts).and_return(false)
|
132
|
+
@instance.expired?("key", @opts).should == true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "when file is in cache" do
|
137
|
+
before do
|
138
|
+
@instance.stub!(:is_in_cache?).and_return(true)
|
139
|
+
Time.stub!(:now).and_return(42)
|
140
|
+
File.stub!(:mtime).and_return(20)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should return false if not expired" do
|
144
|
+
@instance.expired?("key",@opts).should == false
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should return true if expired" do
|
148
|
+
@instance.expired?("key",{:timeout => 5}).should == true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|