package Venus::Box;
use 5.018;
use strict;
use warnings;
use Venus::Class 'with';
with 'Venus::Role::Buildable';
with 'Venus::Role::Proxyable';
# BUILDERS
sub build_arg {
my ($self, $data) = @_;
return {
value => $data,
};
}
sub build_args {
my ($self, $data) = @_;
if (keys %$data == 1 && exists $data->{value}) {
return $data;
}
return {
value => $data,
};
}
sub build_self {
my ($self, $data) = @_;
require Venus::Type;
$data //= {};
$self->{value} = Venus::Type->new(value => $data->{value})->deduce;
return $self;
}
sub build_proxy {
my ($self, $package, $method, @args) = @_;
require Scalar::Util;
my $value = $self->{value};
if (not(Scalar::Util::blessed($value))) {
require Venus::Error;
return Venus::Error->throw(
"$package can only operate on objects, not $value"
);
}
if (!$value->can($method)) {
if (my $handler = $self->can("__handle__${method}")) {
return sub {$self->$handler(@args)};
}
elsif (!$value->can('AUTOLOAD')) {
return undef;
}
}
return sub {
local $_ = $value;
my $result = [
$value->$method(@args)
];
$result = $result->[0] if @$result == 1;
if (Scalar::Util::blessed($result)) {
return not(UNIVERSAL::isa($result, 'Venus::Box'))
? ref($self)->new(value => $result)
: $result;
}
else {
require Venus::Type;
return ref($self)->new(
value => Venus::Type->new(value => $result)->deduce
);
}
};
}
# METHODS
sub __handle__unbox {
my ($self, $code, @args) = @_;
return $code ? $self->$code(@args)->{value} : $self->{value};
}
1;
=head1 NAME
Venus::Box - Box Class
=cut
=head1 ABSTRACT
Box Class for Perl 5
=cut
=head1 SYNOPSIS
package main;
use Venus::Box;
my $box = Venus::Box->new(
value => {},
);
# $box->keys->count->unbox;
=cut
=head1 DESCRIPTION
This package provides a pure Perl boxing mechanism for wrapping objects and
values, and chaining method calls across all objects.
=cut
=head1 INTEGRATES
This package integrates behaviors from:
L<Venus::Role::Buildable>
L<Venus::Role::Proxyable>
=cut
=head1 METHODS
This package provides the following methods:
=cut
=head2 unbox
unbox(Str $method, Any @args) (Any)
The unbox method returns the un-boxed underlying object. This is a virtual
method that dispatches to C<__handle__unbox>. This method supports dispatching,
i.e. providing a method name and arguments whose return value will be acted on
by this method.
I<Since C<0.01>>
=over 4
=item unbox example 1
# given: synopsis;
my $unbox = $box->unbox;
# bless({ value => {} }, "Venus::Hash")
=back
=over 4
=item unbox example 2
# given: synopsis;
my $unbox = $box->unbox('count');
# 0
=back
=cut