Catmandu-BagIt

 view release on metacpan or  search on metacpan

lib/Catmandu/BagIt.pm  view on Meta::CPAN

    my ($self) = @_;
    keys %{$self->_sums};
}

# Return the checksum of of a file name
sub get_checksum {
    my ($self,$file) = @_;

    die "usage: get_checksum(file)" unless $file;

    $self->_sums->{$file};
}

# Read the content of a bag
sub read {
    my ($class,$path) = @_;

    die "usage: read(path)" unless $path;

    my $self = $class->new;

    if (! -d $path ) {
        $self->log->error("$path doesn't exist");
        $self->_push_error("$path doesn't exist");
        return;
    }

    $self->log->info("reading: $path");

    $self->_path($path);

    my $ok = 0;

    $ok += $self->_read_version($path);
    $ok += $self->_read_info($path);
    $ok += $self->_read_manifest($path);
    $ok += $self->_read_tag_manifest($path);
    $ok += $self->_read_tags($path);
    $ok += $self->_read_files($path);
    $ok += $self->_read_fetch($path);

    $self->_dirty(0);

    if ( wantarray ) {
        return $ok == 7 ? ($self) : (undef, $self->errors);
    }
    else {
        return $ok == 7 ? $self : undef;
    }
}

# Write the content of a bag back to disk
sub write {
    my ($self,$path,%opts) = @_;

    $self->_error([]);

    die "usage: write(path[, overwrite => 1])" unless $path;

    # Check if other processes are writing or previous processes died
    if ($self->locked($path)) {
        $self->log->error("$path is locked");
        $self->_push_error("$path is locked");
        return undef;
    }

    if (defined($self->path) && $path ne $self->path) {
        # If the bag is copied from to a new location than all the tag files and
        # files should be flagged as dirty and need to be overwritten
        $self->log->info("copying from old path: " . $self->path);
        $self->_dirty($self->dirty | FLAG_BAGIT | FLAG_BAG_INFO | FLAG_TAG_MANIFEST | FLAG_MANIFEST | FLAG_DATA);

        foreach my $item ($self->list_files) {
            $item->flag($item->flag ^ FLAG_DIRTY);
        }
    }
    elsif (defined($self->path) && $path eq $self->path) {
        # we are ok the path exists and don't need to remove anything
        # updates are possible when overwrite => 1
    }
    elsif ($opts{overwrite} && -d $path) {
        # Remove existing bags
        $self->log->info("removing: $path");
        path($path)->remove_tree;
    }

    if (-f $self->_bagit_file($path)) {
        if ($opts{overwrite}) {
            $self->log->info("overwriting: $path");
        }
        else {
            $self->log->error("$path already exists");
            $self->_push_error("$path already exists");
            return undef;
        }
    }
    else {
        $self->log->info("creating: $path");
        path($path)->mkpath;
        $self->_dirty($self->dirty | FLAG_BAGIT);
    }

    unless ($self->touch($self->_lock_file($path))) {
        $self->log->error("failed to lock in $path");
        return undef;
    }

    $self->_path($path);

    my $ok = 0;

    $ok += $self->_write_bagit($path);
    $ok += $self->_write_info($path);
    $ok += $self->_write_data($path);
    $ok += $self->_write_fetch($path);
    $ok += $self->_write_manifest($path);
    $ok += $self->_write_tag_manifest($path);

    return undef unless $ok == 6;

    $self->_dirty(0);

    unlink($self->_lock_file($path));

lib/Catmandu/BagIt.pm  view on Meta::CPAN

}

sub _bag_info_file {
    my ($self,$path) = @_;

    File::Spec->catfile($path,'bag-info.txt');
}

sub _package_info_file {
    my ($self,$path) = @_;

    File::Spec->catfile($path,'package-info.txt');
}

sub _manifest_file {
    my ($self,$path) = @_;

    for my $alg (qw(md5 sha512 sha256 sha1)) {
      my $p = File::Spec->catfile($path,"manifest-$alg.txt");
      return ($p,$alg) if -f $p;
    }

    return (undef,undef);
}

sub _tagmanifest_file {
    my ($self,$path) = @_;

    for my $alg (qw(md5 sha512 sha256 sha1)) {
      my $p = File::Spec->catfile($path,"tagmanifest-$alg.txt");
      return ($p,$alg) if -f $p;
    }

    return (undef,undef);
}

sub _fetch_file {
    my ($self,$path) = @_;

    File::Spec->catfile($path,'fetch.txt');
}

sub _tag_file {
    my ($self,$path,$file) = @_;

    File::Spec->catfile($path,$file);
}

sub _payload_file {
    my ($self,$path,$file) = @_;

    File::Spec->catfile($path,'data',$file);
}

sub _lock_file {
    my ($self,$path) = @_;

    File::Spec->catfile($path,'.lock');
}

sub locked {
    my ($self,$path) = @_;
    $path //= $self->path;

    return undef unless defined($path);

    -f $self->_lock_file($path);
}

sub touch {
    my ($self,$path) = @_;

    die "usage: touch(path)"
            unless defined($path);

    path("$path")->spew("");

    1;
}

