Dist-Zilla-Plugin-OptionalFeature

 view release on metacpan or  search on metacpan

lib/Dist/Zilla/Plugin/OptionalFeature.pm  view on Meta::CPAN

use MooseX::Types::Common::String 'NonEmptySimpleStr';
use Carp 'confess';
use Module::Runtime 'use_module';
use namespace::autoclean;

has name => (
    is => 'ro', isa => NonEmptySimpleStr,
    required => 1,
);
has description => (
    is => 'ro', isa => NonEmptySimpleStr,
    required => 1,
);

has always_recommend => (
    is => 'ro', isa => Bool,
    default => 0,
);

has always_suggest => (
    is => 'ro', isa => Bool,
    lazy => 1,
    default => sub { shift->always_recommend ? 0 : 1 },
);

has require_develop => (
    is => 'ro', isa => Bool,
    default => 1,
);

has prompt => (
    is => 'ro', isa => Bool,
    lazy => 1,
    default => sub { shift->_prereq_type eq 'requires' ? 1 : 0 },
);

has default => (
    is => 'ro', isa => Bool,
    predicate => '_has_default',
    # NO DEFAULT
);

has check_prereqs => (
    is => 'ro', isa => Bool,
    default => 1,
);

has _prereq_phase => (
    is => 'ro', isa => NonEmptySimpleStr,
    lazy => 1,
    default  => 'runtime',
);

has _prereq_type => (
    is => 'ro', isa => NonEmptySimpleStr,
    lazy => 1,
    default => 'requires',
);

has _prereqs => (
    is => 'ro', isa => HashRef[NonEmptySimpleStr],
    lazy => 1,
    default => sub { {} },
    traits => ['Hash'],
    handles => { _prereq_modules => 'keys', _prereq_version => 'get' },
);

sub mvp_aliases { +{ -relationship => '-type', -load_prereqs => '-check_prereqs' } }

around BUILDARGS => sub
{
    my $orig = shift;
    my $class = shift;

    my $args = $class->$orig(@_);

    my @private = grep { /^_/ } keys %$args;
    confess "Invalid options: @private" if @private;

    # pull these out so they don't become part of our prereq list
    my ($zilla, $plugin_name) = delete @{$args}{qw(zilla plugin_name)};

    my %opts = (
        map { exists $args->{$_} ? ( substr($_, 1) => delete($args->{$_}) ) : () }
        qw(-name -description -always_recommend -always_suggest -require_develop -prompt -default -check_prereqs -phase -type));
    $opts{type} //= delete $args->{'-relationship'} if defined $args->{'-relationship'};

    my @other_options = grep { /^-/ } keys %$args;
    delete @{$args}{@other_options};
    warn "[OptionalFeature] warning: unrecognized option(s): @other_options" if @other_options;

    # handle magic plugin names
    if ((not $opts{name} or not $opts{phase} or not $opts{type})
            # plugin comes from a bundle
        and $plugin_name !~ m! (?: \A | / ) OptionalFeature \z !x)
    {
        $opts{name} ||= $plugin_name;

        if ($opts{name} =~ / -
                (Build|Test|Runtime|Configure|Develop)
                (Requires|Recommends|Suggests|Conflicts)?
            \z/xp)
        {
            $opts{name} = ${^PREMATCH};
            $opts{phase} ||= lc($1) if $1;
            $opts{type} = lc($2) if $2;
        }
    }

    confess 'optional features may not use the configure phase'
        if $opts{phase} and $opts{phase} eq 'configure';

    $opts{_prereq_phase} = delete $opts{phase} if exists $opts{phase};
    $opts{_prereq_type} = delete $opts{type} if exists $opts{type};

    return {
        zilla => $zilla,
        plugin_name => $plugin_name,
        %opts,
        _prereqs => $args,
    };
};

has _dynamicprereqs_prompt => (
    is => 'ro', isa => 'ArrayRef[Str]',
    lazy => 1,
    default => sub {
        my $self = shift;

        my $phase = $self->_prereq_phase;
        my $function = $phase eq 'runtime' ? 'requires'
            : $phase eq 'test' ? 'test_requires'
            : $phase eq 'build' ? 'build_requires'
            : $self->log_fatal("illegal phase $phase");
        $self->log_fatal('prompts are only used for the \'requires\' type')
            if $self->_prereq_type ne 'requires';

        (my $description = $self->description) =~ s/'/\\'/g;

        my $prompt = "prompt('install $description? "
                . ($self->default ? "[Y/n]', 'Y'" : "[y/N]', 'N'" )
                . ') =~ /^y/i';
        my @directives = map {
            my $version = $self->_prereq_version($_);
            $function . "('$_'" . ($version ? ", '$version'" : '') . ')'
        } sort $self->_prereq_modules;

        my $require_clause = !$self->check_prereqs ? ''
            : (join(' && ', map {
                my $version = $self->_prereq_version($_);
                "has_module('$_'" . ($version ? ", '$version'" : '') . ')'
            } sort $self->_prereq_modules
        ) . "\n    || ");

        [
            @directives > 1
                ? (
                    'if (' . $require_clause . $prompt . ') {',   # to mollify vim
                    (map { '  ' . $_ . ';' } @directives),
                    '}',
                  )
                : ( @directives , '  if ' . $require_clause . $prompt . ';' )
        ];
    },
);

# package-scoped singleton to track the OptionalFeature instance that manages all the dynamic prereqs
my $master_plugin;
sub __clear_master_plugin { undef $master_plugin } # for testing

sub before_build
{
    my $self = shift;

    if ($self->prompt and not $master_plugin)
    {
        # because [DynamicPrereqs] inserts Makefile.PL content in reverse
        # order to when it was called, we make just one [OptionalFeature]
        # plugin will create and add all DynamicPrereqs plugins, so the order
        # of the prompts is in the same order as the dist.ini declarations and
        # the corresponding metadata.

        $master_plugin = $self;

        my $plugin = use_module('Dist::Zilla::Plugin::DynamicPrereqs')->new(



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