Tree-File
view release on metacpan or search on metacpan
lib/Tree/File.pm view on Meta::CPAN
my $data = $self->load_file($file);
$lock_mgr->unlock();
return $self->_new_node($root, $data, \%$arg);
}
elsif (-d $file) {
my $dir;
opendir $dir, $file or croak "can't open branch directory $dir: $!";
my $tree = {};
for my $twig (grep { $_ !~ /\A\./ && ! -l "$file/$_" && $_ ne 'CVS' } readdir $dir) {
$tree->{$twig} = $preload
? $self->_load("$root/$twig", $preload-1, { %$arg, preload => $preload-1})
: sub { $self->_load("$root/$twig", 0, { %$arg, preload => 0 }) };
}
$lock_mgr->unlock();
return $self->_new_node($root, $tree, { %$arg, type => 'dir' });
}
else {
$lock_mgr->unlock();
croak "$file doesn't exist or isn't a normal file or directory";
}
}
=head2 C<< $tree->get($id) >>
This returns the branch with the given name. If the name contains slashes,
they indicate recursive fetches, so that these two calls are identical:
$tree->get("foo")->get("bar")->get("baz");
$tree->get("foo/bar/baz");
Leading slashes are ignored.
If a second, true argument is passed to C<get>, any missing data structures
will be autovivified as needed to get to the leaf.
=cut
sub _not_found {
my ($self) = shift;
if ($self->{not_found}) { return $self->{not_found}->(@_) }
return;
}
sub _found {
my ($self) = shift;
return $self->{found} ? $self->{found}->($self, @_) : $_[1];
}
sub get {
my ($self, $id, $autovivify) = @_;
$id && $id =~ s|\A/+||;
my $rest;
croak "get called on $self without property identifier" unless defined $id;
($id, $rest) = split m|/|, $id, 2;
if ($rest) {
my $head = $self->get($id, $autovivify);
return $self->_not_found($id, $self->{root}) unless $head;
return $head->get($rest, $autovivify);
}
if (exists $self->{data}{$id}) {
if (ref $self->{data}{$id} eq 'CODE') {
$self->{data}{$id} = $self->{data}{$id}->();
}
return $self->_found($id, $self->{data}{$id});
}
if ($autovivify) {
return $self->{data}{$id} =
$self->_new_node("$self->{root}/$id", {});
}
return $self->_not_found($id, $self->{root});
}
=head2 C<< $tree->set($id, $value) >>
This sets the identified branch's value to the given value. Hash references
are automatically expanded into trees.
=cut
sub set { ## no critic Ambiguous
my ($self, $id, $value, $root) = @_;
$value = $value->data if eval { $value->isa("Tree::File") };
croak "set called on readonly tree" if $self->{readonly};
$id && $id =~ s|\A/+||;
$root = $id unless $root;
my $rest;
croak "set called on $self without property identifier" unless defined $id;
($id, $rest) = split m|/|, $id, 2;
if ($rest) { return $self->get($id, 1)->set($rest, $value, $root); }
return $self->{data}{$id} =
$self->_new_node($root, $value);
}
=head2 C<< $tree->delete($id) >>
This method deletes the identified branch (and returns the deleted value).
=cut
sub delete { ## no critic Homonym
my ($self, $id) = @_;
croak "delete called on readonly tree" if $self->{readonly};
$id && $id =~ s|\A/+||;
my $rest;
croak "delete called on $self without property identifier" unless defined $id;
($id, $rest) = split m|/|, $id, 2;
if ($rest) { return $self->get($id)->delete($rest); }
return delete $self->{data}{$id};
}
=head2 C<< $tree->move($old_id, $new_id) >>
This method deletes the value at the old id and places it at the new id.
=cut
sub move {
my ($self, $old_id, $new_id) = @_;
$self->set($new_id, $self->delete($old_id));
}
=head2 C<< $tree->path() >>
This method returns the path to this node from the root.
=cut
sub path {
my ($self) = @_;
return $self->{root};
}
=head2 C<< $tree->basename() >>
This method retuns the base name of the node. (If, for example, the path to
the node is "/things/good/all" then its base name is "all".)
=cut
sub basename {
my ($self) = @_;
my @parts = split m{/}, $self->path();
return $parts[-1];
}
sub _handoff {
my $self = shift;
my $method = (caller(1))[3];
$method =~ s/.*:://;
my $node = $self->get(@_);
unless ($node) {
return $self->_not_found(@_);
}
#warn "handing off $method to " . $node->path . "\n";
$node->$method;
}
=head2 C<< $tree->node_names() >>
This method returns the names of all the nodes beneath this branch.
=cut
sub node_names {
my $self = shift;
return $self->_handoff(@_) if @_;
return sort keys %{$self->{data}};
}
=head2 C<< $tree->nodes() >>
This method returns each node beneath this branch.
=cut
sub nodes {
my $self = shift;
return $self->_handoff(@_) if @_;
return map { $self->get($_) } $self->node_names();
}
=head2 C<< $tree->branch_names >>
=cut
sub branch_names {
my $self = shift;
return $self->_handoff(@_) if @_;
return grep { eval { $self->get($_)->isa("Tree::File") } } $self->node_names;
}
=head2 C<< $tree->branches >>
This method returns all the nodes on this branch which are also branches (that
is, are also Tree::File objects).
=cut
sub branches {
my $self = shift;
return $self->_handoff(@_) if @_;
return map { $self->get($_) } $self->branch_names();
( run in 0.593 second using v1.01-cache-2.11-cpan-71847e10f99 )