package Venus::Role::Proxyable;

use 5.018;

use strict;
use warnings;

use Venus::Role 'with';

# METHODS

sub AUTOLOAD {
  require Venus::Error;

  my ($package, $method) = our $AUTOLOAD =~ m[^(.+)::(.+)$];

  my $build = $package->can('BUILDPROXY');

  my $error = qq(Can't locate object method "$method" via package "$package");

  Venus::Error->throw($error) unless $build && ref($build) eq 'CODE';

  my $proxy = $build->($package, $method, @_);

  Venus::Error->throw($error) unless $proxy && ref($proxy) eq 'CODE';

  goto &$proxy;
}

sub BUILDPROXY {
  require Venus::Error;

  my ($package, $method, $self, @args) = @_;

  my $build = $self->can('build_proxy');

  return $build->($self, $package, $method, @args) if $build;

  my $error = qq(Can't locate object method "build_proxy" via package "$package");

  Venus::Error->throw($error);
}

# EXPORTS

sub EXPORT {
  ['AUTOLOAD', 'BUILDPROXY']
}

1;



=head1 NAME

Venus::Role::Proxyable - Proxyable Role

=cut

=head1 ABSTRACT

Proxyable Role for Perl 5

=cut

=head1 SYNOPSIS

  package Example;

  use Venus::Class;

  with 'Venus::Role::Proxyable';

  attr 'test';

  sub build_proxy {
    my ($self, $package, $method, @args) = @_;
    return sub { [$self, $package, $method, @args] } if $method eq 'anything';
    return undef;
  }

  package main;

  my $example = Example->new(test => time);

  # $example->anything(1..4);

=cut

=head1 DESCRIPTION

This package provides a hook into method dispatch resoluton via a wrapper
around the C<AUTOLOAD> routine which processes calls to routines which don't
exist.

=cut

=head1 METHODS

This package provides the following methods:

=cut

=head2 build_proxy

  build_proxy(Str $package, Str $method, Any @args) (CodeRef | Undef)

The build_proxy method should return a code reference to fulfill the method
dispatching request, or undef to result in a method not found error.

I<Since C<0.01>>

=over 4

=item build_proxy example 1

  package main;

  my $example = Example->new(test => 123);

  my $build_proxy = $example->build_proxy('Example', 'everything', 1..4);

  # undef

=back

=over 4

=item build_proxy example 2

  package main;

  my $example = Example->new(test => 123);

  my $build_proxy = $example->build_proxy('Example', 'anything', 1..4);

  # sub { ... }

=back

=cut