Pickles

 view release on metacpan or  search on metacpan

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

use Plack::Util::Accessor qw(appname home);
use Pickles::Util qw(env_value);

sub new {
    my $class = shift;
    my %args = @_;
    my $self = bless {}, $class;
    $self->{appname} = Pickles::Util::appname( $class );
    $self->setup_home( $args{home} );
    $self->{ACTION_PREFIX} = '';
    $self->load_files( $args{files} || [] );
    $self;
}

sub construct {
    my $class = shift;
    my $self = $class->new;
    my $files = $self->get_config_files;
    $self->load_files( $files );
    $self;
}

sub get {
    my( $self, $key, $default ) = @_;
    return defined $self->{$key} ? $self->{$key} : $default;
}

sub setup_home {
    my( $self, $home ) = @_;
    my $dir = 
        $home || env_value( 'HOME', $self->appname ) || $ENV{'PICKLES_HOME'};
    if ( $dir ) {
        $self->{home} = dir( $dir );
    }
    else {
        my $class = ref $self;
        (my $file = "$class.pm") =~ s|::|/|g;
        if (my $inc_path = $INC{$file}) {
            (my $path = $inc_path) =~ s/$file$//;
            my $home = dir($path)->absolute->cleanup;
            $home = $home->parent while $home =~ /b?lib$/;
            $self->{home} = $home;
        }
    }
}

sub load_files {
    my( $self, $files ) = @_;
    my %config;

    # In 5.8.8 at least, putting $self in an evaled code produces
    # extra warnings (and possibly break the behavior of __path_to)
    # so we create a private closure, and plant the closure into
    # the generated packes
    $self->{__FILES} = [];

    my $path_to = sub { $self->path_to(@_) };
    my $load_file = sub {
        my $file = $path_to->( @_ );
        delete $INC{$file};
        my $subconf = require $file;
        # death context should be at the calling config file level
        Carp::croak("Could not parse $file: $@") if $@;
        Carp::croak("Could not do $file: $!")    if ! defined $subconf;
        Carp::croak("Could not run $file")       if ! $subconf;

        push @{$self->{__FILES}}, $file;
        return $subconf;
    };

    for my $file( @{$files} ) {
        # only do this if the file exists
        next unless -e $file;

        my $pkg = $file;
        $pkg =~ s/([^A-Za-z0-9_])/sprintf("_%2x", unpack("C", $1))/eg;

        my $fqname = sprintf '%s::%s', ref $self, $pkg;
        { # XXX This is where we plant that closure
            no strict 'refs';
            no warnings 'redefine';
            *{"$fqname\::__path_to"} = $path_to;
            *{"$fqname\::load_file"} = $load_file;
        }

        my $config_pkg = sprintf <<'SANDBOX', $fqname;
package %s;
{
    my $conf = require $file or die $!;
    $conf;
}
SANDBOX
        delete $INC{$file};
        my $conf = eval $config_pkg || +{};
        if ($@) {
            warn "Error while trying to read config file $file: $@";
        }
        %config = (
            %config,
            %{$conf},
        );
    }
    push @{$self->{__FILES}}, @$files;
    $self->{__TIME} = time;
    for my $key( keys %config ) {
        $self->{$key} = $config{$key};
    }
    \%config;
}

sub get_config_files {
    my $self = shift;
    my @files;

    if ( my $config_file = env_value('CONFIG', $self->appname) ) {
        push @files, $self->path_to( $config_file );
    }
    else {
        my @base_files = ( File::Spec->catfile('etc', 'config.pl'), 'config.pl' );
        foreach my $f (@base_files) {
            my $base = $self->path_to($f);
            push @files, $base if -e $base;
        }
    }
    
    if ( my $env = env_value('ENV', $self->appname) ) {
        my @env_files;
        for my $file( @files ) {
            my ($v, $d, $fname) = File::Spec->splitpath( $file );
            $fname =~ s/(\.[^\.]+)?$/$1 ? "_%s$1" : "%s"/e;
            my $template = File::Spec->catpath( $v, $d, $fname );
            my $filename = sprintf $template, $env;
            push @env_files, $self->path_to( $filename );

        }
        push @files, @env_files;
    }
    return \@files;
}

sub path_to {
    my( $self, @path ) = @_;
    if ( File::Spec->file_name_is_absolute( $path[0] ) ) {
        return File::Spec->catfile( @path );
    }
    file( $self->home, @path )->stringify;
}

1;



( run in 1.660 second using v1.01-cache-2.11-cpan-39bf76dae61 )