NAME
Storage::Abstract - Abstraction for file storage
SYNOPSIS
use Storage::Abstract;
my $storage = Storage::Abstract->new(
driver => 'Directory',
directory => '/my/directory',
);
my $all_filenames = $storage->list;
$storage->store('some/file', 'local_filename');
$storage->store('some/file', \'file_content');
$storage->store('some/file', $open_filehandle);
my $bool = $storage->is_stored('some/file');
$storage->dispose('/some/file');
# retrieving a file
try {
# $fh is always opened in '<:raw' mode
my $fh = $storage->retrieve('some/file', \my %info);
# if passed, %info is filled with extra properties of the file
say $info{mtime};
}
catch ($e) {
# errors are reported via exceptions
if ($e isa 'Storage::Abstract::X::NotFound') {
# not found
}
elsif ($e isa 'Storage::Abstract::X::PathError') {
# file path is invalid
}
elsif ($e isa 'Storage::Abstract::X::HandleError') {
# error opening Perl handle
}
elsif ($e isa 'Storage::Abstract::X::StorageError') {
# error fetching file from storage
}
}
DESCRIPTION
This module lets you store and retrieve files from various places with a unified API. Its main purpose is to abstract away file storage, so that you only have to handle high-level operations without worrying about details.
BETA QUALITY: The interface is not yet stable before version 1.000
.
Drivers
When creating an instance of this module, you need to pass "driver" and any extra attributes that driver need. All implementation details depend on the chosen driver, this module only contains methods which delegate to same methods of the driver.
There are drivers and metadrivers. Metadrivers do not implement any file storage by themselves, but rather change the way other storages work. The module comes with the following driver implementations:
Storage::Abstract::Driver::Memory
This driver keeps the files in Perl process memory as strings.
Storage::Abstract::Driver::Directory
This driver stores the files in a local machine's directory.
Storage::Abstract::Driver::Composite
This metadriver can be configured to keep a couple source storages at once and use them all in sequence until it finds a file.
Storage::Abstract::Driver::Subpath
This metadriver is useful when you want to modify the base path of another storage, to restrict access or adapt a path (for example for HTTP public directory).
Storage::Abstract::Driver::Superpath
This metadriver does the opposite of
subpath
- allows you to modify path of a storage to virtually put it into a nested directory as a whole.Storage::Abstract::Driver::Null
This driver does nothing - it won't store or retrieve anything.
File paths
All file paths used in this module must be Unix-like regardless of the platform. /
is used as a directory separator, ..
is used as an upward directory, .
is used as a current directory (is ignored), and empty file paths are ignored. Any platform-specific parts of file paths which are not listed above are allowed and will not be recognized as path syntax. Drivers which deal with platform-specific paths (for example Storage::Abstract::Driver::Directory) may raise an exception if path contains any platform-specific syntax different from Unix syntax.
All file paths will be normalized, so that:
Empty parts will be removed, so
a//b
will becomea/b
. Because of this, all paths will end up being relative (the leading/
will be removed).Current directory written as
.
will be removed, so./a
will becomea
.Up directory written as
..
will modify the path accordingly, soa/../b
will becomeb
. If the path tries to leave the root, aStorage::Abstract::X::PathError
will be raised.Last part of the path must look like a filename. Paths like
a/
,a/.
ora/..
will raiseStorage::Abstract::X::PathError
.
File handles
This module works with open filehandles in binary mode. These handles are likely to be pointing at an in-memory scalar rather than a regular file, so they are not guaranteed to work with sysread
/syswrite
. You may use fileno
to check if a handle is pointing to an actual file.
If you pass a handle to "store", it should be properly marked as binary with binmode
and should be rewinded to a point from which you want to store it using seek
/ sysseek
. The handle is sure to point at EOF after the module is done copying it.
It is recommended to close the returned handles as soon as possible.
Properties
This module can get additional file data when retrieving files. It is similar to calling stat
on a filehandle, but contains much less information.
Currently, only the following keys are guaranteed to be included for all drivers:
size
The size of the data in the returned handle, in bytes.
mtime
Last modification unix timestamp of the file.
INTERFACE
Attributes
All attributes not listed here will be used as extra attributes for constructing the driver.
driver
Required - This is the name of the driver to use. It must be a partial class name from namespace Storage::Abstract::Driver::
, for example Directory
will point to Storage::Abstract::Driver::Directory. First letter of the driver will be capitalized. If the name is prefixed with +
, the rest of the name will be used as full namespace without adding the standard prefix, same as in Plack.
After the object is created, this will point to an instance of the driver. Alternatively, an already constructed driver object can be passed, and will be used as-is.
Methods
These are common methods not dependant on a driver.
new
$obj = Storage::Abstract->new(%args);
$obj = Storage::Abstract->new(\%args);
Moose-flavoured constructor, but %args
will be used to construct the driver rather than this class.
load_driver
$driver_obj = Storage::Abstract->load_driver(%args);
$driver_obj = Storage::Abstract->load_driver(\%args);
Loads the driver package and constructs the driver using %args
(same as in the constructor). Returns an instance of Storage::Abstract::Driver.
Delegated methods
These methods are delegates from the underlying instance of Storage::Abstract::Driver, stored in "driver". They may raise an exception if the input is invalid or if they encounter an error.
Note that missing file will also be reported via an exception, not by returning undef
.
store
$obj->store($path, \$content)
$obj->store($path, $filename)
$obj->store($path, $handle)
Stores a new file in the storage under $path
. Does not return anything.
is_stored
$bool = $obj->is_stored($path)
Checks whether a given $path
is stored in the storage. Returns true if it is.
retrieve
$handle = $obj->retrieve($path, $properties = undef)
Returns a $handle
to a file stored under $path
.
If $properties
are passed, it must be a reference to a hash. This hash will be filled with additional properties of the file, such as mtime
(modification time).
dispose
$obj->dispose($path)
Removes file $path
from the storage.
It treats missing files as an error, so if no exception occurs you can be sure that the removal was performed.
list
my $filenames_aref = $obj->list;
Lists the names of all files existing in the storage in an array reference. These names will be forced to the normalized form.
This may be a costly operation (depending on the driver), so use it sparingly.
readonly
$bool = $obj->readonly()
Returns true if the storage is readonly. Readonly storage will raise an exception on "store" and "dispose".
set_readonly
$obj->set_readonly($bool)
Sets the readonly status of the storage to a new value.
This method does not work and throws an exception for metadrivers using multiple sources of storage, like Storage::Abstract::Driver::Composite.
AUTHOR
Bartosz Jarzyna <bbrtj.pro@gmail.com>
ACKNOWLEDGEMENTS
Thank you to Alexander Karelas for his feedback during module development.
COPYRIGHT AND LICENSE
Copyright (C) 2024 by Bartosz Jarzyna
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.