Developer-Dashboard

 view release on metacpan or  search on metacpan

lib/Developer/Dashboard/EnvLoader.pm  view on Meta::CPAN

package Developer::Dashboard::EnvLoader;

use strict;
use warnings;

our $VERSION = '4.16';

use Cwd qw(abs_path cwd);
use File::Basename qw(dirname);
use File::Spec;

use Developer::Dashboard::EnvAudit;

# load_runtime_layers(%args)
# Loads every participating plain-directory and DD-OOP-LAYER runtime env file
# from the configured root toward the current working directory.
# Input: hash with paths => Developer::Dashboard::PathRegistry object.
# Output: ordered array reference of loaded env file paths.
sub load_runtime_layers {
    my ( $class, %args ) = @_;
    my $paths = $args{paths} or die "Missing paths\n";
    return $class->load_files(
        files => [
            $class->_plain_directory_env_files($paths),
            $class->_runtime_layer_env_files($paths),
        ],
    );
}

# load_skill_layers(%args)
# Loads every participating skill-root env file from home skill layer to the
# deepest effective skill layer.
# Input: hash with skill_layers => array reference of skill root paths.
# Output: ordered array reference of loaded env file paths.
sub load_skill_layers {
    my ( $class, %args ) = @_;
    return $class->_load_skill_layer_specs(
        specs => $class->_skill_layer_specs( @{ $args{skill_layers} || [] } ),
    );
}

# load_skill_layers_into_hash(%args)
# Loads the ordered nested skill env chain into an isolated temporary
# environment and returns only the added or changed keys.
# Input: hash with skill_layers => array reference of skill root paths and
# optional base_env => hash reference of starting environment values.
# Output: hash reference with loaded file list and env overlay hash.
sub load_skill_layers_into_hash {
    my ( $class, %args ) = @_;
    my $base_env = ref( $args{base_env} ) eq 'HASH' ? { %{ $args{base_env} } } : { %ENV };
    my %before = %{$base_env};

    local %ENV = %{$base_env};
    local $ENV{DEVELOPER_DASHBOARD_ENV_AUDIT};
    local %Developer::Dashboard::EnvAudit::AUDIT;

    my $loaded = $class->_load_skill_layer_specs(
        specs => $class->_skill_layer_specs( @{ $args{skill_layers} || [] } ),
    );
    my %overlay;
    for my $key ( sort keys %ENV ) {
        next
          if exists $before{$key}
          && (
               ( !defined $before{$key} && !defined $ENV{$key} )
            || ( defined $before{$key} && defined $ENV{$key} && $before{$key} eq $ENV{$key} )
          );
        $overlay{$key} = $ENV{$key};
    }

    return {
        files => $loaded,
        env   => \%overlay,
    };
}

# load_files(%args)
# Loads a specific ordered list of .env and .env.pl files, updating both %ENV
# and the shared EnvAudit inventory.
# Input: hash with files => array reference of candidate file paths.
# Output: ordered array reference of the env files that were actually loaded.
sub load_files {
    my ( $class, %args ) = @_;
    my @files = @{ $args{files} || [] };
    my @loaded;
    my %seen;
    for my $file (@files) {
        next if !defined $file || $file eq '';
        my $identity = $class->_path_identity($file);
        next if $seen{$identity}++;
        next if !-f $file;
        if ( $file =~ /\.env\.pl\z/ ) {
            $class->_load_env_pl_file($file);
            push @loaded, $file;
            next;
        }
        $class->_load_env_file($file);
        push @loaded, $file;
    }
    return \@loaded;
}

# load_files_into_hash(%args)
# Loads a specific ordered list of env files into an isolated temporary
# environment and returns only the added or changed keys.
# Input: hash with files => array reference of candidate file paths and
# optional base_env => hash reference of starting environment values.
# Output: hash reference with loaded file list and env overlay hash.
sub load_files_into_hash {
    my ( $class, %args ) = @_;
    my $base_env = ref( $args{base_env} ) eq 'HASH' ? { %{ $args{base_env} } } : { %ENV };
    my %before = %{$base_env};

    local %ENV = %{$base_env};
    local $ENV{DEVELOPER_DASHBOARD_ENV_AUDIT};
    local %Developer::Dashboard::EnvAudit::AUDIT;

    my $loaded = $class->load_files( files => $args{files} );
    my %overlay;
    for my $key ( sort keys %ENV ) {
        next
          if exists $before{$key}
          && (
               ( !defined $before{$key} && !defined $ENV{$key} )
            || ( defined $before{$key} && defined $ENV{$key} && $before{$key} eq $ENV{$key} )
          );
        $overlay{$key} = $ENV{$key};
    }

    return {
        files => $loaded,
        env   => \%overlay,
    };
}

# _plain_directory_env_files($paths)
# Builds the env file list contributed by ancestor directories from the active
# root toward the current working directory.
# Input: path registry object.
# Output: ordered list of plain directory env file paths.
sub _plain_directory_env_files {
    my ( $class, $paths ) = @_;
    my @files;
    for my $dir ( $class->_plain_directory_layers($paths) ) {
        push @files, $class->_env_file_candidates($dir);
    }
    return @files;
}

# _runtime_layer_env_files($paths)
# Builds the env file list contributed by participating DD-OOP-LAYER runtime
# roots from home runtime to deepest child runtime.
# Input: path registry object.
# Output: ordered list of runtime env file paths.
sub _runtime_layer_env_files {
    my ( $class, $paths ) = @_;
    my @files;
    for my $runtime_root ( $paths->runtime_layers ) {
        push @files, $class->_env_file_candidates($runtime_root);
    }
    return @files;
}

# _plain_directory_layers($paths)
# Resolves the ancestor directory chain whose plain .env files participate in
# env loading for the current working directory.
# Input: path registry object.
# Output: ordered list of directory paths from root to cwd.
sub _plain_directory_layers {
    my ( $class, $paths ) = @_;
    my $cwd = $paths->current_working_directory;
    return () if !defined $cwd || $cwd eq '';
    my $home = $paths->home;
    my $project_root = eval { $paths->current_project_root } || '';
    my $stop_dir = '';
    if ( $class->_same_or_descendant_path( $cwd, $home ) ) {
        $stop_dir = $home;
    }
    elsif ( $project_root ne '' && $class->_same_or_descendant_path( $cwd, $project_root ) ) {
        $stop_dir = $project_root;
    }
    else {
        return ();
    }

    my @layers;
    my $dir = $cwd;
    while ($dir) {
        push @layers, $dir;
        last if $class->_path_identity($dir) eq $class->_path_identity($stop_dir);
        my $parent = dirname($dir);
        last if !defined $parent || $parent eq '' || $parent eq $dir;



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