file_pool 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +22 -5
- data/lib/file_pool/version.rb +1 -1
- data/lib/file_pool.rb +165 -78
- data/test/test_file_pool.rb +5 -2
- data/test/test_file_pool_encryption.rb +100 -0
- metadata +37 -71
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 73a8c6c237185c15b0385f37405649571c3668db
|
4
|
+
data.tar.gz: 9f6a23a5c02abe1394956d3ee74590f477224595
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f348a4491e460c47cc3d5e288cb365aa3ba965cebcfd2858646c03e680a587951538406a3615e3e1d7ba9371c6f058e47c3578cae725ce69612b146691bf3102
|
7
|
+
data.tar.gz: fb19cf25d543f63d3c364a96bd08f9ad2a4a52d56c46826eb78fec6361419faceab4c8865d6a37a6948fae964c0c78ee9aa95ce798527593b53726cc69f045f5
|
data/README.md
CHANGED
@@ -2,8 +2,9 @@
|
|
2
2
|
|
3
3
|
FilePool helps to manage a large number of files in a Ruby project. It
|
4
4
|
takes care of the storage of files in a balanced directory tree and
|
5
|
-
generates unique identifiers for all files.
|
6
|
-
|
5
|
+
generates unique identifiers for all files.
|
6
|
+
|
7
|
+
Optionally FilePool can use symmetric encryption for all files managed.
|
7
8
|
|
8
9
|
FilePool does not deal with file meta information. It's only purpose
|
9
10
|
is to return a file's location given a file identifier, which was
|
@@ -17,7 +18,7 @@ the 3 first hexadecimal digits of a UUID as path. For example:
|
|
17
18
|
0/c/f/0cfb082a-fd57-490c-978b-e47d5948bc8b
|
18
19
|
6/1/d/61ddfe33-13f3-4f71-9234-5fbbf5c4fc2c
|
19
20
|
|
20
|
-
FilePool is tested with Ruby 1.8.7 and
|
21
|
+
FilePool is tested with Ruby 1.8.7, 2.0.0 and 2.2.1.
|
21
22
|
|
22
23
|
## Installation
|
23
24
|
|
@@ -36,13 +37,14 @@ Or install it yourself as:
|
|
36
37
|
## Usage
|
37
38
|
|
38
39
|
### Setup
|
39
|
-
|
40
|
+
|
41
|
+
Set up the root path under which all files will reside:
|
40
42
|
|
41
43
|
FilePool.setup '/var/lib/files'
|
42
44
|
|
43
45
|
In a Rails project the file pool setup would be placed in an intializer:
|
44
46
|
|
45
|
-
config/initializers/file_pool.
|
47
|
+
config/initializers/file_pool.rb
|
46
48
|
|
47
49
|
### Example Usage
|
48
50
|
|
@@ -58,6 +60,21 @@ Remove a file
|
|
58
60
|
|
59
61
|
FilePool.remove(fid)
|
60
62
|
|
63
|
+
### Encryption
|
64
|
+
|
65
|
+
FilePool can store files symmetrically encrypted using AES-256-CBC. In order to
|
66
|
+
switch FilePool to encryption pass the location of a file containing the secret:
|
67
|
+
|
68
|
+
FilePool.setup '/var/lib/files', :secrets_file => '/etc/filepool_secrets.yml'
|
69
|
+
|
70
|
+
This file is initialized with a random secret if it is not present. The key and
|
71
|
+
initialization vector stored there will be used for all files. When you request
|
72
|
+
a file with the FilePool#path method FilePool decrypts it first and returns a path to
|
73
|
+
the decrypted file.
|
74
|
+
|
75
|
+
In encryption mode the filepool root directory name is suffixed with "_secured",
|
76
|
+
so that mixed mode (encryped and plain) operation is possible.
|
77
|
+
|
61
78
|
### Maintenance
|
62
79
|
|
63
80
|
FilePool has a straight forward way of storing files. It doesn't use
|
data/lib/file_pool/version.rb
CHANGED
data/lib/file_pool.rb
CHANGED
@@ -1,77 +1,10 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
require 'file_pool/version'
|
3
3
|
require 'uuidtools'
|
4
|
-
|
5
|
-
|
4
|
+
require 'tempfile'
|
5
|
+
require 'openssl'
|
6
|
+
require 'yaml'
|
6
7
|
|
7
|
-
== Introduction
|
8
|
-
|
9
|
-
FilePool helps to manage a large number of files in a Ruby project. It
|
10
|
-
takes care of the storage of files in a balanced directory tree and
|
11
|
-
generates unique identifiers for all files. It also comes in handy
|
12
|
-
when delaing with only a few files.
|
13
|
-
|
14
|
-
FilePool does not deal with file meta information. It's only purpose
|
15
|
-
is to return a file's location given a file identifier, which was
|
16
|
-
generated when the file was added to the pool.
|
17
|
-
|
18
|
-
The identifiers are strings of UUID Type 4 (random), which are also
|
19
|
-
used as file names. The directory tree is a 3 level structure using
|
20
|
-
the 3 first hexadecimal digits of a UUID as path. For example:
|
21
|
-
|
22
|
-
0/d/6/0d6f8dd9-8deb-4500-bb85-2d0796241963
|
23
|
-
0/c/f/0cfb082a-fd57-490c-978b-e47d5948bc8b
|
24
|
-
6/1/d/61ddfe33-13f3-4f71-9234-5fbbf5c4fc2c
|
25
|
-
|
26
|
-
== Examples
|
27
|
-
|
28
|
-
=== Setup
|
29
|
-
The root path and optionally a Logger must be defined:
|
30
|
-
|
31
|
-
FilePool.setup '/var/lib/files'
|
32
|
-
|
33
|
-
In a Rails project the file pool setup should be placed in an intializer:
|
34
|
-
|
35
|
-
config/initializers/file_pool.rb
|
36
|
-
|
37
|
-
=== Usage
|
38
|
-
|
39
|
-
Adding files (perhaps after completed upload)
|
40
|
-
|
41
|
-
fid = FilePool.add('/Temp/p348dvhn4')
|
42
|
-
|
43
|
-
Get location of previously added file
|
44
|
-
|
45
|
-
path = FilePool.path(fid)
|
46
|
-
|
47
|
-
Remove a file
|
48
|
-
|
49
|
-
FilePool.remove(fid)
|
50
|
-
|
51
|
-
== Maintenance
|
52
|
-
|
53
|
-
FilePool has a straight forward way of storing files. It doesn't use
|
54
|
-
any form of index. As long as you stick to directory structure
|
55
|
-
outlined above you can:
|
56
|
-
|
57
|
-
* move the entire pool somewhere else
|
58
|
-
* split the pool using symbolic links or mount points to remote file systems
|
59
|
-
* merge file pools by copying them into one
|
60
|
-
|
61
|
-
There is no risk of overwriting, because UUID type 4 file names are
|
62
|
-
unique. (up to an extremely small collision probability).
|
63
|
-
|
64
|
-
== Notes
|
65
|
-
|
66
|
-
Make sure to store the generated file identifiers safely. There is no
|
67
|
-
way of identifying a file again when it's ID is lost. In doubt generate a hash
|
68
|
-
value from the file and store it somewhere else.
|
69
|
-
|
70
|
-
For large files the pool root should be on the same file system as the files
|
71
|
-
added to the pool. Then adding a file returns immediately. Otherwise
|
72
|
-
files will be copied which may take a significant time.
|
73
|
-
|
74
|
-
=end
|
75
8
|
module FilePool
|
76
9
|
|
77
10
|
class InvalidFileId < Exception; end
|
@@ -84,8 +17,12 @@ module FilePool
|
|
84
17
|
#
|
85
18
|
# root (String)::
|
86
19
|
# absolute path of the file pool's root directory under which all files will be stored.
|
87
|
-
|
20
|
+
# config_file_path (String)::
|
21
|
+
# path to the config file of the filepool.
|
22
|
+
def self.setup root, options={}
|
88
23
|
@@root = root
|
24
|
+
@@crypted_mode = false
|
25
|
+
configure options[:secrets_file]
|
89
26
|
end
|
90
27
|
|
91
28
|
#
|
@@ -110,7 +47,12 @@ module FilePool
|
|
110
47
|
newid = uuid
|
111
48
|
target = path newid
|
112
49
|
|
113
|
-
|
50
|
+
if @@crypted_mode
|
51
|
+
FileUtils.mkpath(id2dir_secured newid)
|
52
|
+
path = crypt(path)
|
53
|
+
else
|
54
|
+
FileUtils.mkpath(id2dir newid)
|
55
|
+
end
|
114
56
|
FileUtils.link(path, target)
|
115
57
|
|
116
58
|
return newid
|
@@ -142,19 +84,52 @@ module FilePool
|
|
142
84
|
end
|
143
85
|
|
144
86
|
#
|
145
|
-
# Return the path
|
87
|
+
# Return the file's path corresponding to the passed file ID, no matter if it
|
88
|
+
# exists or not. In encrypting mode the file is first decrypted and the
|
89
|
+
# returned path will point to a temporary location of the decrypted file.
|
90
|
+
#
|
91
|
+
# To get the path of the encrypted file pass :decrypt => false, as an option.
|
146
92
|
#
|
147
93
|
# === Parameters:
|
148
94
|
#
|
149
95
|
# fid (String)::
|
150
96
|
# File ID which was generated by a previous #add operation.
|
151
97
|
#
|
98
|
+
# options (Hash)::
|
99
|
+
# :decrypt (true,false) In encryption mode don't decrypt, but return the encrypted file's path. Defaults to +true+.
|
100
|
+
#
|
152
101
|
# === Return Value:
|
153
102
|
#
|
154
|
-
# :: *String*, absolute path of the file in the pool.
|
155
|
-
def self.path fid
|
103
|
+
# :: *String*, absolute path of the file in the pool or to temporary location if it was decrypted.
|
104
|
+
def self.path fid, options={}
|
105
|
+
options[:decrypt] = true unless options[:decrypt] == false
|
106
|
+
|
156
107
|
raise InvalidFileId unless valid?(fid)
|
157
|
-
|
108
|
+
|
109
|
+
# file present in pool?
|
110
|
+
if File.file?(id2dir_secured(fid) + "/#{fid}")
|
111
|
+
# present in secured tree
|
112
|
+
if @@crypted_mode
|
113
|
+
if options[:decrypt]
|
114
|
+
# return path of decrypted file (tmp path)
|
115
|
+
decrypt id2dir_secured(fid) + "/#{fid}"
|
116
|
+
else
|
117
|
+
id2dir_secured(fid) + "/#{fid}"
|
118
|
+
end
|
119
|
+
else
|
120
|
+
id2dir_secured(fid) + "/#{fid}"
|
121
|
+
end
|
122
|
+
elsif File.file?(id2dir(fid) + "/#{fid}")
|
123
|
+
# present in plain tree
|
124
|
+
id2dir(fid) + "/#{fid}"
|
125
|
+
else
|
126
|
+
# not present
|
127
|
+
if @@crypted_mode
|
128
|
+
id2dir_secured(fid) + "/#{fid}"
|
129
|
+
else
|
130
|
+
id2dir(fid) + "/#{fid}"
|
131
|
+
end
|
132
|
+
end
|
158
133
|
end
|
159
134
|
|
160
135
|
#
|
@@ -166,7 +141,7 @@ module FilePool
|
|
166
141
|
# fid (String)::
|
167
142
|
# File ID which was generated by a previous #add operation.
|
168
143
|
def self.remove! fid
|
169
|
-
FileUtils.rm path(fid)
|
144
|
+
FileUtils.rm path(fid, :decrypt => false)
|
170
145
|
end
|
171
146
|
|
172
147
|
#
|
@@ -204,7 +179,8 @@ module FilePool
|
|
204
179
|
# Time and Date of last add operation
|
205
180
|
|
206
181
|
def self.stat
|
207
|
-
all_files = Dir.glob("#{root}/*/*/*/*")
|
182
|
+
all_files = Dir.glob("#{root}_secured/*/*/*/*")
|
183
|
+
all_files << Dir.glob("#{root}/*/*/*/*")
|
208
184
|
all_stats = all_files.map{|f| File.stat(f) }
|
209
185
|
|
210
186
|
{
|
@@ -227,6 +203,11 @@ module FilePool
|
|
227
203
|
"#{root}/#{fid[0,1]}/#{fid[1,1]}/#{fid[2,1]}"
|
228
204
|
end
|
229
205
|
|
206
|
+
# secured path from fid without file name
|
207
|
+
def self.id2dir_secured fid
|
208
|
+
"#{root}_secured/#{fid[0,1]}/#{fid[1,1]}/#{fid[2,1]}"
|
209
|
+
end
|
210
|
+
|
230
211
|
# return a new UUID type 4 (random) as String
|
231
212
|
def self.uuid
|
232
213
|
UUIDTools::UUID.random_create.to_s
|
@@ -250,4 +231,110 @@ module FilePool
|
|
250
231
|
(sortedarr[medpt1] + sortedarr[medpt2]).to_f / 2
|
251
232
|
end
|
252
233
|
|
234
|
+
#
|
235
|
+
# Crypt a file and store the result in the temp.
|
236
|
+
#
|
237
|
+
# Returns the path to the crypted file.
|
238
|
+
#
|
239
|
+
# === Parameters:
|
240
|
+
#
|
241
|
+
# path (String)::
|
242
|
+
# path of the file to crypt.
|
243
|
+
#
|
244
|
+
# === Return Value:
|
245
|
+
#
|
246
|
+
# :: *String*Path and name of the crypted file.
|
247
|
+
def self.crypt path
|
248
|
+
# Crypt the file in the temp folder and copy after
|
249
|
+
cipher = create_cipher
|
250
|
+
result = Tempfile.new uuid
|
251
|
+
crypted_content = cipher.update(File.read(path))
|
252
|
+
crypted_content << cipher.final
|
253
|
+
result.write crypted_content
|
254
|
+
result.close
|
255
|
+
result.path
|
256
|
+
end
|
257
|
+
|
258
|
+
#
|
259
|
+
# Decrypt a file and give a path to it.
|
260
|
+
#
|
261
|
+
# Returns the path to the decrypted file.
|
262
|
+
#
|
263
|
+
# === Parameters:
|
264
|
+
#
|
265
|
+
# path (String)::
|
266
|
+
# path of the file to decrypt.
|
267
|
+
#
|
268
|
+
# === Return Value:
|
269
|
+
#
|
270
|
+
# :: *String*Path and name of the crypted file.
|
271
|
+
def self.decrypt path
|
272
|
+
decipher = create_decipher
|
273
|
+
# Now decrypt the data:
|
274
|
+
decrypted_content = decipher.update(File.read(path))
|
275
|
+
decrypted_content << decipher.final
|
276
|
+
# Put it in a temp file
|
277
|
+
output = Tempfile.new uuid
|
278
|
+
output.write decrypted_content
|
279
|
+
output.open
|
280
|
+
output.path
|
281
|
+
end
|
282
|
+
|
283
|
+
#
|
284
|
+
# Creates a cipher to encrypt data.
|
285
|
+
#
|
286
|
+
# Returns the cipher.
|
287
|
+
#
|
288
|
+
# === Return Value:
|
289
|
+
#
|
290
|
+
# :: *Openssl*Cipher object.
|
291
|
+
def self.create_cipher
|
292
|
+
cipher = OpenSSL::Cipher::AES.new(256, :CBC)
|
293
|
+
cipher.encrypt
|
294
|
+
cipher.key = @@key
|
295
|
+
cipher.iv = @@iv
|
296
|
+
cipher
|
297
|
+
end
|
298
|
+
|
299
|
+
#
|
300
|
+
# Creates a decipher to decrypt data.
|
301
|
+
#
|
302
|
+
# Returns the decipher.
|
303
|
+
#
|
304
|
+
# === Return Value:
|
305
|
+
#
|
306
|
+
# :: *Openssl*Cipher object
|
307
|
+
def self.create_decipher
|
308
|
+
decipher = OpenSSL::Cipher::AES.new(256, :CBC)
|
309
|
+
decipher.decrypt
|
310
|
+
decipher.key = @@key
|
311
|
+
decipher.iv = @@iv
|
312
|
+
decipher
|
313
|
+
end
|
314
|
+
|
315
|
+
#
|
316
|
+
# Retrieves configuration from config file or creates
|
317
|
+
# a new one in case there's none available.
|
318
|
+
#
|
319
|
+
def self.configure config_file
|
320
|
+
unless config_file.nil?
|
321
|
+
@@crypted_mode = true
|
322
|
+
begin
|
323
|
+
config = YAML.load_file(config_file)
|
324
|
+
@@iv = config[:iv]
|
325
|
+
@@key = config[:key]
|
326
|
+
rescue Errno::ENOENT
|
327
|
+
cipher = OpenSSL::Cipher::AES.new(256, :CBC)
|
328
|
+
@@iv = cipher.random_iv
|
329
|
+
@@key = cipher.random_key
|
330
|
+
cipher.key = @@key
|
331
|
+
cfg = File.open(config_file, 'w')
|
332
|
+
cfg.write({:iv => @@iv, :key => @@key}.to_yaml)
|
333
|
+
cfg.close
|
334
|
+
File.chmod(0400, config_file)
|
335
|
+
rescue => other_error
|
336
|
+
raise "FilePool: Could not load secrets from #{config_file}: #{other_error}"
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
253
340
|
end
|
data/test/test_file_pool.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'test/unit'
|
5
|
+
require 'shoulda-context'
|
3
6
|
require 'file_pool'
|
4
7
|
|
5
8
|
class FilePoolTest < Test::Unit::TestCase
|
@@ -21,7 +24,7 @@ class FilePoolTest < Test::Unit::TestCase
|
|
21
24
|
assert UUIDTools::UUID.parse(fid).valid?
|
22
25
|
|
23
26
|
md5_orig = Digest::MD5.hexdigest(File.open(@test_dir+"/a").read)
|
24
|
-
md5_pooled = Digest::MD5.hexdigest(File.open(
|
27
|
+
md5_pooled = Digest::MD5.hexdigest(File.open(FilePool.path(fid)).read)
|
25
28
|
|
26
29
|
assert_equal md5_orig, md5_pooled
|
27
30
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'test/unit'
|
5
|
+
require 'shoulda-context'
|
6
|
+
require 'file_pool'
|
7
|
+
|
8
|
+
class FilePoolEncryptionTest < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@test_dir = "#{File.dirname(__FILE__)}/files"
|
12
|
+
@pool_root = "#{File.dirname(__FILE__)}/fp_root"
|
13
|
+
@file_pool_config = "#{File.dirname(__FILE__)}/file_pool_cfg.yml"
|
14
|
+
FilePool.setup @pool_root, :secrets_file => @file_pool_config
|
15
|
+
end
|
16
|
+
|
17
|
+
def teardown
|
18
|
+
FileUtils.rm_r(Dir.glob @pool_root+"/*")
|
19
|
+
FileUtils.rm_r(Dir.glob "#{@pool_root}_secured/*")
|
20
|
+
FileUtils.rm_r(Dir.glob @file_pool_config)
|
21
|
+
end
|
22
|
+
|
23
|
+
context "File Pool" do
|
24
|
+
should "store encrypted files" do
|
25
|
+
fid = FilePool.add(@test_dir+"/a")
|
26
|
+
|
27
|
+
assert UUIDTools::UUID.parse(fid).valid?
|
28
|
+
|
29
|
+
md5_orig = Digest::MD5.hexdigest(File.open(@test_dir+"/a").read)
|
30
|
+
md5_pooled = Digest::MD5.hexdigest(File.open(FilePool.path(fid)).read)
|
31
|
+
|
32
|
+
assert_equal md5_orig, md5_pooled
|
33
|
+
end
|
34
|
+
|
35
|
+
should "return path from stored encrypted files is in the tmp folder" do
|
36
|
+
|
37
|
+
fida = FilePool.add(@test_dir+"/a")
|
38
|
+
assert UUIDTools::UUID.parse(fida).valid?
|
39
|
+
|
40
|
+
fidb = FilePool.add(@test_dir+"/b")
|
41
|
+
assert UUIDTools::UUID.parse(fidb).valid?
|
42
|
+
|
43
|
+
fidc = FilePool.add(@test_dir+"/c")
|
44
|
+
assert UUIDTools::UUID.parse(fidc).valid?
|
45
|
+
|
46
|
+
fidd = FilePool.add!(@test_dir+"/d")
|
47
|
+
assert UUIDTools::UUID.parse(fidd).valid?
|
48
|
+
|
49
|
+
assert_equal Digest::MD5.hexdigest(File.open(@test_dir+"/a").read),
|
50
|
+
Digest::MD5.hexdigest(File.open(FilePool.path(fida)).read)
|
51
|
+
assert_equal Digest::MD5.hexdigest(File.open(@test_dir+"/b").read),
|
52
|
+
Digest::MD5.hexdigest(File.open(FilePool.path(fidb)).read)
|
53
|
+
assert_equal Digest::MD5.hexdigest(File.open(@test_dir+"/c").read),
|
54
|
+
Digest::MD5.hexdigest(File.open(FilePool.path(fidc)).read)
|
55
|
+
assert_equal Digest::MD5.hexdigest(File.open(@test_dir+"/d").read),
|
56
|
+
Digest::MD5.hexdigest(File.open(FilePool.path(fidd)).read)
|
57
|
+
|
58
|
+
assert_equal Dir.tmpdir, File.dirname(FilePool.path(fida))
|
59
|
+
assert_equal Dir.tmpdir, File.dirname(FilePool.path(fidb))
|
60
|
+
assert_equal Dir.tmpdir, File.dirname(FilePool.path(fidc))
|
61
|
+
assert_equal Dir.tmpdir,File.dirname( FilePool.path(fidd))
|
62
|
+
end
|
63
|
+
|
64
|
+
should "remove files from encrypted pool" do
|
65
|
+
|
66
|
+
fidb = FilePool.add(@test_dir+"/b")
|
67
|
+
fidc = FilePool.add!(@test_dir+"/c")
|
68
|
+
fidd = FilePool.add!(@test_dir+"/d")
|
69
|
+
|
70
|
+
path_c = FilePool.path(fidc, :decrypt => false)
|
71
|
+
FilePool.remove(fidc)
|
72
|
+
|
73
|
+
assert !File.exist?(path_c)
|
74
|
+
assert File.exist?(FilePool.path(fidb, :decrypt => false))
|
75
|
+
assert File.exist?(FilePool.path(fidd, :decrypt => false))
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
should "throw exceptions when using add! and remove! on failure in encrypted mode" do
|
80
|
+
assert_raises(FilePool::InvalidFileId) do
|
81
|
+
FilePool.remove!("invalid-id")
|
82
|
+
end
|
83
|
+
|
84
|
+
assert_raises(Errno::ENOENT) do
|
85
|
+
FilePool.remove!("61e9b2d1-1738-440d-9b3d-e3c64876f2b0")
|
86
|
+
end
|
87
|
+
|
88
|
+
assert_raises(Errno::ENOENT) do
|
89
|
+
FilePool.add!("/not/here/foo.png")
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
should "not throw exceptions when using add and remove on failure in encrypted mode" do
|
95
|
+
assert !FilePool.remove("invalid-id")
|
96
|
+
assert !FilePool.remove("61e9b2d1-1738-440d-9b3d-e3c64876f2b0")
|
97
|
+
assert !FilePool.add("/not/here/foo.png")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
metadata
CHANGED
@@ -1,103 +1,69 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: file_pool
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 2
|
9
|
-
- 0
|
10
|
-
version: 0.2.0
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
13
|
-
-
|
6
|
+
authors:
|
7
|
+
- robokopp (Robert Anniés)
|
14
8
|
autorequire:
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
21
|
-
name: shoulda
|
22
|
-
prerelease: false
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
-
none: false
|
25
|
-
requirements:
|
26
|
-
- - ">="
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
hash: 3
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
version: "0"
|
32
|
-
type: :development
|
33
|
-
version_requirements: *id001
|
34
|
-
- !ruby/object:Gem::Dependency
|
11
|
+
date: 2017-02-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
35
14
|
name: uuidtools
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
- - ~>
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
hash: 15
|
43
|
-
segments:
|
44
|
-
- 2
|
45
|
-
- 1
|
46
|
-
- 2
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
47
19
|
version: 2.1.2
|
48
20
|
type: :runtime
|
49
|
-
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.1.2
|
50
27
|
description: |
|
51
28
|
FilePool helps to manage a large number of files in a Ruby
|
52
29
|
project. It takes care of the storage of files in a balanced directory
|
53
30
|
tree and generates unique identifiers for all files.
|
54
|
-
|
55
|
-
email:
|
31
|
+
email:
|
56
32
|
- robokopp@fernwerk.net
|
57
33
|
executables: []
|
58
|
-
|
59
34
|
extensions: []
|
60
|
-
|
61
|
-
|
35
|
+
extra_rdoc_files:
|
36
|
+
- README.md
|
37
|
+
files:
|
62
38
|
- README.md
|
63
|
-
files:
|
64
39
|
- lib/file_pool.rb
|
65
40
|
- lib/file_pool/version.rb
|
66
|
-
- README.md
|
67
41
|
- test/test_file_pool.rb
|
42
|
+
- test/test_file_pool_encryption.rb
|
68
43
|
homepage: https://github.com/robokopp/file_pool
|
69
44
|
licenses: []
|
70
|
-
|
45
|
+
metadata: {}
|
71
46
|
post_install_message:
|
72
47
|
rdoc_options: []
|
73
|
-
|
74
|
-
require_paths:
|
48
|
+
require_paths:
|
75
49
|
- lib
|
76
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
-
|
78
|
-
requirements:
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
79
52
|
- - ">="
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
version: "0"
|
85
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
-
none: false
|
87
|
-
requirements:
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
88
57
|
- - ">="
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
|
91
|
-
segments:
|
92
|
-
- 0
|
93
|
-
version: "0"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
94
60
|
requirements: []
|
95
|
-
|
96
61
|
rubyforge_project:
|
97
|
-
rubygems_version:
|
62
|
+
rubygems_version: 2.4.5
|
98
63
|
signing_key:
|
99
|
-
specification_version:
|
64
|
+
specification_version: 4
|
100
65
|
summary: Manage a large number files in a pool
|
101
|
-
test_files:
|
66
|
+
test_files:
|
102
67
|
- test/test_file_pool.rb
|
68
|
+
- test/test_file_pool_encryption.rb
|
103
69
|
has_rdoc:
|