disk_store 0.1.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.
- 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
|