package Dist::Zilla::PluginBundle::Author::MELO;

BEGIN {

  our $VERSION = '0.003'; # VERSION
  our $AUTHORITY = 'cpan:MELO'; # AUTHORITY
}

# ABSTRACT: Be like MELO when you build your dists

use strict;
use Moose;

## Use most recent versions at the time I wrote this -- I'll probably
## only update them if I really need a newer one
use Dist::Zilla 4.300002;
with qw(
  Dist::Zilla::Role::PluginBundle::Easy
  Dist::Zilla::Role::PluginBundle::Config::Slicer
);

use Dist::Zilla::PluginBundle::Basic ();

use Dist::Zilla::PluginBundle::Git 1.112510 ();
use Dist::Zilla::PluginBundle::TestingMania 0.014     ();
use Dist::Zilla::Plugin::Authority 1.005              ();
use Dist::Zilla::Plugin::Bugtracker 1.111080          ();
use Dist::Zilla::Plugin::CheckExtraTests 0.004        ();
use Dist::Zilla::Plugin::CheckChangesHasContent 0.003 ();
use Dist::Zilla::Plugin::Git::NextVersion ();
use Dist::Zilla::Plugin::GithubMeta 0.26      ();
use Dist::Zilla::Plugin::InstallRelease 0.007 ();
use Dist::Zilla::Plugin::MetaNoIndex ();
use Dist::Zilla::Plugin::MetaProvides::Package 1.12060501 ();
use Dist::Zilla::Plugin::MinimumPerl 1.003                ();
use Dist::Zilla::Plugin::Clean 0.02                       ();
use Dist::Zilla::Plugin::NextRelease   ();
use Dist::Zilla::Plugin::OurPkgVersion ();
use Dist::Zilla::Plugin::PodWeaver     ();
use Dist::Zilla::Plugin::Repository 0.18           ();
use Dist::Zilla::Plugin::ReportVersions::Tiny 1.03 ();
use Dist::Zilla::Plugin::Test::Pod::No404s 1.001   ();
use Pod::Weaver::PluginBundle::Author::MELO ();

use List::Util qw(first);
use Method::Signatures 20111020;

# don't require it in case it won't install somewhere
my $spelling_tests = eval 'require Dist::Zilla::Plugin::Test::PodSpelling';

# cannot use $self->name for class methods
sub _bundle_name {
  my $class = @_ ? ref $_[0] || $_[0] : __PACKAGE__;
  join('', '@', ($class =~ /^.+::PluginBundle::(.+)$/));
}

# FIXME: add 'debug' option to enable ReportPhase
method _default_attributes {
  use Moose::Util::TypeConstraints 1.01;
  return {
    auto_prereqs  => [Bool => 1],
    disable_tests => [Str  => ''],
    fake_release  => [Bool => $ENV{DZIL_FAKERELEASE}],
    authority     => [Str => 'cpan:MELO'],

    # cpanm will choose the best place to install
    install_command      => [Str  => 'cpanm -v -i .'],
    placeholder_comments => [Bool => 1],
    releaser             => [Str  => 'UploadToCPAN'],
    skip_plugins         => [Str  => ''],
    skip_prereqs         => [Str  => ''],
    weaver_config        => [Str  => $self->_bundle_name],
    test_pod_links       => [Bool => 1],
    test_perl_critic     => [Bool => 0],
    test_report_versions => [Bool => 0],
  };
}

method _generate_attribute ($key) {
  has $key => (
    is      => 'ro',
    isa     => $self->_default_attributes->{$key}[0],
    lazy    => 1,
    default => method() {
      return exists($self->payload->{$key})
      ? $self->payload->{$key}
      : $self->_default_attributes->{$key}[1];
    }
  );
}

{

  # generate attributes
  __PACKAGE__->_generate_attribute($_)
    for keys %{__PACKAGE__->_default_attributes};
}

