file_pool 0.2.0 → 0.3.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/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:
|