sub add_file {
    my ($self, $filename, $data, %opts) = @_;

    die "usage: add_file(filename, data [, overwrite => 1])"
            unless defined($filename) && defined($data);

    $self->_error([]);

    unless ($self->_is_legal_file_name($filename)) {
        $self->log->error("illegal file name $filename");
        $self->_push_error("illegal file name $filename");
        return;
    }

    $self->log->info("adding file $filename");

    if ($opts{overwrite}) {
        $self->remove_file($filename);
    }

    if ($self->get_checksum("$filename")) {
        $self->log->error("$filename already exists in bag");
        $self->_push_error("$filename already exists in bag");
        return;
    }

    my $payload = Catmandu::BagIt::Payload->from_any($filename,$data);
    $payload->flag(FLAG_DIRTY);

    my $sum;

    if ( is_string($opts{md5}) ) {
        if ($self->algorithm ne 'md5') {
            $self->log->error("need a " . $self->algorithm . " checksum not an md5");
            $self->_push_error("need a " . $self->algorithm . " checksum not an md5");
            return;
        }
        elsif ($opts{md5} =~ /^[0-9a-f]{32}$/) {
            $sum = $opts{md5};
        }
        else {

lib/Catmandu/BagIt.pm  view on Meta::CPAN

    for my $file ($bagit->list_files) {
        my $stat = [stat($file->path)];
        printf " name: %s\n", $file->filename;
        printf " size: %s\n", $stat->[7];
        printf " last-mod: %s\n", scalar(localtime($stat->[9]));
    }

    my $file = $bagit->get_file("mydata.txt");
    my $fh   = $file->open;

    while (<$fh>) {
       ....
    }

    close($fh);

    print "dirty?\n" if $bagit->is_dirty;

    if ($bagit->complete) {
        print "bag is complete\n";
    }
    else {
        print "bag is not complete!\n";
    }

    if ($bagit->valid) {
        print "bag is valid\n";
    }
    else {
        print "bag is not valid!\n";
    }

    if ($bagit->is_holey) {
        print "bag is holey\n";
    }
    else {
        print "bag isn't holey\n";
    }

    if ($bagit->errors) {
        print join("\n",$bagit->errors);
    }

    # Write operations
    $bagit->add_info('My-Tag','fsdfsdfsdf');
    $bagit->add_info('My-Tag',['dfdsf','dfsfsf','dfdsf']);
    $bagit->remove_info('My-Tag');

    $bagit->add_file("test.txt","my text");
    $bagit->add_file("data.pdf", IO::File->new("/tmp/data.pdf"));
    $bagit->remove_file("test.txt");

    $bagit->add_fetch("http://www.gutenberg.org/cache/epub/1980/pg1980.txt","290000","shortstories.txt");
    $bagit->remove_fetch("shortstories.txt");

    if ($bagit->errors) {
        print join("\n",$bagit->errors);
        exit;
    }

    unless ($bagit->locked) {
        $bagit->write("bags/demo04"); # fails when the bag already exists
        $bagit->write("bags/demo04", new => 1); # recreate the bag when it already existed
        $bagit->write("bags/demo04", overwrite => 1); # overwrites an exiting bag
    }

=head1 CATMANDU MODULES

=over

=item * L<Catmandu::Importer::BagIt>

=item * L<Catmandu::Exporter::BagIt>

=item * L<Catmandu::Store::File::BagIt>

=back

=head1 LARGE FILE SUPPORT

Streaming large files into a BagIt requires a large /tmp directory. The location
of the temp directory can be set with the TMPDIR environmental variable.

=head1 METHODS

=head2 new()

=head2 new(version => ... , algorithm => 'md5|sha1|sha256|sha512')

Create a new BagIt object

=head2 read($directory)

Open an exiting BagIt object and return an instance of BagIt or undef on failure.
In array context the read method also returns all errors as an array:

  my $bagit = Catmandu::BagIt->read("/data/my-bag");

  my ($bagit,@errors) = Catmandu::BagIt->read("/data/my-bag");

=head2 write($directory, [%options])

Write a BagIt to disk. Options: new => 1 recreate the bag when it already existed, overwrite => 1 overwrite
and existing bag (updating the changed tags/files);

=head2 locked

Check if a process has locked the BagIt. Or, a previous process didn't complete the write operations.

=head2 path()

Return the path to the BagIt.

=head2 version()

Return the version of the BagIt.

=head2 encoding()

Return the encoding of the BagIt.

=head2 size()

Return a human readble string of the expected size of the BagIt (adding the actual sizes found on disk plus
the files that need to be fetched from the network).

=head2 payload_oxum()

Return the actual payload oxum of files found in the package.

=head2 is_dirty()

Return true when the BagIt contains changes not yet written to disk.

=head2 is_holey()

Return true when the BagIt contains a non emtpy fetch configuration.

=head2 is_error()

Return an ARRAY of errors when checking complete, valid and write.

=head2 complete()

Return true when the BagIt is complete (all files and manifest files are consistent).

=head2 valid()

Returns true when the BagIt is complete and all checkums match the files on disk.

=head2 list_info_tags()

Return an ARRAY of tag names found in bagit-info.txt.

=head2 add_info($tag,$value)

=head2 add_info($tag,[$values])

Add an info $tag with a $value.

=head2 remove_info($tag)

Remove an info $tag.

=head2 get_info($tag, [$delim])

Return an ARRAY of values found for the $tag name. Or, in scalar context, return a string of
all values optionally delimeted by $delim.



( run in 2.787 seconds using v1.01-cache-2.11-cpan-5c0b1e786e0 )