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 CHANGED
@@ -1,108 +1,297 @@
1
1
  #
2
2
  # filestore.rb
3
3
  # @author Thomas Stätter
4
- # @date 09.07.2012
5
- # @description Library using a file system as storage for arbitrary files
4
+ # @date 2012/11/07
5
+ # @description
6
6
  #
7
7
 
8
- $:.unshift('.')
9
-
10
- require 'uuidtools'
11
- require 'fileutils'
12
- require 'action.rb'
8
+ require 'meta_manager.rb'
13
9
  require 'log.rb'
14
- require 'meta.rb'
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 FileStore
22
- @@STORE_ROOT = 'filestore'
23
- @@DELETED_ROOT = 'deleted'
24
-
25
- def initialize(basePath = ".", metaManager)
26
- raise FileStoreException, "Invalid base path given" if (basePath.nil? or
27
- not File.exists?(basePath) or
28
- not File.directory?(basePath) or
29
- not basePath.is_a?(String) or
30
- basePath == '')
31
-
32
- @storePath = File.join(basePath, @@STORE_ROOT)
33
- @deletedPath = File.join(basePath, @@DELETED_ROOT)
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
- FileUtils.mkdir_p(@storePath) if not Dir.exists?(@storePath)
37
- FileUtils.mkdir_p(@deletedPath) if not Dir.exists?(@deletedPath)
38
- rescue StandardError => e
39
- raise FileStoreException, "Couldn't create store directories", e.backtrace
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
- # needs no specific exception handling dew to the fact, that the directories
43
- # could be created
44
- @logger = Log.new(basePath)
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
- raise FileStoreException, "No meta manager given" if not metaManager.is_a? MetaManager
47
- @metaManager = metaManager
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
- def <<(path)
51
- id = ''
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
- if File.exists?(path) and File.readable?(path) then
54
- id = UUIDTools::UUID.random_create.to_s
55
- action = AddAction.new(id)
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
- action.execute {
58
- dstPath = move(@storePath, id, path)
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
- @logger << action
66
- else
67
- raise FileStoreException, "File is not readable"
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
- def -(id)
74
- if @metaManager[id] != nil then
75
- action = DeleteAction.new(id)
76
-
77
- action.execute {
78
- raise "Couldn't move file" if move(@deletedPath, id, @metaManager[id].path) == ''
79
- @metaManager - id
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
- @logger << action
83
- else
84
- raise FileStoreException, "Key not found"
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
- def move(basePath, id, srcPath)
91
- dstPath = ''
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
- date = Date.today
95
- dstPath = File.join(basePath, date.year.to_s, date.month.to_s, date.day.to_s, id)
96
- dstDir = File.dirname(dstPath)
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
- (FileUtils.mkdir_p(dstDir) and puts "creating #{dstDir}") if not Dir.exists?(dstDir)
99
- FileUtils.mv(srcPath, dstPath)
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, "Couldn't move file", e.backtrace
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 09.07.2012
5
- # @description Library using a file system as storage for arbitrary files
4
+ # @date 2012/11/07
5
+ # @description
6
6
  #
7
- $:.unshift('.')
8
-
9
- require 'filestore.rb'
7
+ require 'date'
8
+ require 'log4r'
9
+ require 'singleton'
10
10
 
11
11
  module FileStore
12
-
13
- class Log
14
- FILE = 'filestore-actions.log'
15
-
16
- def initialize(path = '.')
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
- def close
32
- begin
33
- @logFile.close
34
- rescue StandardError => e
35
- raise FileStoreException, "Couldn't properly close the logger", e.backtrace
36
- end
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
 
@@ -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
@@ -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 10.07.2012
6
- # @description Unit test for classes in filestore.rb
4
+ # @date 2012/11/14
5
+ # @description Test script
7
6
  #
8
- # execute test using: ruby -I ../lib/ tc_filestore.rb
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
- require "test/unit"
11
- require '../lib/filestore.rb'
22
+ puts id
23
+ puts "Enter something to finish"
24
+ enter = gets
12
25
 
13
- class TestFileStore < Test::Unit::TestCase
14
- BASE_PATH = "./data"
15
-
16
- def test_init
17
- # should work
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.2
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-07-11 00:00:00.000000000 Z
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/action.rb
65
+ - lib/memory_meta.rb
44
66
  - lib/log.rb
45
- - lib/meta.rb
46
- - test/ts_gem.rb
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/move_from/test-file-to-move
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.21
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/move_from/test-file-to-move
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'