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:

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 become a/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 become a.

  • Up directory written as .. will modify the path accordingly, so a/../b will become b. If the path tries to leave the root, a Storage::Abstract::X::PathError will be raised.

  • Last part of the path must look like a filename. Paths like a/, a/. or a/.. will raise Storage::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.