package Venus::Path;
use 5.018;
use strict;
use warnings;
use Venus::Class 'base', 'with';
base 'Venus::Kind::Utility';
with 'Venus::Role::Valuable';
with 'Venus::Role::Buildable';
with 'Venus::Role::Accessible';
with 'Venus::Role::Explainable';
use overload (
'""' => 'explain',
'eq' => sub{$_[0]->value eq "$_[1]"},
'ne' => sub{$_[0]->value ne "$_[1]"},
'qr' => sub{qr/@{[quotemeta($_[0]->value)]}/},
'~~' => 'explain',
fallback => 1,
);
# METHODS
sub assertion {
my ($self) = @_;
my $assert = $self->SUPER::assertion;
$assert->clear->string;
return $assert;
}
sub absolute {
my ($self) = @_;
require File::Spec;
return $self->class->new(File::Spec->rel2abs($self->get));
}
sub basename {
my ($self) = @_;
require File::Basename;
return File::Basename::basename($self->get);
}
sub child {
my ($self, $path) = @_;
require File::Spec;
my @parts = File::Spec->splitdir($path);
return $self->class->new(File::Spec->catfile($self->get, @parts));
}
sub chmod {
my ($self, $mode) = @_;
my $path = $self->get;
CORE::chmod($mode, $path);
return $self;
}
sub chown {
my ($self, @args) = @_;
my $path = $self->get;
CORE::chown((map $_||-1, @args[0,1]), $path);
return $self;
}
sub children {
my ($self) = @_;
require File::Spec;
my @paths = map $self->glob($_), '.??*', '*';
return wantarray ? (@paths) : \@paths;
}
sub default {
require Cwd;
return Cwd::getcwd();
}
sub directories {
my ($self) = @_;
my @paths = grep -d, $self->children;
return wantarray ? (@paths) : \@paths;
}
sub exists {
my ($self) = @_;
return int!!-e $self->get;
}
sub explain {
my ($self) = @_;
return $self->get;
}
sub find {
my ($self, $expr) = @_;
$expr = '.*' if !$expr;
$expr = qr/$expr/ if ref($expr) ne 'Regexp';
my @paths;
push @paths, grep {
$_ =~ $expr
} map {
$_->is_directory ? $_->find($expr) : $_
}
$self->children;
return wantarray ? (@paths) : \@paths;
}
sub files {
my ($self) = @_;
my @paths = grep -f, $self->children;
return wantarray ? (@paths) : \@paths;
}
sub glob {
my ($self, $expr) = @_;
require File::Spec;
$expr ||= '*';
my @paths = map $self->class->new($_),
CORE::glob +File::Spec->catfile($self->absolute, $expr);
return wantarray ? (@paths) : \@paths;
}
sub is_absolute {
my ($self) = @_;
require File::Spec;
return int!!(File::Spec->file_name_is_absolute($self->get));
}
sub is_directory {
my ($self) = @_;
my $path = $self->get;
return int!!(-e $path && -d $path);
}
sub is_file {
my ($self) = @_;
my $path = $self->get;
return int!!(-e $path && !-d $path);
}
sub is_relative {
my ($self) = @_;
return int!$self->is_absolute;
}
sub lines {
my ($self, $separator, $binmode) = @_;
$separator //= "\n";
return [split /$separator/, $binmode ? $self->read($binmode) : $self->read];
}
sub lineage {
my ($self) = @_;
require File::Spec;
my @parts = File::Spec->splitdir($self->get);
my @paths = ((
reverse map $self->class->new(File::Spec->catfile(@parts[0..$_])), 1..$#parts
), $self->class->new($parts[0]));
return wantarray ? (@paths) : \@paths;
}
sub open {
my ($self, @args) = @_;
my $path = $self->get;
require IO::File;
my $handle = IO::File->new;
$handle->open($path, @args) or do {
my $throw;
my $error = "Can't open $path: $!";
$throw = $self->throw;
$throw->name('on.open');
$throw->message($error);
$throw->stash(path => $path);
$throw->error;
};
return $handle;
}
sub mkcall {
my ($self, $args) = @_;
my $path = File::Spec->catfile(File::Spec->splitdir($self->get));
my $result;
(defined($result = ($args ? qx($path $args) : qx($path)))) or do {
my $throw;
my $error = "Can't mkcall $path: " . ($! ? "$!" : "exit code ($?)");
$throw = $self->throw;
$throw->name('on.mkcall');
$throw->message($error);
$throw->stash(path => $path);
$throw->error;
};
chomp $result;
return wantarray ? ($result, $?) : $result;
}
sub mkdir {
my ($self, $mode) = @_;
my $path = $self->get;
($mode ? CORE::mkdir($path, $mode) : CORE::mkdir($path)) or do {
my $throw;
my $error = "Can't mkdir $path: $!";
$throw = $self->throw;
$throw->name('on.mkdir');
$throw->message($error);
$throw->stash(path => $path);
$throw->error;
};
return $self;
}
sub mkdirs {
my ($self, $mode) = @_;
my @paths;
for my $path (
grep !!$_, reverse($self->parents), ($self->is_file ? () : $self)
)
{
if ($path->exists) {
next;
}
else {
push @paths, $path->mkdir($mode);
}
}
return wantarray ? (@paths) : \@paths;
}
sub mkfile {
my ($self) = @_;
my $path = $self->get;
return $self if $self->exists;
$self->open('>');
CORE::utime(undef, undef, $path) or do {
my $throw;
my $error = "Can't mkfile $path: $!";
$throw = $self->throw;
$throw->name('on.mkfile');
$throw->message($error);
$throw->stash(path => $path);
$throw->error;
};
return $self;
}
sub name {
my ($self) = @_;
return $self->absolute->get;
}
sub parent {
my ($self) = @_;
require File::Spec;
my @parts = File::Spec->splitdir($self->get);
my $path = File::Spec->catfile(@parts[0..$#parts-1]);
return defined $path ? $self->class->new($path) : undef;
}
sub parents {
my ($self) = @_;
my @paths = $self->lineage;
@paths = @paths[1..$#paths] if @paths;
return wantarray ? (@paths) : \@paths;
}
sub parts {
my ($self) = @_;
require File::Spec;
return [File::Spec->splitdir($self->get)];
}
sub read {
my ($self, $binmode) = @_;
my $path = $self->get;
CORE::open(my $handle, '<', $path) or do {
my $throw;
my $error = "Can't read $path: $!";
$throw = $self->throw;
$throw->name('on.read.open');
$throw->message($error);
$throw->stash(path => $path);
$throw->error;
};
CORE::binmode($handle, $binmode) or do {
my $throw;
my $error = "Can't binmode $path: $!";
$throw = $self->throw;
$throw->name('on.read.binmode');
$throw->message($error);
$throw->stash(binmode => $binmode);
$throw->stash(path => $path);
$throw->error;
}
if defined($binmode);
my $result = my $content = '';
while ($result = $handle->sysread(my $buffer, 131072, 0)) {
$content .= $buffer;
}
if (not(defined($result))) {
my $throw;
my $error = "Can't read from file $path: $!";
$throw = $self->throw;
$throw->name('on.read.error');
$throw->message($error);
$throw->stash(path => $path);
$throw->error;
}
if ($^O =~ /win32/i) {
$content =~ s/\015\012/\012/g;
}
return $content;
}
sub relative {
my ($self, $path) = @_;
require File::Spec;
$path ||= $self->default;
return $self->class->new(File::Spec->abs2rel($self->get, $path));
}
sub rmdir {
my ($self) = @_;
my $path = $self->get;
CORE::rmdir($path) or do {
my $throw;
my $error = "Can't rmdir $path: $!";
$throw = $self->throw;
$throw->name('on.rmdir');
$throw->message($error);
$throw->stash(path => $path);
$throw->error;
};
return $self;
}
sub rmdirs {
my ($self) = @_;
my @paths;
for my $path ($self->children) {
if ($path->is_file) {
push @paths, $path->unlink;
}
else {
push @paths, $path->rmdirs;
}
}
push @paths, $self->rmdir;
return wantarray ? (@paths) : \@paths;
}
sub rmfiles {
my ($self) = @_;
my @paths;
for my $path ($self->children) {
if ($path->is_file) {
push @paths, $path->unlink;
}
else {
push @paths, $path->rmfiles;
}
}
return wantarray ? (@paths) : \@paths;
}
sub sibling {
my ($self, $path) = @_;
require File::Basename;
require File::Spec;
return $self->class->new(File::Spec->catfile(
File::Basename::dirname($self->get), $path));
}
sub siblings {
my ($self) = @_;
my @paths = map $self->parent->glob($_), '.??*', '*';
my %seen = ($self->absolute, 1);
@paths = grep !$seen{$_}++, @paths;
return wantarray ? (@paths) : \@paths;
}
sub test {
my ($self, $spec) = @_;
return eval(
join(' ', map("-$_", grep(/^[a-zA-Z]$/, split(//, $spec || 'e'))), '$self')
);
}
sub unlink {
my ($self) = @_;
my $path = $self->get;
CORE::unlink($path) or do {
my $throw;
my $error = "Can't unlink $path: $!";
$throw = $self->throw;
$throw->name('on.unlink');
$throw->message($error);
$throw->stash(path => $path);
$throw->error;
};
return $self;
}
sub write {
my ($self, $data, $binmode) = @_;
my $path = $self->get;
CORE::open(my $handle, '>', $path) or do {
my $throw;
my $error = "Can't write $path: $!";
$throw = $self->throw;
$throw->name('on.write.open');
$throw->message($error);
$throw->stash(path => $path);
$throw->error;
};
CORE::binmode($handle, $binmode) or do {
my $throw;
my $error = "Can't binmode $path: $!";
$throw = $self->throw;
$throw->name('on.write.binmode');
$throw->message($error);
$throw->stash(binmode => $binmode);
$throw->stash(path => $path);
$throw->error;
}
if defined($binmode);
(($handle->syswrite($data) // -1) == length($data)) or do {
my $throw;
my $error = "Can't write to file $path: $!";
$throw = $self->throw;
$throw->name('on.write.error');
$throw->message($error);
$throw->stash(path => $path);
$throw->error;
};
return $self;
}
1;
=head1 NAME
Venus::Path - Path Class
=cut
=head1 ABSTRACT
Path Class for Perl 5
=cut
=head1 SYNOPSIS
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/planets');
# my $planets = $path->files;
# my $mercury = $path->child('mercury');
# my $content = $mercury->read;
=cut
=head1 DESCRIPTION
This package provides methods for working with file system paths.
=cut
=head1 INHERITS
This package inherits behaviors from:
L<Venus::Kind::Utility>
=cut
=head1 INTEGRATES
This package integrates behaviors from:
L<Venus::Role::Accessible>
L<Venus::Role::Buildable>
L<Venus::Role::Explainable>
L<Venus::Role::Valuable>
=cut
=head1 METHODS
This package provides the following methods:
=cut
=head2 absolute
absolute() (Path)
The absolute method returns a path object where the value (path) is absolute.
I<Since C<0.01>>
=over 4
=item absolute example 1
# given: synopsis;
$path = $path->absolute;
# bless({ value => "/path/to/t/data/planets" }, "Venus::Path")
=back
=cut
=head2 basename
basename() (Str)
The basename method returns the path base name.
I<Since C<0.01>>
=over 4
=item basename example 1
# given: synopsis;
my $basename = $path->basename;
# planets
=back
=cut
=head2 child
child(Str $path) (Path)
The child method returns a path object representing the child path provided.
I<Since C<0.01>>
=over 4
=item child example 1
# given: synopsis;
$path = $path->child('earth');
# bless({ value => "t/data/planets/earth" }, "Venus::Path")
=back
=cut
=head2 children
children() (ArrayRef[Path])
The children method returns the files and directories under the path. This
method can return a list of values in list-context.
I<Since C<0.01>>
=over 4
=item children example 1
# given: synopsis;
my $children = $path->children;
# [
# bless({ value => "t/data/planets/ceres" }, "Venus::Path"),
# bless({ value => "t/data/planets/earth" }, "Venus::Path"),
# bless({ value => "t/data/planets/eris" }, "Venus::Path"),
# bless({ value => "t/data/planets/haumea" }, "Venus::Path"),
# bless({ value => "t/data/planets/jupiter" }, "Venus::Path"),
# bless({ value => "t/data/planets/makemake" }, "Venus::Path"),
# bless({ value => "t/data/planets/mars" }, "Venus::Path"),
# bless({ value => "t/data/planets/mercury" }, "Venus::Path"),
# bless({ value => "t/data/planets/neptune" }, "Venus::Path"),
# bless({ value => "t/data/planets/planet9" }, "Venus::Path"),
# bless({ value => "t/data/planets/pluto" }, "Venus::Path"),
# bless({ value => "t/data/planets/saturn" }, "Venus::Path"),
# bless({ value => "t/data/planets/uranus" }, "Venus::Path"),
# bless({ value => "t/data/planets/venus" }, "Venus::Path"),
# ]
=back
=cut
=head2 chmod
chmod(Str $mode) (Path)
The chmod method changes the file permissions of the file or directory.
I<Since C<0.01>>
=over 4
=item chmod example 1
# given: synopsis;
$path = $path->chmod(0755);
# bless({ value => "t/data/planets" }, "Venus::Path")
=back
=cut
=head2 chown
chown(Str @args) (Path)
The chown method changes the group and/or owner or the file or directory.
I<Since C<0.01>>
=over 4
=item chown example 1
# given: synopsis;
$path = $path->chown(-1, -1);
# bless({ value => "t/data/planets" }, "Venus::Path")
=back
=cut
=head2 default
default() (Str)
The default method returns the default value, i.e. C<$ENV{PWD}>.
I<Since C<0.01>>
=over 4
=item default example 1
# given: synopsis;
my $default = $path->default;
# $ENV{PWD}
=back
=cut
=head2 directories
directories() (ArrayRef[Path])
The directories method returns a list of children under the path which are
directories. This method can return a list of values in list-context.
I<Since C<0.01>>
=over 4
=item directories example 1
# given: synopsis;
my $directories = $path->directories;
# []
=back
=cut
=head2 exists
exists() (Bool)
The exists method returns truthy or falsy if the path exists.
I<Since C<0.01>>
=over 4
=item exists example 1
# given: synopsis;
my $exists = $path->exists;
# 1
=back
=over 4
=item exists example 2
# given: synopsis;
my $exists = $path->child('random')->exists;
# 0
=back
=cut
=head2 explain
explain() (Str)
The explain method returns the path string and is used in stringification
operations.
I<Since C<0.01>>
=over 4
=item explain example 1
# given: synopsis;
my $explain = $path->explain;
# t/data/planets
=back
=cut
=head2 files
files() (ArrayRef[Path])
The files method returns a list of children under the path which are files.
This method can return a list of values in list-context.
I<Since C<0.01>>
=over 4
=item files example 1
# given: synopsis;
my $files = $path->files;
# [
# bless({ value => "t/data/planets/ceres" }, "Venus::Path"),
# bless({ value => "t/data/planets/earth" }, "Venus::Path"),
# bless({ value => "t/data/planets/eris" }, "Venus::Path"),
# bless({ value => "t/data/planets/haumea" }, "Venus::Path"),
# bless({ value => "t/data/planets/jupiter" }, "Venus::Path"),
# bless({ value => "t/data/planets/makemake" }, "Venus::Path"),
# bless({ value => "t/data/planets/mars" }, "Venus::Path"),
# bless({ value => "t/data/planets/mercury" }, "Venus::Path"),
# bless({ value => "t/data/planets/neptune" }, "Venus::Path"),
# bless({ value => "t/data/planets/planet9" }, "Venus::Path"),
# bless({ value => "t/data/planets/pluto" }, "Venus::Path"),
# bless({ value => "t/data/planets/saturn" }, "Venus::Path"),
# bless({ value => "t/data/planets/uranus" }, "Venus::Path"),
# bless({ value => "t/data/planets/venus" }, "Venus::Path"),
# ]
=back
=cut
=head2 find
find(Str | Regexp $expr) (ArrayRef[Path])
The find method does a recursive depth-first search and returns a list of paths
found, matching the expression provided, which defaults to C<*>. This method
can return a list of values in list-context.
I<Since C<0.01>>
=over 4
=item find example 1
# given: synopsis;
my $find = $path->find;
# [
# bless({ value => "t/data/planets/ceres" }, "Venus::Path"),
# bless({ value => "t/data/planets/earth" }, "Venus::Path"),
# bless({ value => "t/data/planets/eris" }, "Venus::Path"),
# bless({ value => "t/data/planets/haumea" }, "Venus::Path"),
# bless({ value => "t/data/planets/jupiter" }, "Venus::Path"),
# bless({ value => "t/data/planets/makemake" }, "Venus::Path"),
# bless({ value => "t/data/planets/mars" }, "Venus::Path"),
# bless({ value => "t/data/planets/mercury" }, "Venus::Path"),
# bless({ value => "t/data/planets/neptune" }, "Venus::Path"),
# bless({ value => "t/data/planets/planet9" }, "Venus::Path"),
# bless({ value => "t/data/planets/pluto" }, "Venus::Path"),
# bless({ value => "t/data/planets/saturn" }, "Venus::Path"),
# bless({ value => "t/data/planets/uranus" }, "Venus::Path"),
# bless({ value => "t/data/planets/venus" }, "Venus::Path"),
# ]
=back
=over 4
=item find example 2
# given: synopsis;
my $find = $path->find('[:\/\\\.]+m[^:\/\\\.]*$');
# [
# bless({ value => "t/data/planets/makemake" }, "Venus::Path"),
# bless({ value => "t/data/planets/mars" }, "Venus::Path"),
# bless({ value => "t/data/planets/mercury" }, "Venus::Path"),
# ]
=back
=over 4
=item find example 3
# given: synopsis;
my $find = $path->find('earth');
# [
# bless({ value => "t/data/planets/earth" }, "Venus::Path"),
# ]
=back
=cut
=head2 glob
glob(Str | Regexp $expr) (ArrayRef[Path])
The glob method returns the files and directories under the path matching the
expression provided, which defaults to C<*>. This method can return a list of
values in list-context.
I<Since C<0.01>>
=over 4
=item glob example 1
# given: synopsis;
my $glob = $path->glob;
# [
# bless({ value => "t/data/planets/ceres" }, "Venus::Path"),
# bless({ value => "t/data/planets/earth" }, "Venus::Path"),
# bless({ value => "t/data/planets/eris" }, "Venus::Path"),
# bless({ value => "t/data/planets/haumea" }, "Venus::Path"),
# bless({ value => "t/data/planets/jupiter" }, "Venus::Path"),
# bless({ value => "t/data/planets/makemake" }, "Venus::Path"),
# bless({ value => "t/data/planets/mars" }, "Venus::Path"),
# bless({ value => "t/data/planets/mercury" }, "Venus::Path"),
# bless({ value => "t/data/planets/neptune" }, "Venus::Path"),
# bless({ value => "t/data/planets/planet9" }, "Venus::Path"),
# bless({ value => "t/data/planets/pluto" }, "Venus::Path"),
# bless({ value => "t/data/planets/saturn" }, "Venus::Path"),
# bless({ value => "t/data/planets/uranus" }, "Venus::Path"),
# bless({ value => "t/data/planets/venus" }, "Venus::Path"),
# ]
=back
=cut
=head2 is_absolute
is_absolute() (Bool)
The is_absolute method returns truthy or falsy is the path is absolute.
I<Since C<0.01>>
=over 4
=item is_absolute example 1
# given: synopsis;
my $is_absolute = $path->is_absolute;
# 0
=back
=cut
=head2 is_directory
is_directory() (Bool)
The is_directory method returns truthy or falsy is the path is a directory.
I<Since C<0.01>>
=over 4
=item is_directory example 1
# given: synopsis;
my $is_directory = $path->is_directory;
# 1
=back
=cut
=head2 is_file
is_file() (Bool)
The is_file method returns truthy or falsy is the path is a file.
I<Since C<0.01>>
=over 4
=item is_file example 1
# given: synopsis;
my $is_file = $path->is_file;
# 0
=back
=cut
=head2 is_relative
is_relative() (Bool)
The is_relative method returns truthy or falsy is the path is relative.
I<Since C<0.01>>
=over 4
=item is_relative example 1
# given: synopsis;
my $is_relative = $path->is_relative;
# 1
=back
=cut
=head2 lineage
lineage() (ArrayRef[Path])
The lineage method returns the list of parent paths up to the root path. This
method can return a list of values in list-context.
I<Since C<0.01>>
=over 4
=item lineage example 1
# given: synopsis;
my $lineage = $path->lineage;
# [
# bless({ value => "t/data/planets" }, "Venus::Path"),
# bless({ value => "t/data" }, "Venus::Path"),
# bless({ value => "t" }, "Venus::Path"),
# ]
=back
=cut
=head2 lines
lines(Str|Regexp $separator, Str $binmode) (ArrayRef[Str])
The lines method returns the list of lines from the underlying file. By default
the file contents are separated by newline.
I<Since C<1.23>>
=over 4
=item lines example 1
# given: synopsis;
my $lines = $path->child('mercury')->lines;
# ['mercury']
=back
=over 4
=item lines example 2
# given: synopsis;
my $lines = $path->child('planet9')->lines($^O =~ /win32/i ? "\n" : "\r\n");
# ['planet', 'nine']
=back
=cut
=head2 mkcall
mkcall(Any @data) (Any)
The mkcall method returns the result of executing the path as an executable. In
list context returns the call output and exit code.
I<Since C<0.01>>
=over 4
=item mkcall example 1
package main;
use Venus::Path;
my $path = Venus::Path->new($^X);
my $output = $path->mkcall('--help');
# Usage: perl ...
=back
=over 4
=item mkcall example 2
package main;
use Venus::Path;
my $path = Venus::Path->new($^X);
my ($call_output, $exit_code) = $path->mkcall('t/data/sun --heat-death');
# ("", 256)
=back
=over 4
=item mkcall example 3
package main;
use Venus::Path;
my $path = Venus::Path->new('.help');
my $output = $path->mkcall;
# Exception! Venus::Path::Error (isa Venus::Error)
=back
=cut
=head2 mkdir
mkdir(Maybe[Str] $mode) (Path)
The mkdir method makes the path as a directory.
I<Since C<0.01>>
=over 4
=item mkdir example 1
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/systems');
$path = $path->mkdir;
# bless({ value => "t/data/systems" }, "Venus::Path")
=back
=over 4
=item mkdir example 2
package main;
use Venus::Path;
my $path = Venus::Path->new('/path/to/xyz');
$path = $path->mkdir;
# Exception! Venus::Path::Error (isa Venus::Error)
=back
=cut
=head2 mkdirs
mkdirs(Maybe[Str] $mode) (ArrayRef[Path])
The mkdirs method creates parent directories and returns the list of created
directories. This method can return a list of values in list-context.
I<Since C<0.01>>
=over 4
=item mkdirs example 1
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/systems');
my $mkdirs = $path->mkdirs;
# [
# bless({ value => "t/data/systems" }, "Venus::Path")
# ]
=back
=over 4
=item mkdirs example 2
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/systems/solar');
my $mkdirs = $path->mkdirs;
# [
# bless({ value => "t/data/systems" }, "Venus::Path"),
# bless({ value => "t/data/systems/solar" }, "Venus::Path"),
# ]
=back
=cut
=head2 mkfile
mkfile() (Path)
The mkfile method makes the path as an empty file.
I<Since C<0.01>>
=over 4
=item mkfile example 1
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/moon');
$path = $path->mkfile;
# bless({ value => "t/data/moon" }, "Venus::Path")
=back
=over 4
=item mkfile example 2
package main;
use Venus::Path;
my $path = Venus::Path->new('/path/to/xyz');
$path = $path->mkfile;
# Exception! Venus::Path::Error (isa Venus::Error)
=back
=cut
=head2 name
name() (Str)
The name method returns the path as an absolute path.
I<Since C<0.01>>
=over 4
=item name example 1
# given: synopsis;
my $name = $path->name;
# /path/to/t/data/planets
=back
=cut
=head2 open
open(Any @data) (FileHandle)
The open method creates and returns an open filehandle.
I<Since C<0.01>>
=over 4
=item open example 1
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/planets/earth');
my $fh = $path->open;
# bless(..., "IO::File");
=back
=over 4
=item open example 2
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/planets/earth');
my $fh = $path->open('<');
# bless(..., "IO::File");
=back
=over 4
=item open example 3
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/planets/earth');
my $fh = $path->open('>');
# bless(..., "IO::File");
=back
=over 4
=item open example 4
package main;
use Venus::Path;
my $path = Venus::Path->new('/path/to/xyz');
my $fh = $path->open('>');
# Exception! Venus::Path::Error (isa Venus::Error)
=back
=cut
=head2 parent
parent() (Path)
The parent method returns a path object representing the parent directory.
I<Since C<0.01>>
=over 4
=item parent example 1
# given: synopsis;
my $parent = $path->parent;
# bless({ value => "t/data" }, "Venus::Path")
=back
=cut
=head2 parents
parents() (ArrayRef[Path])
The parents method returns is a list of parent directories. This method can
return a list of values in list-context.
I<Since C<0.01>>
=over 4
=item parents example 1
# given: synopsis;
my $parents = $path->parents;
# [
# bless({ value => "t/data" }, "Venus::Path"),
# bless({ value => "t" }, "Venus::Path"),
# ]
=back
=cut
=head2 parts
parts() (ArrayRef[Str])
The parts method returns an arrayref of path parts.
I<Since C<0.01>>
=over 4
=item parts example 1
# given: synopsis;
my $parts = $path->parts;
# ["t", "data", "planets"]
=back
=cut
=head2 read
read(Str $binmode) (Str)
The read method reads the file and returns its contents.
I<Since C<0.01>>
=over 4
=item read example 1
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/planets/mars');
my $content = $path->read;
=back
=over 4
=item read example 2
package main;
use Venus::Path;
my $path = Venus::Path->new('/path/to/xyz');
my $content = $path->read;
# Exception! Venus::Path::Error (isa Venus::Error)
=back
=cut
=head2 relative
relative(Str $root) (Path)
The relative method returns a path object representing a relative path
(relative to the path provided).
I<Since C<0.01>>
=over 4
=item relative example 1
package main;
use Venus::Path;
my $path = Venus::Path->new('/path/to/t/data/planets/mars');
my $relative = $path->relative('/path');
# bless({ value => "to/t/data/planets/mars" }, "Venus::Path")
=back
=over 4
=item relative example 2
package main;
use Venus::Path;
my $path = Venus::Path->new('/path/to/t/data/planets/mars');
my $relative = $path->relative('/path/to/t');
# bless({ value => "data/planets/mars" }, "Venus::Path")
=back
=cut
=head2 rmdir
rmdir() (Path)
The rmdir method removes the directory and returns a path object representing
the deleted directory.
I<Since C<0.01>>
=over 4
=item rmdir example 1
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/stars');
my $rmdir = $path->mkdir->rmdir;
# bless({ value => "t/data/stars" }, "Venus::Path")
=back
=over 4
=item rmdir example 2
package main;
use Venus::Path;
my $path = Venus::Path->new('/path/to/xyz');
my $rmdir = $path->mkdir->rmdir;
# Exception! Venus::Path::Error (isa Venus::Error)
=back
=cut
=head2 rmdirs
rmdirs() (ArrayRef[Path])
The rmdirs method removes that path and its child files and directories and
returns all paths removed. This method can return a list of values in
list-context.
I<Since C<0.01>>
=over 4
=item rmdirs example 1
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/stars');
$path->child('dwarfs')->mkdirs;
my $rmdirs = $path->rmdirs;
# [
# bless({ value => "t/data/stars/dwarfs" }, "Venus::Path"),
# bless({ value => "t/data/stars" }, "Venus::Path"),
# ]
=back
=cut
=head2 rmfiles
rmfiles() (ArrayRef[Path])
The rmfiles method recursively removes files under the path and returns the
paths removed. This method does not remove the directories found. This method
can return a list of values in list-context.
I<Since C<0.01>>
=over 4
=item rmfiles example 1
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/stars')->mkdir;
$path->child('sirius')->mkfile;
$path->child('canopus')->mkfile;
$path->child('arcturus')->mkfile;
$path->child('vega')->mkfile;
$path->child('capella')->mkfile;
my $rmfiles = $path->rmfiles;
# [
# bless({ value => "t/data/stars/arcturus" }, "Venus::Path"),
# bless({ value => "t/data/stars/canopus" }, "Venus::Path"),
# bless({ value => "t/data/stars/capella" }, "Venus::Path"),
# bless({ value => "t/data/stars/sirius" }, "Venus::Path"),
# bless({ value => "t/data/stars/vega" }, "Venus::Path"),
# ]
=back
=cut
=head2 sibling
sibling(Str $path) (Path)
The sibling method returns a path object representing the sibling path provided.
I<Since C<0.01>>
=over 4
=item sibling example 1
# given: synopsis;
my $sibling = $path->sibling('galaxies');
# bless({ value => "t/data/galaxies" }, "Venus::Path")
=back
=cut
=head2 siblings
siblings() (ArrayRef[Path])
The siblings method returns all sibling files and directories for the current
path. This method can return a list of values in list-context.
I<Since C<0.01>>
=over 4
=item siblings example 1
# given: synopsis;
my $siblings = $path->siblings;
# [
# bless({ value => "t/data/moon" }, "Venus::Path"),
# bless({ value => "t/data/sun" }, "Venus::Path"),
# ]
=back
=cut
=head2 test
test(Str $expr) (Bool)
The test method evaluates the current path against the stackable file test
operators provided.
I<Since C<0.01>>
=over 4
=item test example 1
# given: synopsis;
my $test = $path->test;
# -e $path
# 1
=back
=over 4
=item test example 2
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/sun');
my $test = $path->test('efs');
# -e -f -s $path
# 1
=back
=cut
=head2 unlink
unlink() (Path)
The unlink method removes the file and returns a path object representing the
removed file.
I<Since C<0.01>>
=over 4
=item unlink example 1
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/asteroid')->mkfile;
my $unlink = $path->unlink;
# bless({ value => "t/data/asteroid" }, "Venus::Path")
=back
=over 4
=item unlink example 2
package main;
use Venus::Path;
my $path = Venus::Path->new('/path/to/xyz');
my $unlink = $path->unlink;
# Exception! Venus::Path::Error (isa Venus::Error)
=back
=cut
=head2 write
write(Str $data, Str $binmode) (Path)
The write method write the data provided to the file.
I<Since C<0.01>>
=over 4
=item write example 1
package main;
use Venus::Path;
my $path = Venus::Path->new('t/data/asteroid');
my $write = $path->write('asteroid');
=back
=over 4
=item write example 2
package main;
use Venus::Path;
my $path = Venus::Path->new('/path/to/xyz');
my $write = $path->write('nothing');
# Exception! Venus::Path::Error (isa Venus::Error)
=back
=cut
=head1 OPERATORS
This package overloads the following operators:
=cut
=over 4
=item operation: C<(.)>
This package overloads the C<.> operator.
B<example 1>
# given: synopsis;
my $result = $path . '/earth';
# "t/data/planets/earth"
=back
=over 4
=item operation: C<(eq)>
This package overloads the C<eq> operator.
B<example 1>
# given: synopsis;
my $result = $path eq 't/data/planets';
# 1
=back
=over 4
=item operation: C<(ne)>
This package overloads the C<ne> operator.
B<example 1>
# given: synopsis;
my $result = $path ne 't/data/planets/';
# 1
=back
=over 4
=item operation: C<(qr)>
This package overloads the C<qr> operator.
B<example 1>
# given: synopsis;
my $result = 't/data/planets' =~ $path;
# 1
=back
=over 4
=item operation: C<("")>
This package overloads the C<""> operator.
B<example 1>
# given: synopsis;
my $result = "$path";
# "t/data/planets"
B<example 2>
# given: synopsis;
my $mercury = $path->child('mercury');
my $result = "$path, $path";
# "t/data/planets, t/data/planets"
=back
=over 4
=item operation: C<(~~)>
This package overloads the C<~~> operator.
B<example 1>
# given: synopsis;
my $result = $path ~~ 't/data/planets';
# 1
=back