Config-Std

 view release on metacpan or  search on metacpan

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

package Config::Std;

our $VERSION = '0.903';

use 5.007_003; # Testing with 5.8.1 since that's cpanm minimum :-)
use strict;
use warnings;

my %global_def_sep;
my %global_inter_gap;

sub import {
    my ($package, $opt_ref) = @_;
    my $caller = caller();
    $global_def_sep{$caller} = $opt_ref->{def_sep};
    $global_inter_gap{$caller} = $opt_ref->{def_gap};
    for my $sub_name (qw( read_config write_config )) {
        $opt_ref->{$sub_name} ||= $sub_name;
    }
    no strict "refs";
    *{$caller.'::'.$opt_ref->{read_config}}  = \&Config::Std::Hash::read_config;
    *{$caller.'::'.$opt_ref->{write_config}} = \&Config::Std::Hash::write_config;
}

package Config::Std::Gap;
use Class::Std;
{
    sub serialize { return "\n" }
    sub update  {}
    sub extend  {}
    sub copy_to {}
}

package Config::Std::Comment;
use Class::Std;
{
    my %text_of : ATTR( :init_arg<text> );

    sub serialize {
        my ($self) = @_;
        return $text_of{ident $self};
    }

    sub append_comment {
        my ($self, $new_text) = @_;
        $text_of{ident $self} .= $new_text;
    }

    sub update  {}
    sub extend  {}
    sub copy_to {}
}

