filecache 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +24 -0
- data/README +27 -0
- data/lib/filecache.rb +107 -0
- data/tests/test_filecache.rb +34 -0
- metadata +57 -0
data/COPYING
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
FileCache is distributed under the MIT license
|
2
|
+
|
3
|
+
Copyright (c) 2008 Simon Whitaker <mailto:sw@netcetera.org>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person
|
6
|
+
obtaining a copy of this software and associated documentation
|
7
|
+
files (the "Software"), to deal in the Software without
|
8
|
+
restriction, including without limitation the rights to use,
|
9
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the
|
11
|
+
Software is furnished to do so, subject to the following
|
12
|
+
conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
19
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
21
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
22
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
= FileCache
|
2
|
+
|
3
|
+
FileCache is a file-based caching library for Ruby
|
4
|
+
|
5
|
+
= Synopsis
|
6
|
+
|
7
|
+
cache = FileCache.new
|
8
|
+
cache.set(:key, "value")
|
9
|
+
puts cache.get(:key) # "value"
|
10
|
+
cache.delete(:key)
|
11
|
+
puts cache.get(:key) # nil
|
12
|
+
|
13
|
+
# create a new cache called "my-cache", rooted in /home/simon/caches
|
14
|
+
# with an expiry time of 30 seconds, and a file hierarchy three
|
15
|
+
# directories deep
|
16
|
+
cache = FileCache.new("my-cache", "/home/simon/caches", 30, 3)
|
17
|
+
cache.put("joe", "bloggs")
|
18
|
+
puts(cache.get("joe")) # "bloggs"
|
19
|
+
sleep 30
|
20
|
+
puts(cache.get("joe")) # nil
|
21
|
+
|
22
|
+
|
23
|
+
= Copyright
|
24
|
+
|
25
|
+
Copyright 2008 Simon Whitaker <mailto:sw@netcetera.org>
|
26
|
+
|
27
|
+
See COPYING for license.
|
data/lib/filecache.rb
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
# A file-based caching library. It uses Marshal::dump and Marshal::load
|
5
|
+
# to serialize/deserialize cache values - so you should be OK to cache
|
6
|
+
# object values.
|
7
|
+
class FileCache
|
8
|
+
|
9
|
+
MAX_DEPTH = 32
|
10
|
+
|
11
|
+
# Create a new reference to a file cache system.
|
12
|
+
# domain:: A string that uniquely identifies this caching
|
13
|
+
# system on the given host
|
14
|
+
# root_dir:: The root directory of the cache file hierarchy
|
15
|
+
# The cache will be rooted at root_dir/domain/
|
16
|
+
# expiry:: The expiry time for cache entries, in seconds. Use
|
17
|
+
# 0 if you want cached values never to expire.
|
18
|
+
# depth:: The depth of the file tree storing the cache. Should
|
19
|
+
# be large enough that no cache directory has more than
|
20
|
+
# a couple of hundred objects in it
|
21
|
+
def initialize(domain = "default", root_dir = "/tmp", expiry = 0, depth = 2)
|
22
|
+
@domain = domain
|
23
|
+
@root_dir = root_dir
|
24
|
+
@expiry = expiry
|
25
|
+
@depth = depth > MAX_DEPTH ? MAX_DEPTH : depth
|
26
|
+
end
|
27
|
+
|
28
|
+
# Set a cache value for the given key. If the cache contains an existing value for
|
29
|
+
# the key it will be overwritten.
|
30
|
+
def set(key, value)
|
31
|
+
f = File.open(get_path(key), "w")
|
32
|
+
Marshal.dump(value, f)
|
33
|
+
f.close
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return the value for the specified key from the cache. Returns nil if
|
37
|
+
# the value isn't found.
|
38
|
+
def get(key)
|
39
|
+
path = get_path(key)
|
40
|
+
|
41
|
+
# expire
|
42
|
+
if @expiry > 0 && File.exists?(path) && Time.new - File.new(path).mtime >= @expiry
|
43
|
+
FileUtils.rm(path)
|
44
|
+
end
|
45
|
+
|
46
|
+
if File.exists?(path)
|
47
|
+
f = File.open(path, "r")
|
48
|
+
result = Marshal.load(f)
|
49
|
+
f.close
|
50
|
+
return result
|
51
|
+
else
|
52
|
+
return nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Delete the value for the given key from the cache
|
57
|
+
def delete(key)
|
58
|
+
FileUtils.rm(get_path(key))
|
59
|
+
end
|
60
|
+
|
61
|
+
# Delete ALL data from the cache, regardless of expiry time
|
62
|
+
def clear
|
63
|
+
if File.exists?(get_root)
|
64
|
+
FileUtils.rm_r(get_root)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Delete all expired data from the cache
|
69
|
+
def purge
|
70
|
+
@t_purge = Time.new
|
71
|
+
purge_dir(get_root) if @expiry > 0
|
72
|
+
end
|
73
|
+
|
74
|
+
#-------- private methods ---------------------------------
|
75
|
+
private
|
76
|
+
def get_path(key)
|
77
|
+
md5 = Digest::MD5.hexdigest(key.to_s).to_s
|
78
|
+
|
79
|
+
dir = File.join(get_root, md5.split(//)[0..@depth - 1])
|
80
|
+
FileUtils.mkdir_p(dir)
|
81
|
+
return File.join(dir, md5)
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_root
|
85
|
+
if @root == nil
|
86
|
+
@root = File.join(@root_dir, @domain)
|
87
|
+
end
|
88
|
+
return @root
|
89
|
+
end
|
90
|
+
|
91
|
+
def purge_dir(dir)
|
92
|
+
Dir.foreach(dir) do |f|
|
93
|
+
next if f =~ /^\.\.?$/
|
94
|
+
path = File.join(dir, f)
|
95
|
+
if File.directory?(path)
|
96
|
+
purge_dir(path)
|
97
|
+
elsif @t_purge - File.new(path).mtime >= @expiry
|
98
|
+
FileUtils.rm(path)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Delete empty directories
|
103
|
+
if Dir.entries(dir).delete_if{|e| e =~ /^\.\.?$/}.empty?
|
104
|
+
Dir.delete(dir)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.push(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
|
5
|
+
require 'filecache'
|
6
|
+
require 'test/unit'
|
7
|
+
|
8
|
+
class TestFileCache < Test::Unit::TestCase
|
9
|
+
KEY1 = "key1"
|
10
|
+
VALUE1 = "value1"
|
11
|
+
VALUE2 = "value2"
|
12
|
+
|
13
|
+
def test_basic
|
14
|
+
f = FileCache.new("unit-test", "/tmp")
|
15
|
+
|
16
|
+
f.set(KEY1, VALUE1)
|
17
|
+
assert_equal(f.get(KEY1), VALUE1, "set/get")
|
18
|
+
|
19
|
+
f.delete(KEY1)
|
20
|
+
assert_nil(f.get(KEY1), "delete")
|
21
|
+
|
22
|
+
f.set(KEY1, VALUE1)
|
23
|
+
assert_equal(f.get(KEY1), VALUE1, "set on previously deleted key")
|
24
|
+
|
25
|
+
f.set(KEY1, VALUE2)
|
26
|
+
assert_equal(f.get(KEY1), VALUE2, "set new value on previously set key")
|
27
|
+
|
28
|
+
f.purge
|
29
|
+
assert_equal(f.get(KEY1), VALUE2, "purge has no effect on cache with expiry=0")
|
30
|
+
|
31
|
+
f.clear
|
32
|
+
assert_nil(f.get(KEY1), "clear removes previously set value")
|
33
|
+
end
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: filecache
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Simon Whitaker
|
8
|
+
autorequire: filecache
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-07-07 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: sw@netcetera.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
- COPYING
|
25
|
+
files:
|
26
|
+
- lib/filecache.rb
|
27
|
+
- tests/test_filecache.rb
|
28
|
+
- README
|
29
|
+
- COPYING
|
30
|
+
has_rdoc: true
|
31
|
+
homepage: http://netcetera.org
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: "0"
|
42
|
+
version:
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: "0"
|
48
|
+
version:
|
49
|
+
requirements: []
|
50
|
+
|
51
|
+
rubyforge_project:
|
52
|
+
rubygems_version: 1.2.0
|
53
|
+
signing_key:
|
54
|
+
specification_version: 2
|
55
|
+
summary: A file-based caching library
|
56
|
+
test_files:
|
57
|
+
- tests/test_filecache.rb
|