Config-Processor

 view release on metacpan or  search on metacpan

lib/Config/Processor.pm  view on Meta::CPAN

  open( my $fh, '<', $file_path ) || die "Can't open $file_path: $!";
  my @data = ( decode_json( join( '', <$fh> ) ) );
  close($fh);

  return @data;
}

sub _process_tree {
  my $self = shift;
  my $ancs = pop;

  return if readonly( $_[0] );

  $_[0] = $self->_process_node( $_[0], $ancs );

  if ( my $node_addr = refaddr( $_[0] ) ) {
    return if $self->{_seen_nodes}{$node_addr};

    $self->{_seen_nodes}{$node_addr} = 1;
  }

  if ( ref( $_[0] ) eq 'HASH' ) {
    foreach ( values %{ $_[0] } ) {
      $self->_process_tree( $_, [ $_[0], @{$ancs} ] );
    }
  }
  elsif ( ref( $_[0] ) eq 'ARRAY' ) {
    foreach ( @{ $_[0] } ) {
      $self->_process_tree( $_, [ $_[0], @{$ancs} ] );
    }
  }

  return;
}

sub _process_node {
  my $self = shift;
  my $node = shift;
  my $ancs = shift;

  return unless defined $node;

  if ( !ref($node) && $self->{interpolate_variables} ) {
    $node =~ s/\$((\$?)\{([^\}]*)\})/
        $2 ? $1 : ( $self->_resolve_var( $3, [ @{$ancs} ] ) || '' )/ge;
  }
  elsif ( ref($node) eq 'HASH' && $self->{process_directives} ) {
    if ( defined $node->{var} ) {
      $node = $self->_resolve_var( $node->{var}, [ @{$ancs} ] );
    }
    elsif ( defined $node->{include} ) {
      $node = $self->_build_tree( $node->{include} );
    }
    else {
      if ( defined $node->{underlay} ) {
        my $layer = delete $node->{underlay};
        $layer = $self->_process_layer( $layer, $ancs );
        $node = $self->{_merger}->merge( $layer, $node );
      }

      if ( defined $node->{overlay} ) {
        my $layer = delete $node->{overlay};
        $layer = $self->_process_layer( $layer, $ancs );
        $node = $self->{_merger}->merge( $node, $layer );
      }
    }
  }

  return $node;
}

sub _process_layer {
  my $self  = shift;
  my $layer = shift;
  my $ancs  = shift;

  if ( ref($layer) eq 'HASH' ) {
    $layer = $self->_process_node( $layer, $ancs );
  }
  elsif ( ref($layer) eq 'ARRAY' ) {
    my $new_layer = {};

    foreach my $node ( @{$layer} ) {
      $node = $self->_process_node( $node, $ancs );
      $new_layer = $self->{_merger}->merge( $new_layer, $node );
    }

    $layer = $new_layer;
  }

  return $layer;
}

sub _resolve_var {
  my $self = shift;
  my $name = shift;
  my $ancs = shift;

  my $value;

  if ( $name =~ m/^\./ ) {
    my $node;
    my @tokens = split( /\./, $name, -1 );

    while (1) {
      my $token = $tokens[0];
      $token =~ s/^\s+//;
      $token =~ s/\s+$//;

      last if length($token) > 0;

      shift @tokens;

      last unless @tokens;
      next unless @{$ancs};

      $node = shift @{$ancs};
    }

    $value = eval {
      $self->_fetch_value( $node, $ancs, \@tokens );
    };

lib/Config/Processor.pm  view on Meta::CPAN

    metrics: { include: metrics/* }

=item underlay

Merges specified configuration parameters with parameters located at the same
context. Configuration parameters from the context overrides parameters from
the directive. C<underlay> directive most usefull in combination with C<var>
and C<include> directives.

For example, you can use this directive to set default values of parameters.

  myapp:
    db:
      connectors:
        default:
          port:   "1234"
          dbname: "stat"
          options:
            PrintWarn:  0
            PrintError: 0
            RaiseError: 1

        stat_master:
          underlay: { var: .default }
          host:     "stat-master.mydb.com"
          username: "stat_writer"
          password: "stat_writer_pass"

        stat_slave:
          underlay: { var: .default }
          host:     "stat-slave.mydb.com"
          username: "stat_reader"
          password: "stat_reader_pass"

You can move default parameters in separate files.

  myapp:
    db:
      connectors:
        underlay:
          - { include: db_connectors/default.yml }
          - { include: db_connectors/default_test.yml }

        stat_master:
          underlay: { var: .default }
          host:     "stat-master.mydb.com"
          username: "stat_writer"
          password: "stat_writer_pass"

        stat_slave:
          underlay: { var: .default }
          host:     "stat-slave.mydb.com"
          username: "stat_reader"
          password: "stat_reader_pass"

        test:
          underlay: { var: .default_test }
          username: "test"
          password: "test_pass"

=item overlay

Merges specified configuration parameters with parameters located at the same
context. Configuration parameters from the directive overrides parameters from
the context. C<overlay> directive most usefull in combination with C<var> and
C<include> directives.

For example, you can use C<overlay> directive to temporaly overriding regular
configuration parameters.

  myapp:
    db:
      connectors:
        default:
          port:   "1234"
          dbname: "stat"
          options:
            PrintWarn:  0
            PrintError: 0
            RaiseError: 1

        test:
          host: "localhost"
          port: "4321"

        stat_master:
          underlay: { var: .default }
          host:     "stat-master.mydb.com"
          username: "stat_writer"
          password: "stat_writer_pass"
          overlay:  { var: .test }

        stat_slave:
          underlay: { var: .default }
          host:     "stat-slave.mydb.com"
          username: "stat_reader"
          password: "stat_reader_pass"
          overlay:  { var: .test }

To disable overriding just assign to C<test> connector empty hash.

  test: {}

=back

=head1 AUTHOR

Eugene Ponizovsky, E<lt>ponizovsky@gmail.comE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2016-2018, Eugene Ponizovsky, E<lt>ponizovsky@gmail.comE<gt>.
All rights reserved.

This module is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.

=cut



( run in 1.425 second using v1.01-cache-2.11-cpan-df04353d9ac )