From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

use Moose;
has 'is_collection' => (
isa => 'Bool',
is => 'ro',
);
has 'url' => (
isa => 'Str',
is => 'ro',
lazy => 1,
builder => '_probe_url',
);
has 'download_verbose' => (
is => 'rw',
isa => 'Bool',
default => 0,
);
no Moose;
use Moose;
use Carp;
has '+is_collection' => (
default => 0,
);
has 'relname' => (
is => 'rw',
isa => 'Str',
required => 1,
);
has 'filename' => (
isa => 'Str',
is => 'ro',
lazy => 1,
builder => '_get_file',
);
has 'mtime' => (
isa => 'DateTime',
is => 'ro',
lazy => 1,
builder => '_probe_mtime',
);
has 'baseurl' => (
isa => 'Str',
is => 'ro',
required => 1,
);
has 'basepath' => (
isa => 'Str',
is => 'ro',
lazy => 1,
builder => '_probe_basepath',
);
has 'create' => (
is => 'rw',
traits => ['Bool'],
isa => 'Bool',
default => 0,
required => 1,
handles => {
has_data => 'not',
},
);
has 'is_stored' => (
is => 'ro',
isa => 'Bool',
traits => ['Bool'],
default => 0,
handles => {
auto_save => 'unset',
no_auto_save => 'set',
stored => 'set',
},
);
sub _probe_url {
my $self = shift;
return join('/', $self->baseurl, $self->relname);
}
sub _probe_basepath {
return shift->baseurl;
}
sub DEMOLISH {
my $self = shift;
if($self->create) {
if(!$self->is_stored) {
carp "object destructor for '" . $self->url . "' entered without an explicit store, storing now...";
$self->store_file;
}
}
}
no Moose;
use Moose;
use File::Path qw/make_path/;
use File::Basename qw/dirname/;
sub _get_file {
my $self = shift;
if($self->create) {
make_path(dirname($self->url));
unlink($self->url);
}
return $self->url;
}
sub store_file {
my $self = shift;
$self->stored;
return 1;
}
sub _probe_mtime {
my $self = shift;
my @stat = stat($self->filename);
return DateTime->from_epoch(epoch => $stat[9]);
}
sub delete {
my $self = shift;
unlink($self->url);
}
sub valid_path_filename {
my $self = shift;
return $self->url;
}
no Moose;
use Moose;
use Carp;
has '+is_collection' => (
default => 1,
);
has 'children' => (
isa => 'ArrayRef[SReview::Files::Base]',
traits => ['Array'],
is => "ro",
lazy => 1,
handles => {
sorted_files => 'sort',
},
builder => '_probe_children',
);
has 'baseurl' => (
isa => 'Str',
is => 'ro',
predicate => 'has_baseurl',
writer => '_set_baseurl',
lazy => 1,
builder => '_probe_baseurl',
);
has 'globpattern' => (
isa => 'Str',
is => 'ro',
predicate => 'has_globpattern',
lazy => 1,
builder => '_probe_globpattern',
);
has 'fileclass' => (
isa => 'Str',
is => 'ro',
required => 1,
);
has 'collection_name' => (
isa => 'Str',
is => 'ro',
default => '(unknown)',
lazy => 1,
);
sub _probe_baseurl {
my $self = shift;
if(!$self->has_globpattern) {
croak("either a globpattern or a baseurl are required!\n");
}
@_ = split(/\*/, $self->globpattern);
my $rv = $_[0];
while(substr($rv, -1) eq '/') {
substr($rv, -1) = '';
}
return $rv;
}
sub _probe_url {
return shift->baseurl;
}
sub _probe_globpattern {
my $self = shift;
if(!$self->has_baseurl) {
croak("either a globpattern or a baseurl are required!\n");
}
return join('/', $self->baseurl, '*');
}
sub _create {
my $self = shift;
my %options = @_;
if(exists($options{fullname})) {
if(substr($options{fullname}, 0, length($self->baseurl)) ne $self->baseurl) {
croak($options{fullname} . " is not accessible through this collection");
}
$options{relname} = substr($options{fullname}, length($self->baseurl));
while(substr($options{relname}, 0, 1) eq '/') {
$options{relname} = substr($options{relname}, 1);
}
delete $options{fullname};
}
$options{baseurl} = $self->baseurl;
my $fileclass = $self->fileclass;
return "$fileclass"->new(%options);
}
sub get_file {
my $self = shift;
my %options = @_;
$options{create} = 0;
return $self->_create(%options);
}
sub add_file {
my $self = shift;
my %options = @_;
$options{create} = 1;
return $self->_create(%options);
}
sub has_file {
my $self = shift;
my $target = shift;
return scalar(grep({(!$_->is_collection) && ($_->relname eq $target)} @{$self->children}));
}
sub delete_files {
my $self = shift;
my %options = @_;
my @names;
if(exists($options{files})) {
@names = sort(@{$options{files}});
} elsif(exists($options{relnames})) {
@names = map({join('/', $self->baseurl, $_)} sort(@{$options{relnames}}));
} else {
croak("need list of files, or list of relative names");
}
my @ownfiles = sort({$a->url cmp $b->url} @{$self->children});
my @to_delete = ();
while(scalar(@names) && scalar(@ownfiles)) {
if($ownfiles[0]->is_collection) {
if($names[0] eq $ownfiles[0]->baseurl) {
push @to_delete, shift @ownfiles;
shift @names;
} elsif(substr($names[0], 0, length($ownfiles[0]->baseurl)) eq $ownfiles[0]->baseurl) {
$ownfiles[0]->delete_files(files => [$names[0]]);
shift @names;
}
shift @ownfiles;
} elsif($names[0] eq $ownfiles[0]->url) {
shift @names;
push @to_delete, shift @ownfiles;
} elsif($names[0] eq substr($ownfiles[0]->url, 0, length($names[0]))) {
push @to_delete, shift @ownfiles;
if((!scalar(@ownfiles)) || $names[0] ne substr($ownfiles[0]->url, 0, length($names[0]))) {
shift @names;
}
} elsif ($names[0] gt $ownfiles[0]->url) {
shift @ownfiles;
} else {
carp "ignoring request to delete file or directory ${names[0]} from collection " . $self->collection_name . ", as it does not exist";
shift @names;
}
};
if(scalar(@names)) {
carp "ignoring request to delete file or directory ${names[0]} from collection " . $self->collection_name . ", as it does not exist";
}
foreach my $file(@to_delete) {
$file->delete;
}
}
sub delete {
my $self = shift;
foreach my $child(@{$self->children}) {
$child->delete;
}
}
no Moose;
use Moose;
use Carp;
has '+fileclass' => (
default => 'SReview::Files::Access::direct',
);
sub _probe_children {
my $self = shift;
my @return;
foreach my $file(glob($self->globpattern)) {
my $child;
if(-d $file) {
$child = SReview::Files::Collection::direct->new(baseurl => join("/", $self->baseurl, basename($file)), download_verbose => $self->download_verbose);
} else {
my $basename = substr($file, length($self->baseurl));
while(substr($basename, 0, 1) eq '/') {
$basename = substr($basename, 1);
}
$child = SReview::Files::Access::direct->new(baseurl => $self->baseurl, relname => $basename, download_verbose => $self->download_verbose);
}
push @return, $child;
}
return \@return;
}
sub has_file {
my ($self, $target) = @_;
if(-f join('/', $self->baseurl, $target)) {
return 1;
}
return 0;
}
sub delete {
my $self = shift;
$self->SUPER::delete;
rmdir($self->baseurl);
}
no Moose;
sub create {
my $class = shift;
my $target = shift;
my $relname = shift;
my $config = SReview::Config::Common::setup();
my $methods = $config->get("accessmethods");
my $method;
if(!exists($methods->{$target})) {
die "missing method for $target\n";
}
$method = $methods->{$target};
eval "require SReview::Files::Collection::$method;";
if($@) {
die "$@: $!";
}
if($target eq "input") {
return "SReview::Files::Collection::$method"->new(globpattern => $relname, collection_name => $target);
} else {
return "SReview::Files::Collection::$method"->new(baseurl => $relname, collection_name => $target);
}
}
1;