disk_store 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +38 -0
- data/README.md +70 -0
- data/Rakefile +7 -0
- data/disk_store.gemspec +28 -0
- data/lib/disk_store/version.rb +3 -0
- data/lib/disk_store.rb +84 -0
- data/spec/cassettes/DiskStore/web_resources/should_cache_the_result_of_a_web_resource_in_a_file.json +1 -0
- data/spec/disk_store_spec.rb +84 -0
- data/spec/spec_helper.rb +32 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 93990471281e2c963748cbebdfc8856ae2959ade
|
4
|
+
data.tar.gz: ca3fd4505c2c65f150b55a8fcbe5d0356f11a4a0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 67b15e61229c63c442e53495e4ea82a4ac8034c457b6d5513896c8bf0c9319836871b3bb618316dc5adace98787cfe1f02f311875f300b0c5d27d34c009473a7
|
7
|
+
data.tar.gz: b361398d2d4f22802061480be065f8c1002ceb43e7dbafaa1cbc9946f55724336945ecb1052867bef378f0af6390ee8c1c3593bd6558ba84391d3eee6b60de9f
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
disk_store (0.0.1)
|
5
|
+
rake
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
addressable (2.3.5)
|
11
|
+
crack (0.4.1)
|
12
|
+
safe_yaml (~> 0.9.0)
|
13
|
+
diff-lcs (1.2.5)
|
14
|
+
multi_json (1.8.4)
|
15
|
+
rake (10.1.1)
|
16
|
+
rspec (2.14.1)
|
17
|
+
rspec-core (~> 2.14.0)
|
18
|
+
rspec-expectations (~> 2.14.0)
|
19
|
+
rspec-mocks (~> 2.14.0)
|
20
|
+
rspec-core (2.14.7)
|
21
|
+
rspec-expectations (2.14.5)
|
22
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
23
|
+
rspec-mocks (2.14.5)
|
24
|
+
safe_yaml (0.9.7)
|
25
|
+
vcr (2.8.0)
|
26
|
+
webmock (1.13.0)
|
27
|
+
addressable (>= 2.2.7)
|
28
|
+
crack (>= 0.3.2)
|
29
|
+
|
30
|
+
PLATFORMS
|
31
|
+
ruby
|
32
|
+
|
33
|
+
DEPENDENCIES
|
34
|
+
disk_store!
|
35
|
+
multi_json
|
36
|
+
rspec
|
37
|
+
vcr
|
38
|
+
webmock
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# DiskStore
|
2
|
+
|
3
|
+
DiskStore is a way of caching large files to disk in Ruby. Unlike ActiveSupport::Cache::Store,
|
4
|
+
which is designed primarily for storing values of strings, DiskStore is meant for
|
5
|
+
caching files to disk.
|
6
|
+
|
7
|
+
DiskStore stores the files on disk in a very similar way to Rails' [ActiveSupport::Cache::FileStore](http://api.rubyonrails.org/classes/ActiveSupport/Cache/FileStore.html).
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'disk_store'
|
13
|
+
```
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
You should use this in a similar way that you would use most Ruby caching libraries. Instead
|
18
|
+
of passing around string values to cache, you should pass around IO objects. Here are a few
|
19
|
+
examples.
|
20
|
+
|
21
|
+
### Setup
|
22
|
+
|
23
|
+
DiskStore requires a directory to to store the files.
|
24
|
+
|
25
|
+
It takes a single parameter, which is the directory at which to store the cached files.
|
26
|
+
If no parameter is specified, it uses the current directory.
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
cache = DiskStore.new("path/to/my/cache/directory")
|
30
|
+
```
|
31
|
+
|
32
|
+
### Reading
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
cache = DiskStore.new
|
36
|
+
cached_file = cache.read("my_cache_key") #=> File.open('somewhere_on_disk')
|
37
|
+
```
|
38
|
+
|
39
|
+
### Writing
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
cache = DiskStore.new
|
43
|
+
cache.write("my_cache_key", File.open('file.psd', 'rb'))
|
44
|
+
```
|
45
|
+
|
46
|
+
### Fetching
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
cache = DiskStore.new
|
50
|
+
cache.fetch("my_cache_key") do
|
51
|
+
File.open('file.psd', 'rb')
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
This is where it gets cool. You can also feed it other IO classes.
|
56
|
+
Here we cache the result of a file download onto disk.
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
cache = DiskStore.new
|
60
|
+
cache.fetch("my_other_cache_key") do
|
61
|
+
open("https://layervault.com/cats.gif")
|
62
|
+
end
|
63
|
+
```
|
64
|
+
|
65
|
+
### Deleting
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
cache = DiskStore.new
|
69
|
+
cache.delete("my_cache_key") #=> true
|
70
|
+
```
|
data/Rakefile
ADDED
data/disk_store.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'disk_store/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "disk_store"
|
8
|
+
gem.version = DiskStore::VERSION
|
9
|
+
gem.authors = ["Kelly Sutton"]
|
10
|
+
gem.email = ["kelly@layervault.com"]
|
11
|
+
gem.description = %q{Cache files the smart way.}
|
12
|
+
gem.summary = %q{Cache fiels the smart way.}
|
13
|
+
gem.homepage = "http://cosmos.layervault.com/cache.html"
|
14
|
+
gem.license = 'MIT'
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($/).delete_if { |f| f.include?('examples/') }
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
|
21
|
+
gem.add_dependency "rake"
|
22
|
+
|
23
|
+
gem.test_files = Dir.glob("spec/**/*")
|
24
|
+
gem.add_development_dependency "multi_json"
|
25
|
+
gem.add_development_dependency "rspec"
|
26
|
+
gem.add_development_dependency "vcr"
|
27
|
+
gem.add_development_dependency "webmock"
|
28
|
+
end
|
data/lib/disk_store.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
class DiskStore
|
4
|
+
DIR_FORMATTER = "%03X"
|
5
|
+
FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
|
6
|
+
EXCLUDED_DIRS = ['.', '..'].freeze
|
7
|
+
|
8
|
+
def initialize(path=nil)
|
9
|
+
path ||= "."
|
10
|
+
@root_path = File.expand_path path
|
11
|
+
end
|
12
|
+
|
13
|
+
def read(key)
|
14
|
+
File.open(key_file_path(key), 'rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
def write(key, io)
|
18
|
+
file_path = key_file_path(key)
|
19
|
+
ensure_cache_path(File.dirname(file_path))
|
20
|
+
IO::copy_stream(io, File.open(file_path, 'wb'))
|
21
|
+
end
|
22
|
+
|
23
|
+
def exist?(key)
|
24
|
+
File.exist?(key_file_path(key))
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete(key)
|
28
|
+
file_path = key_file_path(key)
|
29
|
+
if exist?(key)
|
30
|
+
File.delete(file_path)
|
31
|
+
delete_empty_directories(File.dirname(file_path))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def fetch(key)
|
36
|
+
if block_given?
|
37
|
+
if exist?(key)
|
38
|
+
read(key)
|
39
|
+
else
|
40
|
+
io = yield
|
41
|
+
write(key, io)
|
42
|
+
read(key)
|
43
|
+
end
|
44
|
+
else
|
45
|
+
read(key)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# These methods were borrowed mostly from ActiveSupport::Cache::FileStore
|
52
|
+
|
53
|
+
# Translate a key into a file path.
|
54
|
+
def key_file_path(key)
|
55
|
+
fname = URI.encode_www_form_component(key)
|
56
|
+
hash = Zlib.adler32(fname)
|
57
|
+
hash, dir_1 = hash.divmod(0x1000)
|
58
|
+
dir_2 = hash.modulo(0x1000)
|
59
|
+
fname_paths = []
|
60
|
+
|
61
|
+
# Make sure file name doesn't exceed file system limits.
|
62
|
+
begin
|
63
|
+
fname_paths << fname[0, FILENAME_MAX_SIZE]
|
64
|
+
fname = fname[FILENAME_MAX_SIZE..-1]
|
65
|
+
end until fname.nil? || fname == ""
|
66
|
+
|
67
|
+
File.join(@root_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, *fname_paths)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Make sure a file path's directories exist.
|
71
|
+
def ensure_cache_path(path)
|
72
|
+
FileUtils.makedirs(path) unless File.exist?(path)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Delete empty directories in the cache.
|
76
|
+
def delete_empty_directories(dir)
|
77
|
+
return if File.realpath(dir) == File.realpath(@root_path)
|
78
|
+
if Dir.entries(dir).reject{ |f| EXCLUDED_DIRS.include?(f) }.empty?
|
79
|
+
Dir.delete(dir) rescue nil
|
80
|
+
delete_empty_directories(File.dirname(dir))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|