package Data::Object::Role::Buildable;

use 5.014;

use Moo::Role;

sub BUILD {

  return $_[0];
}

around BUILD => sub {
  my ($orig, $self, $args) = @_;

  if ($self->can('build_self')) {
    $self->build_self($args);
  }

  return $self;
};

around BUILDARGS => sub {
  my ($orig, $class, @args) = @_;

  # build_arg accepts a single-arg (non-hash)
  my $inflate = @args == 1 && ref $args[0] ne 'HASH';

  # single argument
  if ($class->can('build_arg') && $inflate) {
    @args = ($class->build_arg($args[0]));
  }

  # build_args should not accept a single-arg (non-hash)
  my $ignore = @args == 1 && ref $args[0] ne 'HASH';

  # standard arguments
  if ($class->can('build_args') && !$ignore) {
    @args = ($class->build_args(@args == 1 ? $args[0] : {@args}));
  }

  return $class->$orig(@args);
};

1;

=encoding utf8

=head1 NAME

Data::Object::Role::Buildable

=cut

=head1 ABSTRACT

Buildable Role for Perl 5

=cut

=head1 SYNOPSIS

  package Vehicle;

  use Moo;

  with 'Data::Object::Role::Buildable';

  has name => (
    is => 'rw'
  );

  1;

=cut

=head1 DESCRIPTION

This package provides methods for hooking into object construction of the
consuming class, e.g. handling single-arg object construction.

=cut

=head1 SCENARIOS

This package supports the following scenarios:

=cut

=head2 buildarg

  package Car;

  use Moo;

  extends 'Vehicle';

  sub build_arg {
    my ($class, $name) = @_;

    # do something with $name or $class ...

    return { name => $name };
  }

  package main;

  my $car = Car->new('tesla');

This package supports handling a C<build_arg> method, as a hook into object
construction, which is called and passed a single argument if a single argument
is passed to the constructor.

=cut

=head2 buildargs

  package Sedan;

  use Moo;

  extends 'Car';

  sub build_args {
    my ($class, $args) = @_;

    # do something with $args or $class ...

    $args->{name} = ucfirst $args->{name};

    return $args;
  }

  package main;

  my $sedan = Sedan->new('tesla');

This package supports handling a C<build_args> method, as a hook into object
construction, which is called and passed a C<hashref> during object
construction.

=cut

=head2 buildself

  package Taxicab;

  use Moo;

  extends 'Sedan';

  sub build_self {
    my ($self, $args) = @_;

    # do something with $self or $args ...

    $args->{name} = 'Toyota';

    return;
  }

  package main;

  my $taxicab = Taxicab->new('tesla');

This package supports handling a C<build_self> method, as a hook into object
construction, which is called and passed a C<hashref> during object
construction. Note: Manipulating the arguments doesn't effect object's
construction or properties.

=cut

=head1 AUTHOR

Al Newkirk, C<awncorp@cpan.org>

=head1 LICENSE

Copyright (C) 2011-2019, Al Newkirk, et al.

This is free software; you can redistribute it and/or modify it under the terms
of the The Apache License, Version 2.0, as elucidated in the L<"license
file"|https://github.com/iamalnewkirk/data-object-role-buildable/blob/master/LICENSE>.

=head1 PROJECT

L<Wiki|https://github.com/iamalnewkirk/data-object-role-buildable/wiki>

L<Project|https://github.com/iamalnewkirk/data-object-role-buildable>

L<Initiatives|https://github.com/iamalnewkirk/data-object-role-buildable/projects>

L<Milestones|https://github.com/iamalnewkirk/data-object-role-buildable/milestones>

L<Contributing|https://github.com/iamalnewkirk/data-object-role-buildable/blob/master/CONTRIBUTE.md>

L<Issues|https://github.com/iamalnewkirk/data-object-role-buildable/issues>

=cut
