Data-Show

 view release on metacpan or  search on metacpan

lib/Data/Show.pm  view on Meta::CPAN

}


# Useful regexes...
my $OWS;         BEGIN { $OWS         = qr{ (?: \s++ | \# [^\n]*+ )*+ }x;               }
my $IDENT;       BEGIN { $IDENT       = qr{ [^\W\d]\w* (?: :: [^\W\d]\w* )* | [_\W] }x; }
my $COLOUR_CHAR; BEGIN { $COLOUR_CHAR = qr{ (?: \e[^m]*m )*  [^\n]  (?: \e[^m]*m )* }x; }
my $VALID_ARG;   BEGIN { $VALID_ARG   = qr{ \A (?: to        | with      | fallback
                                                 | base      | warnings  | as
                                                 | style     | grid      | termwidth
                                                 | datastyle | filestyle | linestyle
                                                 | codestyle | showstyle | gridstyle
                                               ) \z }x; }

# Track lexically scoped output targets and styles...
my @OUTPUT_FH;
my @STYLE;

# Export the module's API, or that of a plugin (as requested)...
sub import {
    # Track load context...
    my ($package, $file, $line) = _get_context();

    # Remove the module name from the argument list...
    shift @_;

    # Handle the special case of a 'base' argument (by adding it as the caller's base class)...
    if (@_ > 0 && $_[0] eq 'base') {
        die "If 'base' is specified, it must be the only argument at $file line $line\n" if @_ > 2;
        no strict 'refs';
        push @{caller().'::ISA'}, _load_plugin( $_[1] // 'Data::Show::Plugin', $file, $line, 'warn' );
        return;
    }

    # Check for missing named args and improve the usual warning for that problem...
    die "No value specified for named argument '$_[-1]' at $file line $line\n"
        if @_ % 2 != 0;

    # Unpack args (including defaults from config file)....
    state $defaults_ref = _load_defaults($file, $line);
    my %opt = (%{$defaults_ref}, @_);

    # Punish invalid arguments...
    _validate_args(\%opt, "at $file line $line", "named argument");

    # Any 'to' arg must be a filehandle, filename, or scalar ref (and open it if necessary)...
    $opt{to} = _open_target( $opt{to} // $DEFAULT_TARGET, $file, $line, $opt{warnings} ne 'off' );

    # Unpack fallback arguments into an arrayref...
    $opt{fallback} = [ split m{ \s*,\s* }x, $opt{fallback} ];

    # Resolve style options according to terminal background (i.e. dark or light)
    for my $option (@opt{ grep /\A.+style\z/, keys %opt}) {
        $option = [split /\s*,\s*/, $option]->[$IS_LIGHT_BG ? -1 : 0];
    }

    # Install Data::Show::Plugin base class as well...
    $INC{'Data/Show/Plugin.pm'} = $INC{'Data/Show.pm'};

    # Track lexical options...
    $^H{'Data::Show/with'}       = _load_plugin( $opt{with}, $file, $line,
                                                 $opt{warnings} ne 'off', $opt{fallback} );
    $^H{'Data::Show/termwidth'}  = $opt{termwidth};
    $^H{'Data::Show/to'}         = @OUTPUT_FH;
    $^H{'Data::Show/style'}      = @STYLE;
    my $existing_as              = $^H{'Data::Show/as'} // '(?!)';
    $^H{'Data::Show/as'}         = "$existing_as|$opt{as}";
    push @OUTPUT_FH, $opt{to};
    push @STYLE,     { add_grid => $opt{grid},
                       mode     => $opt{style},
                       map { m/(.+)style/ ? ($1 => $opt{$_}) : () } keys %opt
                     };

    # Install the function...
    no strict 'refs';
    *{caller() . '::' . $opt{as}} = \&show;
}

# A "no Data::Show" turns show() into a no-op...
sub unimport {
    # Track disabling lexically...
    $^H{'Data::Show/noshow'} = 1;

    # Install the function...
    no strict 'refs';
    *{caller() . '::show'} = \&show;
}

sub _validate_args {
    my ($opt_ref, $where, $what) = @_;

    # Collect and report non-valid arguments...
    my @unknown_args = grep { !m{$VALID_ARG} } keys %{$opt_ref};
    die "Unknown $what" . (@unknown_args == 1 ? '' : 's') . " $where:\n",
        join q{}, map { "    $_\n" } @unknown_args
            if @unknown_args;

    # By the time we're validating, we shouldn't see a 'base' option...
    return if !exists $opt_ref->{base};
    die $what eq 'named argument' ? "If 'base' is specified, it must be the only argument $where\n"
                                  : "Can't specify 'base' as a $what $where\n"
}

# Ensure output filehandles are valid (or fall back to the default)...
sub _open_target {
    my ($target, $file, $line, $warnings) = @_;

    # Track already opened targets, and reuse them...
    state %already_open;
    return $already_open{$target} if $already_open{$target};

    # Handle stringy filenames and in-memory targets...
    my $to_type = ref($target);
    if (!$to_type && ref(\$target) ne 'GLOB' || $to_type eq 'SCALAR') {
        if (open my $fh, '>', $target) {
            return ($already_open{$target} = $fh);
        }
        else {
            warn "Could not open named 'to' argument for output at $file line $line\n"
                if $warnings;
            return ($already_open{$target} = $DEFAULT_TARGET);
        }
    }

    # Handle filehandle-y targets...
    elsif (_is_writeable($target)) {
        return ($already_open{$target} = $target);



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