package Test::WriteVariants::Context;
use strict;
my $ContextClass = __PACKAGE__;
# a Context is an ordered list of various kinds of named values (such as env vars, our vars)
# possibly including other Context objects.
#
# Values can be looked up by name. The first match will be returned.
sub new {
my $class = shift;
$class = ref $class if ref $class;
return bless [ @_ ], $class;
}
sub new_composite { shift->new(@_) } # see Test::WriteVariants::Context::BaseItem
sub push_var { # add a var to an existing config
my ($self, $var) = @_;
push @$self, $var;
return;
}
sub _new_var {
my ($self, $t, $n, $v, %e) = @_;
my $var = $t->new($n, $v, %e);
return $self->new( $var ); # wrap var item in a context list
}
sub new_env_var { shift->_new_var($ContextClass.'::EnvVar', @_) }
sub new_our_var { shift->_new_var($ContextClass.'::OurVar', @_) }
sub new_module_use { shift->_new_var($ContextClass.'::ModuleUse', @_) }
sub new_meta_info { shift->_new_var($ContextClass.'::MetaInfo', @_) }
# XXX should ensure that a given type+name is only output once (the latest one)
sub get_code {
my $self = shift;
my @code;
for my $setting (reverse @$self) {
push @code, (ref $setting) ? $setting->get_code : $setting;
}
return join "", @code;
}
sub get_var { # search backwards through list of settings, stop at first match
my ($self, $name, $type) = @_;
for my $setting (reverse @$self) {
next unless $setting;
my @value = $setting->get_var($name, $type);
return $value[0] if @value;
}
return;
}
sub get_env_var { my ($self, $name) = @_; return $self->get_var($name, $ContextClass.'::EnvVar') }
sub get_our_var { my ($self, $name) = @_; return $self->get_var($name, $ContextClass.'::OurVar') }
sub get_module_use { my ($self, $name) = @_; return $self->get_var($name, $ContextClass.'::ModuleUse') }
sub get_meta_info { my ($self, $name) = @_; return $self->get_var($name, $ContextClass.'::MetaInfo') }
{
package Test::WriteVariants::Context::BaseItem;
use strict;
require Data::Dumper;
require Carp;
# base class for an item (a name-value-type triple)
sub new {
my ($class, $name, $value) = @_;
my $self = bless {} => $class;
$self->name($name);
$self->value($value);
return $self;
}
sub name {
my $self = shift;
$self->{name} = shift if @_;
return $self->{name};
}
sub value {
my $self = shift;
$self->{value} = shift if @_;
return $self->{value};
}
sub get_code {
return '';
}
sub get_var {
my ($self, $name, $type) = @_;
return if $type && !$self->isa($type); # empty list
return if $name ne $self->name; # empty list
return $self->value; # scalar
}
sub quote_values_as_perl {
my $self = shift;
my @perl_values = map {
my $val = Data::Dumper->new([$_])->Terse(1)->Purity(1)->Useqq(1)->Sortkeys(1)->Dump;
chomp $val;
$val;
} @_;
Carp::confess("quote_values_as_perl called with multiple items in scalar context (@perl_values)")
if @perl_values > 1 && !wantarray;
return $perl_values[0] unless wantarray;
return @perl_values;
}
# utility method to get a new composite when you only have a value object
sub new_composite { $ContextClass->new(@_) }
} # ::BaseItem
{
package Test::WriteVariants::Context::EnvVar;
use strict;
use parent -norequire, 'Test::WriteVariants::Context::BaseItem';
# subclass representing a named environment variable
sub get_code {
my $self = shift;
my $name = $self->{name};
my @lines;
if (defined $self->{value}) {
my $perl_value = $self->quote_values_as_perl($self->{value});
push @lines, sprintf('$ENV{%s} = %s;', $name, $perl_value);
push @lines, sprintf('END { delete $ENV{%s} } # for VMS', $name);
}
else {
# we treat undef to mean the ENV var should not exist in %ENV
push @lines, sprintf('local $ENV{%s};', $name); # preserve old value for VMS
push @lines, sprintf('delete $ENV{%s};', $name); # delete from %ENV
}
return join "\n", @lines, '';
}
}
{
package Test::WriteVariants::Context::OurVar;
use strict;
use parent -norequire, 'Test::WriteVariants::Context::BaseItem';
# subclass representing a named 'our' variable
sub get_code {
my $self = shift;
my $perl_value = $self->quote_values_as_perl($self->{value});
return sprintf 'our $%s = %s;%s', $self->{name}, $perl_value, "\n";
}
}
{
package Test::WriteVariants::Context::ModuleUse;
use strict;
use parent -norequire, 'Test::WriteVariants::Context::BaseItem';
# subclass representing 'use $name (@$value)'
sub get_code {
my $self = shift;
my @imports = $self->quote_values_as_perl(@{$self->{value}});
return sprintf 'use %s (%s);%s', $self->{name}, join(", ", @imports), "\n";
}
}
{
package Test::WriteVariants::Context::MetaInfo;
use strict;
use parent -norequire, 'Test::WriteVariants::Context::BaseItem';
# subclass that doesn't generate any code
# It's just used to convey information between plugins
}
1;