disk_store 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
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
@@ -0,0 +1,7 @@
1
+ task :console do
2
+ require 'irb'
3
+ require 'irb/completion'
4
+ require 'disk_store'
5
+ ARGV.clear
6
+ IRB.start
7
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ class DiskStore
2
+ VERSION = "0.1.0"
3
+ 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