Articulate

 view release on metacpan or  search on metacpan

lib/Articulate/Syntax.pm  view on Meta::CPAN

    use Moo;
    has delegates_to =>
      is      => 'rw',
      default => sub { [] },
      coerce  => sub{ instantiate_array(@_) };

Which means given config like the following:

    Articulate::SomeDelegatingComponent:
      delegates_to:
        - My::Validation::For::Articles
        - class: My::Validation::For::Images
          args:
            - max_width: 1024
              max_height: 768
        - class: My::Validation::For::Documents
          constructor: preset
          args: pdf

You can be guaranteed that looping through C<< @{ $self->delegates_to }
>> will always produce objects.

=head3 instantiate

Attempts to create an object from the hashref or class name provided.

If the value is a string, it will treat as a class name, and perform
C<< $class->new >>, or, if the method exists, C<< $class->instance >>
will be preferred (for instance, as provided by C<MooX::Singleton>).

If the value is a hashref, it will look at the values for the keys
C<class>, C<constructor>, and C<args>. It will then attempt to perform
C<< $class->$constructor(@$args) >>, unless the constructor is absent
(in which case C<instance> or C<new> will be supplied), or if C<args>
is not an arrayref, in which case it will be passed to the constructor
as a single argument (or the empty list will be passed if C<args> is
undefined).

If the value is an object, the object will simply be returned.

=cut

sub instantiate {
  my $original = shift;
  if ( blessed $original ) {
    return $original;
  }
  elsif ( !ref $original ) {
    Module::Load::load($original);
    if ( $original->can('instance') ) {
      return $original->instance();
    }
    else {
      return $original->new();
    }
  }
  elsif ( ref $original eq ref {} ) {
    my $class = $original->{class};
    my $args  = $original->{args};
    if ( 1 == keys %$original and join( '', keys %$original ) !~ /^[a-z_]/ )
    { # single key that looks like a class
      $class = join '', keys %$original;
      $args = $original->{$class};
    }
    throw_error Internal => 'Instantiation failed: expecting key class, got '
      . ( join ', ', keys %$original )
      unless defined $class;
    Module::Load::load($class);
    my $constructor = $original->{constructor}
      // ( $class->can('instance') ? 'instance' : 'new' );
    my @args = (
        ( defined $args )
      ? ( ref $args eq ref [] )
          ? @$args
          : $args
      : ()
    );
    return $class->$constructor(@args);
  }
}

sub instantiate_array {
  my $arrayref = shift;
  return [] unless defined $arrayref;

# delegates_to => "Class::Name" should be interpreted as delegates_to => ["Class::Name"]
  $arrayref = [$arrayref] unless ref $arrayref and ref $arrayref eq ref [];
  return [ map { instantiate $_ } @$arrayref ];
}

=head3 from_meta

  sub email_address { from_meta (shift, 'schema/user/email_address'); }

This method uses Data::DPath to retrieve a field from the metadata
structure.

=cut

sub from_meta {
  my $structure = shift;
  my $item      = shift;
  my @results   = dpath( $item->meta )->match($structure);
  return shift @results;
}

=head3 dpath_get

  my $value = dpath_get($structure, '/path/in/structure');

=cut

sub dpath_get {
  my $structure = shift;
  my $path      = shift;
  my @results   = dpath($path)->match($structure);
  return shift @results;
}

=head3 dpath_set



( run in 1.591 second using v1.01-cache-2.11-cpan-97f6503c9c8 )