# main
after configure => sub {

  # TODO: converting this to a anonymous method using Method::Signatures
  # triggers a bug
  # "my" variable $skip masks earlier declaration in same statement at
  # lib/Dist/Zilla/PluginBundle/Author/MELO.pm line 92, <GEN1> line 11.
  my ($self) = @_;

  my @plugins_to_skip = ($self->skip_plugins);
  push @plugins_to_skip, 'Test::Perl::Critic' unless $self->test_perl_critic;
  push @plugins_to_skip, 'Test::Pod::LinkCheck' unless $self->test_pod_links;

  my $skip_re = join('|', grep {$_} @plugins_to_skip);
  $skip_re = qr/$skip_re/;

  my $dynamic = $self->payload;

  # sneak this config in behind @TestingMania's back
  $dynamic->{'Test::Compile.fake_home'} = 1
    unless first {/Test::Compile\W+fake_home/} keys %$dynamic;

  my $plugins = $self->plugins;

  my $i = -1;
  while (++$i < @$plugins) {
    my $spec = $plugins->[$i] or next;

    # NOTE: $conf retains its reference (modifications alter $spec)
    my ($name, $class, $conf) = @$spec;

    # ignore the prefix (@Bundle/Name => Name) (DZP::Name => Name)
    my ($alias) = ($name =~ m#([^/]+)$#);

    # exclude any plugins that match 'skip_plugins'
    if ($skip_re) {

      # match on full name or plugin class (regexp should use \b not \A)
      if ($name =~ $skip_re || $class =~ $skip_re) {
        $self->log("Skipping plugin $name");
        splice(@$plugins, $i, 1);
        redo;
      }
    }
  }
  if ($ENV{DZIL_BUNDLE_DEBUG}) {
    eval {
      require Data::Dumper;
      $self->log(Data::Dumper::Dumper($self->plugins));
    };
    warn $@ if $@;
  }
};


method configure {
  $self->add_plugins(

    # provide version
    'Git::NextVersion',

    # gather and prune
    $self->_generate_manifest_skip,
    qw(
      GatherDir
      PruneCruft
      ManifestSkip
      ),

    # this is just for github
    # TODO: still not sure this is a good idea - if metacpan.org used that on
    # the distribution homepage, I would include them on my dists...
    [ PruneFiles => 'PruneRepoMetaFiles' => {match => '^(README.(pod|mm?d))$'}
    ],

    # Devel::Cover db does not need to be packaged with distribution
    [PruneFiles => 'PruneDevelCoverDatabase' => {match => '^(cover_db/.+)'}],

    # munge files
    [ Authority => {
        authority      => $self->authority,
        do_munging     => 1,
        do_metadata    => 1,
        locate_comment => $self->placeholder_comments,
      }
    ],
    [ NextRelease => {

        # w3cdtf
        time_zone => 'UTC',
        format    => q[%-9v %{yyyy-MM-dd'T'HH:mm:ss'Z'}d],
      }
    ],

    # We prefer OurPkgVersion, code should not jump around
    ($self->placeholder_comments ? 'OurPkgVersion' : 'PkgVersion'),

    # Weaver
    ['PodWeaver' => {config_plugin => $self->weaver_config}],

    # generated distribution files
    qw(
      License
      Readme
      ),

    # metadata
    # FIXME: make sure we always generate the Github issues URL and skip
    # the mailto: -- I prefer to manage my issues where I have my code,
    # at Github
    'Bugtracker',

    # FIXME: patch Repository to complain if no repository is found
    'Repository',
    'GithubMeta',
  );

  $self->add_plugins(
    [AutoPrereqs => $self->config_slice({skip_prereqs => 'skip'})])
    if $self->auto_prereqs;

  $self->add_plugins(
    [ MetaNoIndex => {

        # could use grep { -d $_ } but that will miss any generated files
        directory => [qw(corpus examples inc share t xt)],
        namespace => [qw(Local t::lib)],
        'package' => [qw(DB)],
      }
    ],
    [    # AFTER MetaNoIndex
      'MetaProvides::Package' => {meta_noindex => 1}
    ],

    qw(
      MinimumPerl
      MetaConfig
      MetaYAML
      MetaJSON
      ),

    # Make sure we use a sane version of Test::More, always
    [ Prereqs => 'TestMoreWithSubtests' => {
        -phase       => 'test',
        -type        => 'requires',
        'Test::More' => '0.98'
      }
    ],

    # build system -- MakeMaker works for me, nuf said
    qw(
      ExecDir
      ShareDir
      MakeMaker
      ),
  );

  ## Testing
  $self->add_plugins('ReportVersions::Tiny') if $self->test_report_versions;
  $self->add_plugins('Test::Pod::No404s')    if $self->test_pod_links;

  if ($spelling_tests) {
    $self->add_plugins('Test::PodSpelling');
  }
  else {
    $self->log(
      "Test::PodSpelling Plugin failed to load.  Pleese dunt mayke ani misteaks.\n"
    );
  }

  $self->add_bundle(
    '@TestingMania' => $self->config_slice({disable_tests => 'disable'}));

  $self->add_plugins(

    # manifest: must come after all generated files
    'Manifest',

    # before release
    qw(
      CheckExtraTests
      CheckChangesHasContent
      TestRelease
      ConfirmRelease
      ),
  );

  # release
  my $releaser = $self->fake_release ? 'FakeRelease' : $self->releaser;
  $self->add_plugins($releaser)
    if $releaser;

  # Git power
  $self->add_bundle('@Git');

  $self->add_plugins(
    [InstallRelease => {install_command => $self->install_command}])
    if $self->install_command;

  # cleanup afterwards
  $self->add_plugins('Clean');
}


# As of Dist::Zilla 4.102345 pluginbundles don't have log and log_fatal methods
foreach my $method (qw(log log_fatal)) {
  unless (__PACKAGE__->can($method)) {
    no strict 'refs';    ## no critic (NoStrict)
    *$method =
      $method =~ /fatal/
      ? sub { die($_[1]) }
      : sub { warn("[${\$_[0]->_bundle_name}] $_[1]") };
  }
}

sub _generate_manifest_skip {

  # include a default MANIFEST.SKIP for the tests and/or historical reasons
  return [
    GenerateFile => 'GenerateManifestSkip' => {
      filename    => 'MANIFEST.SKIP',
      is_template => 1,
      content     => <<'EOF_MANIFEST_SKIP',

\B\.git\b
\B\.gitignore$
^.prove/
^.proverc$
^[\._]build
^blib/
^_build/
^Makefile$
\bpm_to_blib$
^MYMETA\.
^.DS_Store$

EOF_MANIFEST_SKIP
    }
  ];
}

no Moose;
__PACKAGE__->meta->make_immutable;
1;


__END__
=pod

=for :stopwords Pedro Melo ACKNOWLEDGEMENTS cpan testmatrix url annocpan anno bugtracker rt
cpants kwalitee diff irc mailto metadata placeholders metacpan

=encoding utf-8

=head1 NAME

Dist::Zilla::PluginBundle::Author::MELO - Be like MELO when you build your dists

=head1 VERSION

version 0.003

=head1 SYNOPSIS

    # dist.ini
    [@Author::MELO]

=head1 DESCRIPTION

This is the way MELO is building his dists, using L<Dist::Zilla> and in
particular L<Dist::Zilla::Role::PluginBundle::Easy>.

I'm still working through all the kinks so don't expect nothing stable
until this B<warning> disappears.

This Bundle was forked from
L<RWSTAUNER|Dist::Zilla::PluginBundle::Author::RWSTAUNER>.

=head1 RATIONALE

These are the facts about my code that I would like the bundle to take
advantage off, or enforce, on all my dists:

=over 4

=item I use git

This means that the release process should tag and push each new release
to my remote repositories.

=item I use Github for code and issue/bug tracking

Although I keep my code on several remote repositories, Github is the
public face for my code, and the official location of the main
repository for it.

I also like the integration between the code tools and the issue
tracker, and the new issue dashboard, so I prefer to use Github Issues
as my prefered bugtracker for all my modules.

I still track whatever is sent to RT, but I'll most likely move the
ticket over to Issues and link the two.

=item Better to test as much as possible locally before shipping

Catching a typo or a bug before shipping is much better than receiving
the FAIL CPAN Testers report, so I enable a I<lot> of Author and
Release tests.

=back

=head1 CONFIGURATION

Possible options and their default values:

    auto_prereqs         = 1  ; enable AutoPrereqs
    disable_tests        =    ; corresponds to @TestingMania.disable
    fake_release         = 0  ; if true will use FakeRelease instead of 'releaser'
    authority            = 'cpan:MELO' ; to make D::Z::P::Authority happy
    install_command      = cpanm -v -i . (passed to InstallRelease)
    placeholder_comments = 1 ; use '# VERSION' and '# AUTHORITY' comments
    releaser             = UploadToCPAN
    skip_plugins         =    ; default empty; a regexp of plugin names to exclude
    skip_prereqs         =    ; default empty; corresponds to AutoPrereqs.skip
    weaver_config        = @Author::MELO
    test_pod_links       = 1  ; Pod::Links and Pod::No404s enabled
    test_perl_critic     = 0  ; No Perl::Critic by default
    test_report_versions = 0  ; No ReportVersions::Tiny by default

The C<fake_release> option also respects C<$ENV{DZIL_FAKERELEASE}>.

The C<release> option can be set to an alternate releaser plugin
or to an empty string to disable adding a releaser.
This can make it easier to include a plugin that requires configuration
by just ignoring the default releaser and including your own normally.

B<NOTE>:
This bundle consumes L<Dist::Zilla::Role::PluginBundle::Config::Slicer>
so you can also specify attributes for any of the bundled plugins.
The option should be the plugin name and the attribute separated by a dot:

    [@Author::MELO]
    AutoPrereqs.skip = Bad::Module

B<Note> that this is different than

    [@Author::MELO]
    [AutoPrereqs]
    skip = Bad::Module

which will load the plugin a second time.
The first example actually alters the plugin configuration
as it is included by the Bundle.

See L<Config::MVP::Slicer/CONFIGURATION SYNTAX> for more information.

If your situation is more complicated you can use the C<skip_plugins>
attribute to have the Bundle ignore that plugin
and then you can add it yourself:

    [MetaNoIndex]
    directory = one-dir
    directory = another-dir
    
    [@Author::MELO]
    skip_plugins = MetaNoIndex

=head1 EQUIVALENT F<dist.ini>

This bundle is roughly equivalent to:

  [Git::NextVersion]      ; autoincrement version from last tag

  ; choose files to include (dzil core [@Basic])
  [GatherDir]             ; everything under top dir
  [PruneCruft]            ; default stuff to skip
  [ManifestSkip]          ; custom stuff to skip
  ; use PruneFiles to specifically remove ^(dist.ini)$
  ; use PruneFiles to specifically remove ^(README.pod)$ (just for github)

  ; munge files
  [Authority]             ; inject $AUTHORITY into modules
  do_metadata = 1         ; default
  [NextRelease]           ; simplify maintenance of Changes file
  ; use W3CDTF format for release timestamps (for unambiguous dates)
  time_zone = UTC
  format    = %-9v %{yyyy-MM-dd'T'HH:mm:ss'Z'}d
  [OurPkgVersion]         ; inject $VERSION (use PkgVersion if 'placeholder_comments' == 0)
  [Prepender]             ; add header to source code files

  [PodWeaver]             ; munge POD in all modules
  config_plugin = @Author::MELO
  ; 'weaver_config' can be set to an alternate Bundle

  ; generate files
  [License]               ; generate distribution files (dzil core [@Basic])
  [Readme]

  ; metadata
  [Bugtracker]            ; include bugtracker URL and email address (uses RT)
  [Repository]            ; determine git information (if -e ".git")
  [GithubMeta]            ; overrides [Repository] if repository is on github

  [AutoPrereqs]
  ; disable with 'auto_prereqs = 0'

  [MetaNoIndex]           ; encourage CPAN not to index:
  directory = corpus
  directory = examples
  directory = inc
  directory = share
  directory = t
  directory = xt
  namespace = Local
  namespace = t::lib
  package   = DB

  [MetaProvides::Package] ; describe packages included in the dist
  meta_noindex = 1        ; ignore things excluded by above MetaNoIndex

  [MinimumPerl]           ; automatically determine Perl version required

  [MetaConfig]            ; include Dist::Zilla info in distmeta (dzil core)
  [MetaYAML]              ; include META.yml (v1.4) (dzil core [@Basic])
  [MetaJSON]              ; include META.json (v2) (more info than META.yml)

  [Prereqs / TestRequires]
  Test::More = 0.96       ; recent Test::More (including proper working subtests)

  [ExtraTests]            ; build system (dzil core [@Basic])
  [ExecDir]               ; include 'bin/*' as executables
  [ShareDir]              ; include 'share/' for File::ShareDir

  [MakeMaker]             ; create Makefile.PL

  ; generate t/ and xt/ tests
  [ReportVersions::Tiny]  ; show module versions used in test reports
  [@TestingMania]         ; *Lots* of dist tests
  [Test::PodSpelling]     ; spell check POD (if installed)

  [Manifest]              ; build MANIFEST file (dzil core [@Basic])

  ; actions for releasing the distribution (dzil core [@Basic])
  [CheckChangesHasContent]
  [TestRelease]           ; run tests before releasing
  [ConfirmRelease]        ; are you sure?
  [UploadToCPAN]
  ; see CONFIGURATION for alternate Release plugin configuration options

  [@Git]                  ; use Git bundle to commit/tag/push after releasing
  [InstallRelease]        ; install the new dist (using 'install_command')

=head1 ENVIRONMENT

We use a lot of modules and plugins and some of them can enable or
disable features based on environment variables. I've copied some of the
more useful ones to here.

=over 4



=back

= DZIL_FAKERELEASE

Enable to skip the release to CPAN as the final step of a C<< dzil release >> run.

= SKIP_POD_LINKCHECK

If true, the L<Test::Pod::LinkCheck> module is not used, and the Pod
links will not be checked.

See also the configuration C<test_pod_links> to disable this check
permanently.

= SKIP_POD_NO404S

If true, the L<Test::Pod::No404s> module is not used, and any links on
your Pod will not be checked to see if they really exist.

See also the configuration C<test_pod_links> to disable this check
permanently.

=head1 SEE ALSO

=over 4

=item *

L<Dist::Zilla>

=item *

L<Dist::Zilla::Role::PluginBundle::Easy>

=item *

L<Dist::Zilla::Role::PluginBundle::Config::Slicer>

=item *

L<Pod::Weaver>

=back

=for Pod::Coverage log log_fatal

=head1 SUPPORT

=head2 Perldoc

You can find documentation for this module with the perldoc command.

  perldoc Dist::Zilla::PluginBundle::Author::MELO

=head2 Websites

The following websites have more information about this module, and may be of help to you. As always,
in addition to those websites please use your favorite search engine to discover more resources.

=over 4

=item *

CPAN Testers

The CPAN Testers is a network of smokers who run automated tests on uploaded CPAN distributions.

L<http://www.cpantesters.org/distro/D/Dist-Zilla-PluginBundle-Author-MELO>

=item *

CPAN Testers Matrix

The CPAN Testers Matrix is a website that provides a visual overview of the test results for a distribution on various Perls/platforms.

L<http://matrix.cpantesters.org/?dist=Dist-Zilla-PluginBundle-Author-MELO>

=item *

CPAN Testers Dependencies

The CPAN Testers Dependencies is a website that shows a chart of the test results of all dependencies for a distribution.

L<http://deps.cpantesters.org/?module=Dist::Zilla::PluginBundle::Author::MELO>

=item *

CPAN Ratings

The CPAN Ratings is a website that allows community ratings and reviews of Perl modules.

L<http://cpanratings.perl.org/d/Dist-Zilla-PluginBundle-Author-MELO>

=back

=head2 Email

You can email the author of this module at C<MELO at cpan.org> asking for help with any problems you have.

=head2 Bugs / Feature Requests

Please report any bugs or feature requests by email to C<bug-dist-zilla-pluginbundle-author-melo at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/Public/Dist/Display.html?Name=Dist-Zilla-PluginBundle-Author-MELO>. You will be automatically notified of any
progress on the request by the system.

=head2 Source Code


L<https://github.com/melo/Dist-Zilla-PluginBundle-Author-Melo>

  git clone https://github.com/melo/Dist-Zilla-PluginBundle-Author-Melo.git

=head1 AUTHOR

Pedro Melo <melo@simplicidade.org>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2011 by Pedro Melo.

This is free software, licensed under:

  The Artistic License 2.0 (GPL Compatible)

=cut

