disk_store 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a169969f0a6815116676b69bccfada6d9c021c00
4
- data.tar.gz: 4047279dfc9bc369a3012e495b73a5d22e5e4f59
3
+ metadata.gz: e58eb0c302ae1fdb782a4dee97571d9579b9981e
4
+ data.tar.gz: 7469c9c689d3658ffa1fe50a30ae5c7c0c9142de
5
5
  SHA512:
6
- metadata.gz: bfbbe98e30b10f1790fc29d4136df196063eccacbf7d47dd2f28bca6e62e130c88136d79f41ceb9b2d33e341b91e5a4852803f5155ec3789ca342846fcdcbf79
7
- data.tar.gz: d9d4184f527e83e14102b6347bb40906047581f84497bdc21533246c9aefeb8b9fd8e53ed4228874057cd521925630b8c40ba98199a6bd59d8d85fc864f23d9b
6
+ metadata.gz: 51c2e56da273a2984c84fe2805642b0cfe95960198a96e1392fa2716cd7d0eb1602675269074532c3745329a1e09e7c1a38e0a147ff5723c2fae0518078a1caf
7
+ data.tar.gz: 50437b7e690559f8c90bac340f7522df7fc08f159bf1946bd7aa428495ae3d18359aaff73d213340851bacd8f9d7ba0b2427f02969a4f88fe48b119c96a21f3e
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- disk_store (0.3.0)
4
+ disk_store (0.4.0)
5
5
  celluloid
6
6
  rake
7
7
 
@@ -1,3 +1,3 @@
1
1
  class DiskStore
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/disk_store.rb CHANGED
@@ -2,6 +2,8 @@ require 'open-uri'
2
2
  require 'disk_store/reaper'
3
3
 
4
4
  class DiskStore
5
+ class MD5DidNotMatch < StandardError; end
6
+
5
7
  DIR_FORMATTER = "%03X"
6
8
  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)
7
9
  EXCLUDED_DIRS = ['.', '..'].freeze
@@ -15,15 +17,20 @@ class DiskStore
15
17
  @reaper = Reaper.spawn_for(@root_path, @options)
16
18
  end
17
19
 
18
- def read(key)
19
- File.open(key_file_path(key), 'rb')
20
+ def read(key, md5 = nil)
21
+ fd = File.open(key_file_path(key), 'rb')
22
+ validate_file!(key_file_path(key), md5) if !md5.nil?
23
+ fd
24
+ rescue MD5DidNotMatch => e
25
+ delete(key)
26
+ raise e
20
27
  end
21
28
 
22
- def write(key, io)
29
+ def write(key, io, md5 = nil)
23
30
  file_path = key_file_path(key)
24
31
  ensure_cache_path(File.dirname(file_path))
25
32
 
26
- File.open(file_path, 'wb') do |f|
33
+ fd = File.open(file_path, 'wb') do |f|
27
34
  begin
28
35
  f.flock File::LOCK_EX
29
36
  IO::copy_stream(io, f)
@@ -34,6 +41,12 @@ class DiskStore
34
41
  f.flock File::LOCK_UN
35
42
  end
36
43
  end
44
+
45
+ validate_file!(file_path, md5) if !md5.nil?
46
+ fd
47
+ rescue MD5DidNotMatch => e
48
+ delete(key)
49
+ raise e
37
50
  end
38
51
 
39
52
  def exist?(key)
@@ -49,17 +62,17 @@ class DiskStore
49
62
  true
50
63
  end
51
64
 
52
- def fetch(key)
65
+ def fetch(key, md5 = nil)
53
66
  if block_given?
54
67
  if exist?(key)
55
- read(key)
68
+ read(key, md5)
56
69
  else
57
70
  io = yield
58
- write(key, io)
71
+ write(key, io, md5)
59
72
  read(key)
60
73
  end
61
74
  else
62
- read(key)
75
+ read(key, md5)
63
76
  end
64
77
  end
65
78
 
@@ -84,6 +97,13 @@ private
84
97
  File.join(@root_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, *fname_paths)
85
98
  end
86
99
 
100
+ def validate_file!(file_path, md5)
101
+ real_md5 = Digest::MD5.file(file_path).hexdigest
102
+ if md5 != real_md5
103
+ raise MD5DidNotMatch.new("MD5 mismatch. Expected: #{md5}, Actual: #{real_md5}")
104
+ end
105
+ end
106
+
87
107
  # Make sure a file path's directories exist.
88
108
  def ensure_cache_path(path)
89
109
  FileUtils.makedirs(path) unless File.exist?(path)
@@ -29,13 +29,39 @@ describe DiskStore do
29
29
  end
30
30
 
31
31
  context "#read" do
32
- it "reads from disk" do
32
+ before(:each) do
33
33
  cache.write(key, file)
34
34
  file.close
35
35
  file.unlink # ensure the tempfile is deleted
36
+ end
37
+
38
+ it "reads from disk" do
36
39
  expect(cache.read(key)).to be_an_instance_of(File)
37
40
  expect(cache.read(key).read).to eq file_contents
38
41
  end
42
+
43
+ describe "md5 validation" do
44
+ let(:expected_md5) { Digest::MD5.hexdigest(file_contents) }
45
+
46
+ it "validates the md5 when reading from disk" do
47
+ expect(cache).to receive(:validate_file!)
48
+ cache.read(key, expected_md5)
49
+ end
50
+
51
+ it "does not throw an exception when the md5 is correct" do
52
+ expect {
53
+ cache.read(key, expected_md5)
54
+ }.to_not raise_error
55
+ end
56
+
57
+ it "raises an exception and deletes the file when the md5 is incorrect" do
58
+ expect {
59
+ cache.read(key, "naw")
60
+ }.to raise_error(DiskStore::MD5DidNotMatch)
61
+
62
+ expect(cache.exist?(key)).to be_false
63
+ end
64
+ end
39
65
  end
40
66
 
41
67
  context "#write" do
@@ -47,6 +73,29 @@ describe DiskStore do
47
73
  file_path = Dir[File.join(@tmpdir, "**/*")].last
48
74
  expect(File.binread(file_path)).to eq file_contents
49
75
  end
76
+
77
+ describe "md5 validation" do
78
+ let(:expected_md5) { Digest::MD5.hexdigest(file_contents) }
79
+
80
+ it "validates md5 after writing to disk" do
81
+ expect(cache).to receive(:validate_file!)
82
+ cache.write(key, file, expected_md5)
83
+ end
84
+
85
+ it "does not throw an exception when the md5 is correct" do
86
+ expect {
87
+ cache.write(key, file, expected_md5)
88
+ }.to_not raise_error
89
+ end
90
+
91
+ it "raises an exception and deletes the file when the md5 is incorrect" do
92
+ expect {
93
+ cache.write(key, file, "i am incorrect")
94
+ }.to raise_error(DiskStore::MD5DidNotMatch)
95
+
96
+ expect(cache.exist?(key)).to be_false
97
+ end
98
+ end
50
99
  end
51
100
 
52
101
  context "#delete" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: disk_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kelly Sutton