package Config::Std::Keyval;
use Class::Std;
{
    my %key_of      : ATTR( :init_arg<key> :get<key> );
    my %vals_of     : ATTR;
    my %deleted_of  : ATTR;

    sub BUILD {
        my ($self, $ident, $arg_ref) = @_;

        $vals_of{$ident}     = [ { %{$arg_ref} } ];
    }

    my %SEPARATOR = ( ':' => ': ', '=' => ' = ' );

    use Carp;

    sub serialize {
        my ($self, $def_sep, $block_name) = @_;
        my $ident = ident $self;

        return "" if $deleted_of{$ident};

        my ($key, $vals) = ($key_of{$ident}, $vals_of{$ident});

        my $keyspace = q{ } x length($key);

        my $serialization = q{};

        for my $n (0..$#{$vals}) {
            my ($val,$sep,$comm) = @{$vals->[$n]}{qw(val sep comm)};

            my $val_type = ref $val;
            croak qq{Can't save \L$val_type\E ref as value for key {'$block_name'}{'$key'} (only scalars or array refs)}
                if $val_type && $val_type ne 'ARRAY';

            $sep = $SEPARATOR{$sep || $def_sep};

            my @vals = $val_type eq 'ARRAY' ? @{$val} : $val;
            s/ (?!\Z) \n /\n$keyspace$sep/gxms for @vals;

            $serialization .= $comm || q{};

            $serialization .= join q{}, map {"$key$sep$_\n"} @vals;
        }

        return $serialization;
    }

    sub update {
        my ($self, $hash_ref, $updated_ref) = @_;
        my $ident = ident $self;

        my $key = $key_of{$ident};

        if (!exists $hash_ref->{$key}) {
            $deleted_of{$ident} = 1;
        }
        else {
            my $val = $hash_ref->{$key};
            my @newvals = ref $val eq 'ARRAY' ? @{$val} : $val;
            for my $n (0..$#newvals) {
                $vals_of{$ident}[$n]{val} = $newvals[$n];
            }
            splice @{$vals_of{$ident}}, scalar @newvals;
        }

        $updated_ref->{$key} = 1;

        return 1;
    }

    sub copy_to {
        my ($self, $hash_ref)  = @_;
        my $ident = ident $self;
        my @vals = map $_->{val}, @{$vals_of{$ident}};
        $hash_ref->{$key_of{$ident}} = @vals > 1 ? \@vals : $vals[0];
    }

    sub multivalue {
        my ($self, $sep, $val, $comm) = @_;
        push @{$vals_of{ident $self}}, {val=>$val, sep=>$sep, comm=>$comm};
    }
}

package Config::Std::Block;
use Class::Std;
{
    my %name_of         : ATTR( :init_arg<name> :get<name> default => '' );
    my %sep_count_of    : ATTR;
    my %precomm_of      : ATTR( :init_arg<precomm> default => '' );
    my %parcomm_of      : ATTR( :init_arg<parcomm> default => '' );
    my %components_of   : ATTR;
    my %deleted_of      : ATTR;
    my %seen            : ATTR;
    my %is_first        : ATTR( :init_arg<first> default => '' );

    sub BUILD {
        my ($self, $ident) = @_;
        @{$sep_count_of{$ident}}{':','='} = (0,0);
        $components_of{$ident} = [];
        $seen{$ident} = {};
    }

    sub copy_to {
        my ($self, $hash_ref) = @_;
        my $ident = ident $self;

        my $keyvals = $hash_ref->{$name_of{$ident}} ||= {};

        for my $comp ( @{$components_of{$ident}} ) {
            $comp->copy_to($keyvals);
        }

        $hash_ref->{$name_of{$ident}} = $keyvals;
    }

    sub serialize {
        my ($self, $first, $caller, $post_gap, $inter_gap) = @_;
        my $ident = ident $self;

        return q{} if $deleted_of{$ident};

        my $is_anon = $first && length($name_of{$ident}) == 0;

        my $serialization = q{};
        if (!$is_anon) {
            $serialization = ($precomm_of{$ident} || q{})
                           . "[$name_of{$ident}]"
                           . (defined $parcomm_of{$ident}?$parcomm_of{$ident}:q{})
                           . "\n";
        }

        my $gds = $global_def_sep{$caller};
        my $def_sep
            = defined $gds                                             ? $gds
            : $sep_count_of{$ident}{':'} >= $sep_count_of{$ident}{'='} ? ':'
            :                                                            '='
            ;

        $self->ensure_gap() if $inter_gap && !$is_anon;

        for my $comp ( @{$components_of{$ident}} ) {
            $serialization .= $comp->serialize($def_sep, $name_of{$ident});
        }

        return $serialization;
    }

    sub update {
        my ($self, $hash_ref, $updated_ref) = @_;
        my $ident = ident $self;

        if (!defined $hash_ref) {
            $deleted_of{$ident} = 1;
            return;
        }

        for my $comp ( @{$components_of{$ident}} ) {
            $comp->update($hash_ref, $updated_ref) or next;
        }
    }

    sub extend {
        my ($self, $hash_ref, $updated_ref, $post_gap, $inter_gap) = @_;

        # Only the first occurrence of a block has new keys added...
        return unless $is_first{ident $self};

        my $first = 1;
	# RT 85956
        for my $key ( sort grep {!$updated_ref->{$_}} keys %{$hash_ref}) {
            my $value = $hash_ref->{$key};
            my $separate = ref $value || $value =~ m/\n./xms;
            $self->ensure_gap() if ($first ? $post_gap : $inter_gap)
                                    || $separate;
            $self->add_keyval($key, undef, $hash_ref->{$key});
            $self->add_gap() if $separate;
            $first = 0;
        }
    }

    sub ensure_gap {
        my ($self) = @_;
        my $comp_ref = $components_of{ident $self};
        return if @{$comp_ref} && $comp_ref->[-1]->isa('Config::Std::Gap');
        push @{$comp_ref}, Config::Std::Gap->new();
    }

    sub add_gap {
        my ($self) = @_;
        push @{$components_of{ident $self}}, Config::Std::Gap->new();
    }

    sub add_comment {
        my ($self, $text) = @_;
        my $comp_ref = $components_of{ident $self};
        if ($comp_ref && @{$comp_ref} && $comp_ref->[-1]->isa('Config::Std::Comment') ) {
            $comp_ref->[-1]->append_comment($text);
        }
        else {
            push @{$comp_ref}, Config::Std::Comment->new({text=>$text});
        }
    }

    sub add_keyval {
        my ($self, $key, $sep, $val, $comm) = @_;
        my $ident = ident $self;

        $sep_count_of{$ident}{$sep}++ if $sep;



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