filestore 0.0.2 → 0.0.3
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.
- data/lib/filestore.rb +261 -72
- data/lib/log.rb +18 -29
- data/lib/memory_meta.rb +104 -0
- data/lib/meta_manager.rb +53 -0
- data/lib/multitenant_filestore.rb +148 -0
- data/test/tc_filestore.rb +24 -53
- data/test/tc_multitenant.rb +29 -0
- metadata +31 -16
- data/lib/action.rb +0 -56
- data/lib/meta.rb +0 -81
- data/test/move_from/test-file-to-move +0 -0
- data/test/tc_action.rb +0 -48
- data/test/tc_log.rb +0 -40
- data/test/tc_meta.rb +0 -101
- data/test/ts_gem.rb +0 -17
data/lib/filestore.rb
CHANGED
|
@@ -1,108 +1,297 @@
|
|
|
1
1
|
#
|
|
2
2
|
# filestore.rb
|
|
3
3
|
# @author Thomas Stätter
|
|
4
|
-
# @date
|
|
5
|
-
# @description
|
|
4
|
+
# @date 2012/11/07
|
|
5
|
+
# @description
|
|
6
6
|
#
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
require 'uuidtools'
|
|
11
|
-
require 'fileutils'
|
|
12
|
-
require 'action.rb'
|
|
8
|
+
require 'meta_manager.rb'
|
|
13
9
|
require 'log.rb'
|
|
14
|
-
require '
|
|
10
|
+
require 'etc'
|
|
11
|
+
require 'yaml'
|
|
12
|
+
require 'fileutils'
|
|
13
|
+
require 'uuidtools'
|
|
15
14
|
|
|
16
15
|
module FileStore
|
|
17
|
-
|
|
16
|
+
#
|
|
17
|
+
# Base exception class used for errors occurring in this module
|
|
18
|
+
#
|
|
18
19
|
class FileStoreException < Exception
|
|
19
20
|
end
|
|
20
|
-
|
|
21
|
-
class
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
21
|
+
#
|
|
22
|
+
# Main library class implementing a simple file store used for storing and managing
|
|
23
|
+
# arbitrary files
|
|
24
|
+
#
|
|
25
|
+
class SimpleFileStore
|
|
26
|
+
# Name of the lock file
|
|
27
|
+
STORE_LOCK_FILE = ".locked"
|
|
28
|
+
# Name of the meta file describing the current file store
|
|
29
|
+
META_FILE = "filestore.yaml"
|
|
30
|
+
# The base name of the file store directory
|
|
31
|
+
STORE_ROOT = 'filestore'
|
|
32
|
+
# The base name of the directory for storing deleted files
|
|
33
|
+
DELETED_ROOT = 'deleted'
|
|
34
|
+
# The base name of the directory storing files extracted from file store by a
|
|
35
|
+
# rollback action
|
|
36
|
+
ROLLBACK_ROOT = 'rollback'
|
|
37
|
+
#
|
|
38
|
+
# Accessors for important properties
|
|
39
|
+
#
|
|
40
|
+
attr_reader :metaManager, :rootPath, :storePath, :deletedPath, :rollbackPath, :metaFile
|
|
41
|
+
#
|
|
42
|
+
# Initializes a new instance of SimpleFileStore
|
|
43
|
+
# @param metaManager The meta data manager instance to be used by this store
|
|
44
|
+
# @param rootPath The path where the file store resides
|
|
45
|
+
#
|
|
46
|
+
def initialize(metaManager, rootPath = '.')
|
|
47
|
+
raise FileStoreException, "Root path already locked" if SimpleFileStore.is_directory_locked?(rootPath)
|
|
48
|
+
raise FileStoreException, "FileStore root path #{rootPath} doesn't exist" if not File.directory?(rootPath)
|
|
49
|
+
raise FileStoreException, "FileStore root path #{rootPath} isn't writable" if not File.writable?(rootPath)
|
|
50
|
+
raise FileStoreException, "No meta data manager given" if metaManager.nil?
|
|
51
|
+
raise FileStoreException, "Meta data manager must be of type FileStore::MetaManager" if not metaManager.is_a?(MetaManager)
|
|
52
|
+
|
|
53
|
+
@metaManager = metaManager
|
|
54
|
+
@rootPath = rootPath
|
|
55
|
+
@storePath = File.join(@rootPath, STORE_ROOT)
|
|
56
|
+
@deletedPath = File.join(@rootPath, DELETED_ROOT)
|
|
57
|
+
@rollbackPath = File.join(@rootPath, ROLLBACK_ROOT)
|
|
58
|
+
@metaFile = File.join(@rootPath, META_FILE)
|
|
59
|
+
@locked = false
|
|
34
60
|
|
|
35
61
|
begin
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
rescue
|
|
39
|
-
|
|
62
|
+
# Try to recover existing store
|
|
63
|
+
SimpleFileStore.recover_store(self)
|
|
64
|
+
rescue FileStoreException => e
|
|
65
|
+
# Recovery failed, trying to create the store
|
|
66
|
+
SimpleFileStore.create_store(self)
|
|
40
67
|
end
|
|
41
68
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
69
|
+
lock
|
|
70
|
+
end
|
|
71
|
+
#
|
|
72
|
+
# Adds a file to the store
|
|
73
|
+
# @param file The file to be stored
|
|
74
|
+
# @param meta Optional meta data to be stored along with the physical file
|
|
75
|
+
# @param shouldMove Determines wether to original file should be deleted
|
|
76
|
+
# @returns The newly created ID for the file
|
|
77
|
+
#
|
|
78
|
+
def add(file, meta = {}, shouldMove = true)
|
|
79
|
+
raise FileStoreException, "File #{file} not found" if not File.exists?(file)
|
|
80
|
+
raise FileStoreException, "File #{file} isn't readable" if not File.readable?(file)
|
|
81
|
+
raise FileStoreException, "File #{file} can't be removed" if not File.writable?(file)
|
|
45
82
|
|
|
46
|
-
|
|
47
|
-
|
|
83
|
+
meta = {} if meta.nil?
|
|
84
|
+
id = ""
|
|
85
|
+
|
|
86
|
+
begin
|
|
87
|
+
dir = SimpleFileStore.get_daily_directory(@storePath)
|
|
88
|
+
Logger.instance.logger.info "Adding file #{file} to directory #{dir}"
|
|
89
|
+
id = SimpleFileStore.get_id(self)
|
|
90
|
+
Logger.instance.logger.info "Using file id #{id}"
|
|
91
|
+
dstPath = File.join(dir, id)
|
|
92
|
+
Logger.instance.logger.info "Created destination path #{dstPath}"
|
|
93
|
+
|
|
94
|
+
shouldMove ? Logger.instance.logger.info("Moving file") : Logger.instance.logger.info("Copying file")
|
|
95
|
+
shouldMove ? (FileUtils.mv(file, dstPath)) : (FileUtils.copy_file(file, dstPath))
|
|
96
|
+
rescue Exception => e
|
|
97
|
+
raise FileStoreException, "Couldn't add file #{file} to store.", e.backtrace
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
meta[:path] = dstPath
|
|
101
|
+
@metaManager.add_or_update(id, meta)
|
|
102
|
+
|
|
103
|
+
return id
|
|
48
104
|
end
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
105
|
+
#
|
|
106
|
+
# Retrieves a file identified by it's ID
|
|
107
|
+
# @param id The files ID to retrieve
|
|
108
|
+
# @returns A hash of file object (:path) and corresponding meta data (:data)
|
|
109
|
+
# representing the file in the store
|
|
110
|
+
#
|
|
111
|
+
def get(id)
|
|
112
|
+
raise FileStoreException, "No ID given" if id.nil? or id == ''
|
|
113
|
+
raise FileStoreException, "No file for ID #{id} found" if not @metaManager.has_id?(id)
|
|
52
114
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
115
|
+
md = @metaManager.get_data(id)
|
|
116
|
+
path = md[:path]
|
|
117
|
+
|
|
118
|
+
raise FileStoreException, "No valid meta data found for ID #{id}" if md.nil? or not File.exists?(path)
|
|
119
|
+
|
|
120
|
+
return { :path => File.new(path), :data => md }
|
|
121
|
+
end
|
|
122
|
+
#
|
|
123
|
+
# Moves a file from the current to the deleted store
|
|
124
|
+
# @param id The ID identifying the file to be moved
|
|
125
|
+
#
|
|
126
|
+
def remove(id)
|
|
127
|
+
raise FileStoreException, "No file ID given for removal" if id == '' or id.nil?
|
|
128
|
+
raise FileStoreException, "File ID for removal not found in store" if not @metaManager.has_id?(id)
|
|
129
|
+
|
|
130
|
+
file = @metaManager.get_data(id)[:path]
|
|
131
|
+
|
|
132
|
+
begin
|
|
133
|
+
@metaManager.remove(id)
|
|
56
134
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
raise "Couldn't move file" if dstPath == ''
|
|
61
|
-
|
|
62
|
-
@metaManager << MetaData.new(id, { MetaData::FIELD_PATH => dstPath })
|
|
63
|
-
}
|
|
135
|
+
dir = SimpleFileStore.get_daily_directory(@deletedPath)
|
|
136
|
+
dstPath = File.join(dir, id)
|
|
64
137
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
raise FileStoreException, "
|
|
138
|
+
FileUtils.move(file, dstPath)
|
|
139
|
+
rescue Exception => e
|
|
140
|
+
raise FileStoreException, "Couldn't move file #{file} to deleted store.\n#{e.message}"
|
|
68
141
|
end
|
|
69
|
-
|
|
70
|
-
id
|
|
71
142
|
end
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
143
|
+
#
|
|
144
|
+
# Restores a file identified by it's id
|
|
145
|
+
# @param id The file ID
|
|
146
|
+
#
|
|
147
|
+
def restore(id)
|
|
148
|
+
raise FileStoreException, "No file ID given for restore" if id == '' or id.nil?
|
|
149
|
+
|
|
150
|
+
md = @metaManager.restore(id)
|
|
151
|
+
file = md[:path]
|
|
152
|
+
|
|
153
|
+
begin
|
|
154
|
+
dir = SimpleFileStore.get_daily_directory(@storePath)
|
|
155
|
+
dstPath = File.join(dir, id)
|
|
81
156
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
raise FileStoreException, "
|
|
157
|
+
FileUtils.move(file, dstPath)
|
|
158
|
+
rescue Exception => e
|
|
159
|
+
raise FileStoreException, "Couldn't restore file #{file} from deleted store.\n#{e.message}"
|
|
160
|
+
#
|
|
161
|
+
# Delete restored entry from metaManager
|
|
162
|
+
@metaManager.delete(id)
|
|
85
163
|
end
|
|
86
164
|
end
|
|
165
|
+
#
|
|
166
|
+
# Shuts down the file store
|
|
167
|
+
#
|
|
168
|
+
def shutdown
|
|
169
|
+
@metaManager.shutdown
|
|
170
|
+
|
|
171
|
+
release_lock
|
|
172
|
+
end
|
|
173
|
+
#
|
|
174
|
+
# Determines wether this store is locked
|
|
175
|
+
#
|
|
176
|
+
def locked?
|
|
177
|
+
return @locked
|
|
178
|
+
end
|
|
87
179
|
|
|
88
180
|
private
|
|
89
181
|
|
|
90
|
-
|
|
91
|
-
|
|
182
|
+
#
|
|
183
|
+
# Release the lock from the store
|
|
184
|
+
#
|
|
185
|
+
def release_lock
|
|
186
|
+
begin
|
|
187
|
+
File.delete File.join(@rootPath, STORE_LOCK_FILE)
|
|
188
|
+
@locked = false
|
|
189
|
+
rescue Exception => e
|
|
190
|
+
raise FileStoreException, "Couldn't release lock from #{@storePath}.\n#{e.message}"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
#
|
|
194
|
+
# Locks the current instance of file store as well as the corresponding path on
|
|
195
|
+
# the file system using a hidden file
|
|
196
|
+
#
|
|
197
|
+
def lock
|
|
198
|
+
begin
|
|
199
|
+
FileUtils.touch File.join(@rootPath, STORE_LOCK_FILE)
|
|
200
|
+
@locked = true
|
|
201
|
+
rescue Exception => e
|
|
202
|
+
raise FileStoreException, "Couldn't lock the store in path #{@storePath}.\n#{e.message}"
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
#
|
|
206
|
+
# Determines wether the store path is already locked by another instance
|
|
207
|
+
# of SimpleFileStore
|
|
208
|
+
#
|
|
209
|
+
def self.is_directory_locked?(rootPath)
|
|
210
|
+
return File.exists?(File.join(rootPath, SimpleFileStore::STORE_LOCK_FILE))
|
|
211
|
+
end
|
|
212
|
+
#
|
|
213
|
+
# Creates a new file ID
|
|
214
|
+
# @returns A string representing the file's ID
|
|
215
|
+
#
|
|
216
|
+
def self.get_id(store)
|
|
217
|
+
for i in 0..2 do
|
|
218
|
+
id = UUIDTools::UUID.random_create.to_s
|
|
219
|
+
|
|
220
|
+
return id if not store.metaManager.has_id?(id)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
raise FileStoreException, "Couldn't find unique ID"
|
|
224
|
+
end
|
|
225
|
+
#
|
|
226
|
+
# Returns the currently used directory
|
|
227
|
+
#
|
|
228
|
+
def self.get_daily_directory(base)
|
|
229
|
+
date = Date.today
|
|
230
|
+
dir = File.join(base, date.year.to_s, date.month.to_s, date.day.to_s)
|
|
92
231
|
|
|
93
232
|
begin
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
233
|
+
FileUtils.mkdir_p(dir) if not File.directory?(dir)
|
|
234
|
+
rescue Exception => e
|
|
235
|
+
raise FileStoreException, "Can't create daily directory #{dir}.\n#{e.message}"
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
raise FileStoreException, "Daily directory #{dir} isn't writable" if not File.writable?(dir)
|
|
239
|
+
return dir
|
|
240
|
+
end
|
|
241
|
+
#
|
|
242
|
+
# Setup for a new file store directory
|
|
243
|
+
# @param store The file store instance to set up
|
|
244
|
+
#
|
|
245
|
+
def self.create_store(store)
|
|
246
|
+
Logger.instance.logger.info "Trying to create store in #{store.storePath}"
|
|
247
|
+
# Try to create needed directories
|
|
248
|
+
begin
|
|
249
|
+
FileUtils.mkdir [store.storePath, store.deletedPath, store.rollbackPath]
|
|
250
|
+
rescue Errno::ENOENT => e
|
|
251
|
+
raise FileStoreException, "One ore more system directories couldn't be created.\n#{e.message}"
|
|
252
|
+
end
|
|
253
|
+
# Try to create hidden meta file
|
|
254
|
+
begin
|
|
255
|
+
meta = { :created_at => Date.today.strftime('%d.%m.%Y %H:%M:%S:%L'),
|
|
256
|
+
:storePath => store.storePath,
|
|
257
|
+
:deletedPath => store.deletedPath,
|
|
258
|
+
:rollbackPath => store.rollbackPath,
|
|
259
|
+
:created_by => Etc.getlogin
|
|
260
|
+
}
|
|
97
261
|
|
|
98
|
-
(
|
|
99
|
-
|
|
262
|
+
File.open(store.metaFile, "w+") do |fh|
|
|
263
|
+
YAML.dump(meta, fh)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
#
|
|
267
|
+
# Creation was successful
|
|
268
|
+
#
|
|
269
|
+
rescue Exception => e
|
|
270
|
+
raise FileStoreException, "Meta file #{store.metaFile} couldn't be created.\n#{e.message}"
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
#
|
|
274
|
+
# Recover an existing file store
|
|
275
|
+
# @param store The file store instance to recover
|
|
276
|
+
#
|
|
277
|
+
def self.recover_store(store)
|
|
278
|
+
# trying to recover existing file store
|
|
279
|
+
begin
|
|
280
|
+
Logger.instance.logger.info "Trying to recover store from #{store.metaFile}"
|
|
281
|
+
meta = YAML.load_file(store.metaFile)
|
|
282
|
+
|
|
283
|
+
raise FileStoreException, "Store directory not found" if not File.directory?(meta[:storePath])
|
|
284
|
+
raise FileStoreException, "Deleted directory not found" if not File.directory?(meta[:deletedPath])
|
|
285
|
+
raise FileStoreException, "Rollback directory not found" if not File.directory?(meta[:rollbackPath])
|
|
286
|
+
|
|
287
|
+
#
|
|
288
|
+
# Recovery was successful
|
|
289
|
+
#
|
|
100
290
|
rescue Exception => e
|
|
101
|
-
raise FileStoreException, "
|
|
102
|
-
ensure
|
|
103
|
-
return dstPath
|
|
291
|
+
raise FileStoreException, "Unable to recover file store from path #{store.rootPath}.\n#{e.message}"
|
|
104
292
|
end
|
|
105
293
|
end
|
|
294
|
+
|
|
106
295
|
end
|
|
107
296
|
|
|
108
297
|
end
|
data/lib/log.rb
CHANGED
|
@@ -1,39 +1,28 @@
|
|
|
1
1
|
#
|
|
2
2
|
# log.rb
|
|
3
3
|
# @author Thomas Stätter
|
|
4
|
-
# @date
|
|
5
|
-
# @description
|
|
4
|
+
# @date 2012/11/07
|
|
5
|
+
# @description
|
|
6
6
|
#
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
require '
|
|
7
|
+
require 'date'
|
|
8
|
+
require 'log4r'
|
|
9
|
+
require 'singleton'
|
|
10
10
|
|
|
11
11
|
module FileStore
|
|
12
|
-
|
|
13
|
-
class
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
begin
|
|
18
|
-
@logPath = File.join(path, FILE)
|
|
19
|
-
@logFile = File.new(@logPath, 'a+')
|
|
20
|
-
rescue StandardError => e
|
|
21
|
-
raise FileStoreException, "Initialization of logger failed", e.backtrace
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def <<(action)
|
|
26
|
-
return if not action.is_a? Action
|
|
27
|
-
|
|
28
|
-
@logFile.puts action.to_s
|
|
29
|
-
end
|
|
12
|
+
#
|
|
13
|
+
# Logging facility class
|
|
14
|
+
#
|
|
15
|
+
class Logger
|
|
16
|
+
include Singleton
|
|
30
17
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
18
|
+
attr_reader :logger
|
|
19
|
+
#
|
|
20
|
+
# Creates a new logging facility
|
|
21
|
+
#
|
|
22
|
+
def initialize
|
|
23
|
+
@logger = Log4r::Logger.new 'FileStore'
|
|
24
|
+
@logger.outputters = Log4r::StdoutOutputter.new(:level => Log4r::WARN,
|
|
25
|
+
:formatter => Log4r::PatternFormatter.new(:pattern => "[%l] %d - %m"))
|
|
37
26
|
end
|
|
38
27
|
end
|
|
39
28
|
|
data/lib/memory_meta.rb
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#
|
|
2
|
+
# memory_meta.rb
|
|
3
|
+
# @author Thomas Stätter
|
|
4
|
+
# @date 2012/11/08
|
|
5
|
+
# @description
|
|
6
|
+
#
|
|
7
|
+
require 'yaml'
|
|
8
|
+
require 'filestore.rb'
|
|
9
|
+
require 'meta_manager.rb'
|
|
10
|
+
require 'log.rb'
|
|
11
|
+
|
|
12
|
+
module FileStore
|
|
13
|
+
#
|
|
14
|
+
# Class implementing a memory based MetaManager
|
|
15
|
+
#
|
|
16
|
+
class MemoryMetaManager < MetaManager
|
|
17
|
+
# Constant defining the default file path
|
|
18
|
+
@@FILE = './meta.yaml'
|
|
19
|
+
# Accessor for the file to store data to
|
|
20
|
+
attr_reader :file
|
|
21
|
+
#
|
|
22
|
+
# Creates a new instance of MemoryMetaManager
|
|
23
|
+
# @param persistentFile The file where the manager class is persisted to
|
|
24
|
+
#
|
|
25
|
+
def initialize(persistentFile = '')
|
|
26
|
+
@data = Hash.new
|
|
27
|
+
@removed = Hash.new
|
|
28
|
+
@file = (persistentFile.nil? or persistentFile == '')? @@FILE : persistentFile
|
|
29
|
+
|
|
30
|
+
begin
|
|
31
|
+
if File.exists?(@file)
|
|
32
|
+
Logger.instance.logger.info "loading meta yaml from #{@file}"
|
|
33
|
+
@mm = YAML.load_file(@file) if File.exists?(@file)
|
|
34
|
+
Logger.instance.logger.info "Loaded meta yaml: #{@mm}"
|
|
35
|
+
@data = @mm[:current]
|
|
36
|
+
@removed = @mm[:removed]
|
|
37
|
+
else
|
|
38
|
+
Logger.instance.logger.info "Creating new meta store in #{@file}"
|
|
39
|
+
end
|
|
40
|
+
rescue Exception => e
|
|
41
|
+
raise FileStoreException, "Couldn't load meta data from file #{@file}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
45
|
+
#
|
|
46
|
+
# @see MetaManager::get_data
|
|
47
|
+
#
|
|
48
|
+
def get_data(id)
|
|
49
|
+
raise FileStoreException, "No meta data available for ID #{id}" if not @data.key?(id)
|
|
50
|
+
|
|
51
|
+
return @data[id]
|
|
52
|
+
end
|
|
53
|
+
#
|
|
54
|
+
# @see MetaManager::add_or_update
|
|
55
|
+
#
|
|
56
|
+
def add_or_update(id, metaData)
|
|
57
|
+
raise FileStoreException, "Only hashsets can be added" if not metaData.is_a?(Hash)
|
|
58
|
+
raise FileStoreException, "Only Strings can be used as keys" if not id.is_a?(String)
|
|
59
|
+
|
|
60
|
+
@data[id] = (@data.key?(id) ? @data[id].merge!(metaData) : @data[id] = metaData)
|
|
61
|
+
end
|
|
62
|
+
#
|
|
63
|
+
# @see MetaManager::remove
|
|
64
|
+
#
|
|
65
|
+
def remove(id)
|
|
66
|
+
raise FileStoreException, "Only Strings can be used as keys" if not id.is_a?(String)
|
|
67
|
+
raise FileStoreException, "ID #{id} not found in meta store" if not @data.key?(id)
|
|
68
|
+
|
|
69
|
+
@removed[id] = @data[id]
|
|
70
|
+
@data.delete(id)
|
|
71
|
+
end
|
|
72
|
+
#
|
|
73
|
+
# @see MetaManager::restore
|
|
74
|
+
#
|
|
75
|
+
def restore(id)
|
|
76
|
+
raise FileStoreException, "Only Strings can be used as keys" if not id.is_a?(String)
|
|
77
|
+
raise FileStoreException, "ID #{id} not found in deleted meta store" if not @removed.key?(id)
|
|
78
|
+
|
|
79
|
+
@data[id] = @removed[id]
|
|
80
|
+
@removed.delete(id)
|
|
81
|
+
end
|
|
82
|
+
#
|
|
83
|
+
# see MetaManager::shutdown
|
|
84
|
+
#
|
|
85
|
+
def shutdown
|
|
86
|
+
begin
|
|
87
|
+
File.open(@file, "w+") do |fh|
|
|
88
|
+
YAML.dump({:current => @data, :removed => @removed}, fh)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
@data = nil
|
|
92
|
+
rescue Exception => e
|
|
93
|
+
raise FileStoreException, "Couldn't serialize meta manager to file #{@file}.\n#{e.message}"
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
#
|
|
97
|
+
# @see MetaManager::has_id?
|
|
98
|
+
#
|
|
99
|
+
def has_id?(id)
|
|
100
|
+
@data.key?(id)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
end
|
data/lib/meta_manager.rb
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#
|
|
2
|
+
# meta_manager.rb
|
|
3
|
+
# @author Thomas Stätter
|
|
4
|
+
# @date 2012/11/08
|
|
5
|
+
# @description
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
module FileStore
|
|
9
|
+
#
|
|
10
|
+
# Base class for implementing a meta manager class. This class is used for storing
|
|
11
|
+
# and managing file meta data
|
|
12
|
+
#
|
|
13
|
+
class MetaManager
|
|
14
|
+
#
|
|
15
|
+
# Returns the data set identified by the given id
|
|
16
|
+
# @param ID The ID to be looked for
|
|
17
|
+
# @returns A hashset containing all stored meta data
|
|
18
|
+
#
|
|
19
|
+
def get_data(id)
|
|
20
|
+
end
|
|
21
|
+
#
|
|
22
|
+
# Removes a dataset from the collection
|
|
23
|
+
# @param id The key to identify the data to be deleted
|
|
24
|
+
#
|
|
25
|
+
def remove(id)
|
|
26
|
+
end
|
|
27
|
+
#
|
|
28
|
+
# Restores a previously deleted meta data set
|
|
29
|
+
# @param id The ID of the meta data set to be restored
|
|
30
|
+
#
|
|
31
|
+
def restore(id)
|
|
32
|
+
end
|
|
33
|
+
#
|
|
34
|
+
# Adds/updates a dataset to/in the collection
|
|
35
|
+
# @param id The key to identify the data
|
|
36
|
+
# @param metaData The actual meta data to store
|
|
37
|
+
#
|
|
38
|
+
def add_or_update(id, metaData)
|
|
39
|
+
end
|
|
40
|
+
#
|
|
41
|
+
# Shuts down the manager class and clears all used resources
|
|
42
|
+
#
|
|
43
|
+
def shutdown
|
|
44
|
+
end
|
|
45
|
+
#
|
|
46
|
+
# Determines wether a given ID is already in use
|
|
47
|
+
# @param id The ID to be tested
|
|
48
|
+
#
|
|
49
|
+
def has_id?(id)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#
|
|
2
|
+
# multitenant_filestore.rb
|
|
3
|
+
# @author Thomas Stätter
|
|
4
|
+
# @date 2012/11/21
|
|
5
|
+
# @description
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
require 'filestore.rb'
|
|
9
|
+
require 'memory_meta.rb'
|
|
10
|
+
require 'log.rb'
|
|
11
|
+
require 'uuidtools'
|
|
12
|
+
require 'fileutils'
|
|
13
|
+
require 'yaml'
|
|
14
|
+
|
|
15
|
+
module FileStore
|
|
16
|
+
#
|
|
17
|
+
# Class implementing a multitenant file store
|
|
18
|
+
#
|
|
19
|
+
class MultiTenantFileStore
|
|
20
|
+
attr_reader :stores
|
|
21
|
+
#
|
|
22
|
+
# Initializes a new instance of MultiTenantFileStore
|
|
23
|
+
# @rootPath The path where all file stores are located
|
|
24
|
+
#
|
|
25
|
+
def initialize(rootPath = '.')
|
|
26
|
+
raise FileStoreException, "Given root path isn't accessable" if not File.directory?(rootPath)
|
|
27
|
+
|
|
28
|
+
@rootPath = rootPath
|
|
29
|
+
@stores = MultiTenantFileStore.recover(rootPath)
|
|
30
|
+
end
|
|
31
|
+
#
|
|
32
|
+
# Creates a new file store for a tenant
|
|
33
|
+
# @param id The optional ID of the tenant. If omitted, an ID will be created
|
|
34
|
+
# automatically
|
|
35
|
+
# @returns The tenants ID
|
|
36
|
+
#
|
|
37
|
+
def create_tenant_store(id = '')
|
|
38
|
+
id = UUIDTools::UUID.random_create.to_s if id == '' or id.nil?
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
path = File.join(@rootPath, id)
|
|
42
|
+
FileUtils.mkdir path
|
|
43
|
+
mm = MemoryMetaManager.new(File.join(path, "meta.yaml"))
|
|
44
|
+
sfs = SimpleFileStore.new(mm, path)
|
|
45
|
+
|
|
46
|
+
@stores[id] = sfs
|
|
47
|
+
rescue Exception => e
|
|
48
|
+
raise FileStoreException, "Couldn't create multitenant store.\n#{e.message}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
return id
|
|
52
|
+
end
|
|
53
|
+
#
|
|
54
|
+
# Permanently removes a tenant's store
|
|
55
|
+
# @param id The tenant's ID
|
|
56
|
+
#
|
|
57
|
+
def remove_tenant_store(id)
|
|
58
|
+
raise FileStoreException, "Tenant #{id} can't be removed. Not registered." if not @stores.key?(id)
|
|
59
|
+
|
|
60
|
+
begin
|
|
61
|
+
@stores.delete(id)
|
|
62
|
+
FileUtils.remove_dir File.join(@rootPath, id)
|
|
63
|
+
rescue Exception => e
|
|
64
|
+
raise FileStoreException, "Couldn't delete tenant #{id}.\n#{e.message}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
#
|
|
68
|
+
# Returns the complete file store for a given tenant
|
|
69
|
+
# @param id The tenant's id
|
|
70
|
+
# @returns An instance of FileStore::SimpleFileStore
|
|
71
|
+
#
|
|
72
|
+
def get_tenant_store(id)
|
|
73
|
+
raise FileStoreException, "Tenant #{id} not registered. No file store given." if not @stores.key?(id)
|
|
74
|
+
|
|
75
|
+
return @stores[id]
|
|
76
|
+
end
|
|
77
|
+
#
|
|
78
|
+
# Adds a file to the tenant's store
|
|
79
|
+
# @param tenant The tenant's ID
|
|
80
|
+
# @param file The file to be added
|
|
81
|
+
# @param md Optional meta data
|
|
82
|
+
#
|
|
83
|
+
def add_to_tenant(tenant, file, md = {})
|
|
84
|
+
raise FileStoreException, "Tenant #{id} not registered. File can't be added." if not @stores.key?(tenant)
|
|
85
|
+
|
|
86
|
+
@stores[tenant].add(file, md)
|
|
87
|
+
end
|
|
88
|
+
#
|
|
89
|
+
# Removes a file from the tenant's store
|
|
90
|
+
# @param tenant The tenant's ID
|
|
91
|
+
# @param id The ID of the file to be removed
|
|
92
|
+
#
|
|
93
|
+
def remove_from_tenant(tenant, id)
|
|
94
|
+
raise FileStoreException, "Tenant #{id} not registered. File can't be removed." if not @stores.key?(tenant)
|
|
95
|
+
|
|
96
|
+
@stores[tenant].remove(id)
|
|
97
|
+
end
|
|
98
|
+
#
|
|
99
|
+
# Retrieves a file from the tenant's store
|
|
100
|
+
# @param tenant The tenant's ID
|
|
101
|
+
# @param id The file's ID
|
|
102
|
+
# @returns A hash containing the file object (:path) and the corresponding meta
|
|
103
|
+
# data (:data)
|
|
104
|
+
#
|
|
105
|
+
def get_from_tenant(tenant, id)
|
|
106
|
+
raise FileStoreException, "Given tenant #{tenant} isn't registered" if not @stores.key?(tenant)
|
|
107
|
+
|
|
108
|
+
return @stores[tenant].get(id)
|
|
109
|
+
end
|
|
110
|
+
#
|
|
111
|
+
# Shuts down this multitenant store
|
|
112
|
+
#
|
|
113
|
+
def shutdown
|
|
114
|
+
# Shut down any tenant store
|
|
115
|
+
@stores.values.each do |s|
|
|
116
|
+
s.shutdown
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
#
|
|
122
|
+
# Recovers a multitenant store
|
|
123
|
+
# @param rootPath The base path of the multitenant store
|
|
124
|
+
#
|
|
125
|
+
def self.recover(rootPath)
|
|
126
|
+
raise FileStoreException, "Root path #{rootPath} isn't a valid multitenant store" if not File.directory?(rootPath)
|
|
127
|
+
|
|
128
|
+
stores = {}
|
|
129
|
+
|
|
130
|
+
Dir.glob(File.join(rootPath, "*")).each { |e|
|
|
131
|
+
begin
|
|
132
|
+
if File.directory?(e)
|
|
133
|
+
tenant = File.basename(e)
|
|
134
|
+
mm = MemoryMetaManager.new(File.join(e, "meta.yaml"))
|
|
135
|
+
sfs = SimpleFileStore.new(mm, e)
|
|
136
|
+
|
|
137
|
+
stores[tenant] = sfs
|
|
138
|
+
end
|
|
139
|
+
rescue Exception => e
|
|
140
|
+
Logger.instance.logger.error "Couldn't create store for tenant #{tenant}.\n#{e.message}"
|
|
141
|
+
end
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return stores
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
end
|
data/test/tc_filestore.rb
CHANGED
|
@@ -1,60 +1,31 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
1
|
#
|
|
3
2
|
# tc_filestore.rb
|
|
4
3
|
# @author Thomas Stätter
|
|
5
|
-
# @date
|
|
6
|
-
# @description
|
|
4
|
+
# @date 2012/11/14
|
|
5
|
+
# @description Test script
|
|
7
6
|
#
|
|
8
|
-
#
|
|
7
|
+
# call: ruby -I ../lib/ tc_filestore.rb
|
|
8
|
+
#
|
|
9
|
+
require_relative '../lib/filestore.rb'
|
|
10
|
+
require_relative '../lib/memory_meta.rb'
|
|
11
|
+
|
|
12
|
+
include FileStore
|
|
13
|
+
|
|
14
|
+
basePath = "/Users/thomas/Documents/DEV/ruby/FileStoreGEM/test/store_test"
|
|
15
|
+
testFile = "/Users/thomas/Documents/DEV/ruby/FileStoreGEM/test/testfile.txt"
|
|
16
|
+
|
|
17
|
+
begin
|
|
18
|
+
mm = MemoryMetaManager.new(File.join(basePath, "meta.yaml"))
|
|
19
|
+
sfs = SimpleFileStore.new(mm, basePath)
|
|
20
|
+
id = sfs.add(testFile, {:hugo => 'boss'}, false)
|
|
9
21
|
|
|
10
|
-
|
|
11
|
-
|
|
22
|
+
puts id
|
|
23
|
+
puts "Enter something to finish"
|
|
24
|
+
enter = gets
|
|
12
25
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
assert_nothing_raised { FileStore::FileStore.new(BASE_PATH, FileStore::MemoryMetaManager.new {{}}) }
|
|
19
|
-
# shouldn't work, invalid path
|
|
20
|
-
assert_raise(FileStore::FileStoreException) { FileStore::FileStore.new("/some/invalid/path", FileStore::MemoryMetaManager.new {{}}) }
|
|
21
|
-
assert_raise(FileStore::FileStoreException) { FileStore::FileStore.new("", FileStore::MemoryMetaManager.new {{}}) }
|
|
22
|
-
assert_raise(FileStore::FileStoreException) { FileStore::FileStore.new(nil, FileStore::MemoryMetaManager.new {{}}) }
|
|
23
|
-
# shouldn't work, invalid MetaManager
|
|
24
|
-
assert_raise(FileStore::FileStoreException) { FileStore::FileStore.new(BASE_PATH, {}) }
|
|
25
|
-
assert_raise(FileStore::FileStoreException) { FileStore::FileStore.new(BASE_PATH, nil) }
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def test_add
|
|
29
|
-
mm = FileStore::MemoryMetaManager.new {
|
|
30
|
-
data = {}
|
|
31
|
-
|
|
32
|
-
Dir.glob(File.join(BASE_PATH, "**", "*")).each { |f|
|
|
33
|
-
if not File.directory?(f) and File.basename(f) != FileStore::Log::FILE then
|
|
34
|
-
data[File.basename(f)] = File.absolute_path(f)
|
|
35
|
-
end
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
data
|
|
39
|
-
}
|
|
40
|
-
fs = FileStore::FileStore.new(BASE_PATH, mm)
|
|
41
|
-
id = fs << './move_from/test-file-to-move'
|
|
42
|
-
|
|
43
|
-
assert_not_nil(id, "Returned ID can't be nil")
|
|
44
|
-
assert_not_same(id, '', "Returned ID can't be empty")
|
|
45
|
-
assert_instance_of(String, id, "Weird ID returned: #{id}")
|
|
46
|
-
# should raise an exception
|
|
47
|
-
assert_raise(FileStore::FileStoreException) { fs << '/some/invalid/file' }
|
|
48
|
-
|
|
49
|
-
# restore the test file
|
|
50
|
-
`touch ./move_from/test-file-to-move`
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def test_remove
|
|
54
|
-
fs = FileStore::FileStore.new(BASE_PATH, FileStore::MemoryMetaManager.new {{}})
|
|
55
|
-
|
|
56
|
-
# should raise an exception
|
|
57
|
-
assert_raise(FileStore::FileStoreException) { fs - 'adsfasdf' }
|
|
58
|
-
end
|
|
59
|
-
|
|
26
|
+
sfs.remove(id)
|
|
27
|
+
sfs.shutdown
|
|
28
|
+
rescue Exception => e
|
|
29
|
+
puts e
|
|
30
|
+
sfs.shutdown if not sfs.nil?
|
|
60
31
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#
|
|
2
|
+
# tc_multitenant.rb
|
|
3
|
+
# @author Thomas Stätter
|
|
4
|
+
# @date 2012/11/21
|
|
5
|
+
# @description Test script
|
|
6
|
+
#
|
|
7
|
+
# call: ruby -I ../lib/ tc_multitenant.rb
|
|
8
|
+
#
|
|
9
|
+
require_relative '../lib/multitenant_filestore.rb'
|
|
10
|
+
require 'fileutils'
|
|
11
|
+
|
|
12
|
+
include FileStore
|
|
13
|
+
|
|
14
|
+
basePath = "/Users/thomas/Documents/DEV/ruby/FileStoreGEM/test/multi_store_test"
|
|
15
|
+
testFile = "/Users/thomas/Documents/DEV/ruby/FileStoreGEM/test/testfile.txt"
|
|
16
|
+
|
|
17
|
+
begin
|
|
18
|
+
mtfs = MultiTenantFileStore.new basePath
|
|
19
|
+
tenant = mtfs.create_tenant_store
|
|
20
|
+
mtfs.add_to_tenant tenant, testFile, { :hugo => "boss" }
|
|
21
|
+
|
|
22
|
+
puts mtfs.stores
|
|
23
|
+
mtfs.shutdown
|
|
24
|
+
rescue Exception => e
|
|
25
|
+
puts e
|
|
26
|
+
puts e.backtrace
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
FileUtils.touch testFile
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: filestore
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-
|
|
12
|
+
date: 2012-11-22 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: uuidtools
|
|
@@ -33,6 +33,28 @@ dependencies:
|
|
|
33
33
|
- - ! '>='
|
|
34
34
|
- !ruby/object:Gem::Version
|
|
35
35
|
version: '2.0'
|
|
36
|
+
- !ruby/object:Gem::Dependency
|
|
37
|
+
name: log4r
|
|
38
|
+
requirement: !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ~>
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: 1.1.10
|
|
44
|
+
- - ! '>='
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
none: false
|
|
51
|
+
requirements:
|
|
52
|
+
- - ~>
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 1.1.10
|
|
55
|
+
- - ! '>='
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: '1.0'
|
|
36
58
|
description: Organizes a file storage using the file system and some meta data
|
|
37
59
|
email: thomas.staetter@gmail.com
|
|
38
60
|
executables: []
|
|
@@ -40,15 +62,12 @@ extensions: []
|
|
|
40
62
|
extra_rdoc_files: []
|
|
41
63
|
files:
|
|
42
64
|
- lib/filestore.rb
|
|
43
|
-
- lib/
|
|
65
|
+
- lib/memory_meta.rb
|
|
44
66
|
- lib/log.rb
|
|
45
|
-
- lib/
|
|
46
|
-
-
|
|
47
|
-
- test/tc_action.rb
|
|
48
|
-
- test/tc_log.rb
|
|
49
|
-
- test/tc_meta.rb
|
|
67
|
+
- lib/meta_manager.rb
|
|
68
|
+
- lib/multitenant_filestore.rb
|
|
50
69
|
- test/tc_filestore.rb
|
|
51
|
-
- test/
|
|
70
|
+
- test/tc_multitenant.rb
|
|
52
71
|
homepage: https://www.xing.com/profile/thomas.staetter
|
|
53
72
|
licenses: []
|
|
54
73
|
post_install_message:
|
|
@@ -68,16 +87,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
68
87
|
- !ruby/object:Gem::Version
|
|
69
88
|
version: '0'
|
|
70
89
|
requirements:
|
|
71
|
-
- gem uuidtools
|
|
90
|
+
- gem uuidtools, log4r
|
|
72
91
|
rubyforge_project:
|
|
73
|
-
rubygems_version: 1.8.
|
|
92
|
+
rubygems_version: 1.8.24
|
|
74
93
|
signing_key:
|
|
75
94
|
specification_version: 3
|
|
76
95
|
summary: Simple file storage
|
|
77
96
|
test_files:
|
|
78
|
-
- test/ts_gem.rb
|
|
79
|
-
- test/tc_action.rb
|
|
80
|
-
- test/tc_log.rb
|
|
81
|
-
- test/tc_meta.rb
|
|
82
97
|
- test/tc_filestore.rb
|
|
83
|
-
- test/
|
|
98
|
+
- test/tc_multitenant.rb
|
data/lib/action.rb
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# action.rb
|
|
3
|
-
# @author Thomas Stätter
|
|
4
|
-
# @date 09.07.2012
|
|
5
|
-
# @description Library using a file system as storage for arbitrary files
|
|
6
|
-
#
|
|
7
|
-
$:.unshift('.')
|
|
8
|
-
|
|
9
|
-
require 'filestore.rb'
|
|
10
|
-
|
|
11
|
-
module FileStore
|
|
12
|
-
|
|
13
|
-
class Action
|
|
14
|
-
@@DATE_FORMAT = '%d.%m.%Y %H:%M:%S:%L'
|
|
15
|
-
|
|
16
|
-
@@STATUS_NOT_STARTED = "NOT STARTED"
|
|
17
|
-
@@STATUS_SUCCESS = "SUCCESS"
|
|
18
|
-
@@STATUS_FAILURE = "FAILURE"
|
|
19
|
-
|
|
20
|
-
@type = 'UNDEFINED'
|
|
21
|
-
|
|
22
|
-
def initialize(id)
|
|
23
|
-
raise FileStoreException, "No identifier given for action" if id.nil?
|
|
24
|
-
raise FileStoreException, "Identifier can only be of type String or Numeric" if (not id.is_a?(String) and not id.is_a?(Numeric))
|
|
25
|
-
raise FileStoreException, "Identifier can't be empty" if id.is_a?(String) and id == ''
|
|
26
|
-
|
|
27
|
-
@identifier = id
|
|
28
|
-
@status = @@STATUS_NOT_STARTED
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def execute(&block)
|
|
32
|
-
@start = Date.today.strftime @@DATE_FORMAT
|
|
33
|
-
|
|
34
|
-
begin
|
|
35
|
-
block.call
|
|
36
|
-
@end = Date.today.strftime @@DATE_FORMAT
|
|
37
|
-
rescue Exception => e
|
|
38
|
-
@status = @@STATUS_FAILURE
|
|
39
|
-
raise FileStoreException, 'Caught exception while executing action', e.backtrace
|
|
40
|
-
else
|
|
41
|
-
@status = @@STATUS_SUCCESS
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def to_s
|
|
46
|
-
"#{@status} - #{@start} - #{self.class.name} - #{@msg} - #{@end} - #{@identifier}"
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
class AddAction < Action
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
class DeleteAction < Action
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
end
|
data/lib/meta.rb
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
#
|
|
2
|
-
# meta.rb
|
|
3
|
-
# @author Thomas Stätter
|
|
4
|
-
# @date 10.07.2012
|
|
5
|
-
# @description Library using a file system as storage for arbitrary files
|
|
6
|
-
#
|
|
7
|
-
$:.unshift('.')
|
|
8
|
-
|
|
9
|
-
require 'filestore.rb'
|
|
10
|
-
|
|
11
|
-
module FileStore
|
|
12
|
-
|
|
13
|
-
class MetaManager
|
|
14
|
-
|
|
15
|
-
def initialize
|
|
16
|
-
yield
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def <<(md)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def -(id)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
class MemoryMetaManager < MetaManager
|
|
28
|
-
|
|
29
|
-
def initialize
|
|
30
|
-
# must be a hash object
|
|
31
|
-
@mgmt = super
|
|
32
|
-
|
|
33
|
-
raise FileStoreException, "MemoryMetamanager needs a not nil hash object for initialization" if not @mgmt.is_a?(Hash) or @mgmt.nil?
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def <<(md)
|
|
37
|
-
raise FileStoreException, "Can't add 'nil' to the store" if md.nil?
|
|
38
|
-
raise FileStoreException, "Only objects of type 'FileStore::MetaData' can be added to the store" if not md.instance_of?(MetaData)
|
|
39
|
-
|
|
40
|
-
@mgmt[md.key] = md
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def -(id)
|
|
44
|
-
raise FileStoreException, "Can't remove 'nil' from the store" if id.nil?
|
|
45
|
-
|
|
46
|
-
@mgmt.delete(id) if @mgmt.has_key?(id)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def [](id)
|
|
50
|
-
raise FileStoreException, "Can't read 'nil' from the store" if id.nil?
|
|
51
|
-
|
|
52
|
-
return @mgmt[id] if @mgmt.has_key?(id)
|
|
53
|
-
nil
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
class MetaData
|
|
59
|
-
FIELD_PATH = 'path'
|
|
60
|
-
FIELD_FILENAME = 'filename'
|
|
61
|
-
|
|
62
|
-
attr_reader :key, :data
|
|
63
|
-
|
|
64
|
-
def initialize(key, data)
|
|
65
|
-
raise FileStoreException, "No identifier given for meta data" if key.nil?
|
|
66
|
-
raise FileStoreException, "Identifier can only be of type String or Numeric" if (not key.is_a?(String) and not key.is_a?(Numeric))
|
|
67
|
-
raise FileStoreException, "No data given" if data.nil?
|
|
68
|
-
raise FileStoreException, "Data can only be of type Hash" if not data.is_a?(Hash)
|
|
69
|
-
raise FileStoreException, "Identifier can't be empty" if key.is_a?(String) and key == ''
|
|
70
|
-
|
|
71
|
-
@key = key
|
|
72
|
-
@data = data
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
def path
|
|
76
|
-
@data[FIELD_PATH]
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
end
|
|
File without changes
|
data/test/tc_action.rb
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
#
|
|
3
|
-
# tc_action.rb
|
|
4
|
-
# @author Thomas Stätter
|
|
5
|
-
# @date 10.07.2012
|
|
6
|
-
# @description Unit test for classes in meta.rb
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
# execute test using: ruby -I ../lib/ tc_action.rb
|
|
10
|
-
|
|
11
|
-
require "test/unit"
|
|
12
|
-
require "../lib/filestore.rb"
|
|
13
|
-
|
|
14
|
-
class TestAction < Test::Unit::TestCase
|
|
15
|
-
|
|
16
|
-
def test_init
|
|
17
|
-
# initialization of an instance of FileStore::Action should raise an
|
|
18
|
-
# exception if no identifier is given or the identifier is not a string or
|
|
19
|
-
# numeric
|
|
20
|
-
assert_raise(FileStore::FileStoreException, ArgumentError) { FileStore::Action.new }
|
|
21
|
-
assert_raise(FileStore::FileStoreException, ArgumentError) { FileStore::Action.new(nil) }
|
|
22
|
-
assert_raise(FileStore::FileStoreException, ArgumentError) { FileStore::Action.new([]) }
|
|
23
|
-
# identifier can't be an empty string if it's a String
|
|
24
|
-
assert_raise(FileStore::FileStoreException) { FileStore::Action.new('') }
|
|
25
|
-
assert_nothing_raised(Exception) { FileStore::Action.new("asdf") }
|
|
26
|
-
assert_nothing_raised(Exception) { FileStore::Action.new(213123) }
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def test_execute
|
|
30
|
-
# execution should fail if no block is given
|
|
31
|
-
assert_raise(TypeError, FileStore::FileStoreException) {
|
|
32
|
-
action = FileStore::Action.new("asdf")
|
|
33
|
-
action.execute
|
|
34
|
-
}
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def test_string
|
|
38
|
-
# execution shouldn't fail in any case
|
|
39
|
-
assert_nothing_raised(Exception) { FileStore::Action.new("asdf").to_s }
|
|
40
|
-
assert_nothing_raised(Exception) { FileStore::Action.new(123).to_s }
|
|
41
|
-
assert_nothing_raised(Exception) {
|
|
42
|
-
action = FileStore::Action.new("asdf")
|
|
43
|
-
action.execute {}
|
|
44
|
-
action.to_s
|
|
45
|
-
}
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
end
|
data/test/tc_log.rb
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
#
|
|
3
|
-
# tc_log.rb
|
|
4
|
-
# @author Thomas Stätter
|
|
5
|
-
# @date 10.07.2012
|
|
6
|
-
# @description Unit test for classes in log.rb
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
# execute test using: ruby -I ../lib/ tc_log.rb
|
|
10
|
-
|
|
11
|
-
require "test/unit"
|
|
12
|
-
require "../lib/filestore.rb"
|
|
13
|
-
|
|
14
|
-
class TestLog < Test::Unit::TestCase
|
|
15
|
-
|
|
16
|
-
def test_init
|
|
17
|
-
# should be working, default path would be '.'
|
|
18
|
-
assert_nothing_raised { FileStore::Log.new }
|
|
19
|
-
# should raise an error, because the path is invalid
|
|
20
|
-
assert_raise(FileStore::FileStoreException){ FileStore::Log.new('/some/invalid/path') }
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def test_log
|
|
24
|
-
# shouldn't raise any error (but nothing will happen to the log)
|
|
25
|
-
logger = FileStore::Log.new
|
|
26
|
-
|
|
27
|
-
assert_nothing_raised {
|
|
28
|
-
logger << "asdf"
|
|
29
|
-
logger << 2343
|
|
30
|
-
logger << []
|
|
31
|
-
logger << FileStore::DeleteAction.new("asdf")
|
|
32
|
-
}
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def test_close
|
|
36
|
-
logger = FileStore::Log.new
|
|
37
|
-
|
|
38
|
-
assert_nothing_raised { logger.close }
|
|
39
|
-
end
|
|
40
|
-
end
|
data/test/tc_meta.rb
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
#
|
|
3
|
-
# tc_meta.rb
|
|
4
|
-
# @author Thomas Stätter
|
|
5
|
-
# @date 10.07.2012
|
|
6
|
-
# @description Unit test for classes in meta.rb
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
# execute test using: ruby -I ../lib/ tc_meta.rb
|
|
10
|
-
|
|
11
|
-
require "test/unit"
|
|
12
|
-
require "../lib/filestore.rb"
|
|
13
|
-
|
|
14
|
-
# Test cases for FileStore::MetaData
|
|
15
|
-
class TestMetaData < Test::Unit::TestCase
|
|
16
|
-
def test_init
|
|
17
|
-
# arguments 'key' and 'data' must be provided
|
|
18
|
-
assert_raise(ArgumentError) { FileStore::MetaData.new }
|
|
19
|
-
# argument 'key' must be of type String or Numeric
|
|
20
|
-
assert_raise(FileStore::FileStoreException) { FileStore::MetaData.new({},{}) }
|
|
21
|
-
assert_nothing_raised(FileStore::FileStoreException) { FileStore::MetaData.new('asdf',{}) }
|
|
22
|
-
assert_nothing_raised(FileStore::FileStoreException) { FileStore::MetaData.new(234,{}) }
|
|
23
|
-
# identifier may not be an empty string
|
|
24
|
-
assert_raise(FileStore::FileStoreException) { FileStore::MetaData.new('',{}) }
|
|
25
|
-
# data argument may not be nil
|
|
26
|
-
assert_raise(FileStore::FileStoreException) { FileStore::MetaData.new('asdf',nil) }
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def test_accessors
|
|
30
|
-
# attr_readers for key and data may not return nil
|
|
31
|
-
md = FileStore::MetaData.new(234,{})
|
|
32
|
-
|
|
33
|
-
assert_not_nil(md.key, "Key can't be nil")
|
|
34
|
-
assert_not_nil(md.data, "Key can't be nil")
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def test_path
|
|
38
|
-
md = FileStore::MetaData.new(234,{ FileStore::MetaData::FIELD_PATH => '/some/path' })
|
|
39
|
-
# path may only return a string
|
|
40
|
-
assert_instance_of(String, md.path, "Path can only be of type string")
|
|
41
|
-
|
|
42
|
-
md = FileStore::MetaData.new(234,{})
|
|
43
|
-
# path may be nil
|
|
44
|
-
assert_nil(md.path, "Path is not given although it should be there")
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Test cases for FileStore::MetaManager
|
|
49
|
-
class TestMetaManager < Test::Unit::TestCase
|
|
50
|
-
def test_init
|
|
51
|
-
# should raise a type error if no initialize code is given
|
|
52
|
-
assert_raise(LocalJumpError) { FileStore::MetaManager.new }
|
|
53
|
-
assert_nothing_raised(Exception) { FileStore::MetaManager.new {} }
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Test cases for FileStore::MemoryMetaManager
|
|
58
|
-
class TestMemoryMetaManager < Test::Unit::TestCase
|
|
59
|
-
def test_init
|
|
60
|
-
# the init block may only return a not nil hash object
|
|
61
|
-
assert_raise(FileStore::FileStoreException){ FileStore::MemoryMetaManager.new {""} }
|
|
62
|
-
assert_raise(FileStore::FileStoreException){ FileStore::MemoryMetaManager.new { } }
|
|
63
|
-
assert_nothing_raised(FileStore::FileStoreException){ FileStore::MemoryMetaManager.new {{}} }
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def test_add
|
|
67
|
-
mm = FileStore::MemoryMetaManager.new {{}}
|
|
68
|
-
|
|
69
|
-
assert_raise(FileStore::FileStoreException){ mm << nil }
|
|
70
|
-
assert_raise(FileStore::FileStoreException){ mm << "asdfasdf" }
|
|
71
|
-
assert_nothing_raised(FileStore::FileStoreException){
|
|
72
|
-
md = FileStore::MetaData.new(234,{})
|
|
73
|
-
mm << md
|
|
74
|
-
}
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def test_remove
|
|
78
|
-
mm = FileStore::MemoryMetaManager.new {{}}
|
|
79
|
-
|
|
80
|
-
assert_raise(FileStore::FileStoreException){ mm - nil }
|
|
81
|
-
assert_nothing_raised(Exception){
|
|
82
|
-
mm - "asdf"
|
|
83
|
-
mm - 123123
|
|
84
|
-
mm - []
|
|
85
|
-
mm - {}
|
|
86
|
-
mm - FileStore::MetaData.new(234,{})
|
|
87
|
-
}
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def test_read
|
|
91
|
-
mm = FileStore::MemoryMetaManager.new {{}}
|
|
92
|
-
|
|
93
|
-
assert_raise(FileStore::FileStoreException){ mm[nil] }
|
|
94
|
-
assert_nothing_raised(Exception){
|
|
95
|
-
mm[[]]
|
|
96
|
-
mm["asdf"]
|
|
97
|
-
mm[2421]
|
|
98
|
-
}
|
|
99
|
-
assert_nil(mm[2343], "Nil wasn't returned althought the given key wasn't existing")
|
|
100
|
-
end
|
|
101
|
-
end
|
data/test/ts_gem.rb
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
#
|
|
3
|
-
# ts_gem.rb
|
|
4
|
-
# @author Thomas Stätter
|
|
5
|
-
# @date 10.07.2012
|
|
6
|
-
# @description Test suite for the complete gem
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
# execute test using: ruby -I../lib/ -I. ts_gem.rb
|
|
10
|
-
$:.unshift('.')
|
|
11
|
-
$:.unshift('../lib')
|
|
12
|
-
|
|
13
|
-
require "test/unit"
|
|
14
|
-
require 'tc_action.rb'
|
|
15
|
-
require 'tc_meta.rb'
|
|
16
|
-
require 'tc_log.rb'
|
|
17
|
-
require 'tc_filestore.rb'
|