The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl

use Kelp::Base -strict;
use Getopt::Long;
use File::Path 'make_path';
use Kelp::Template;
use File::Basename;
use File::Slurp 'write_file';

my $path = '.';
my $verbose = 1;
my $force = 0;
my $less = 0;
my $help = 0;

GetOptions(
    "path=s"   => \$path,
    "verbose!" => \$verbose,
    "force!"   => \$force,
    "less"     => \$less,
    "help"     => \$help
);

my $name = $ARGV[0] || do { $help = 1; '' };

if ( $help ) {
    my $usage = q[
Usage: Kelp [options] <name>

Options:
    --path=s        Path where to create the files
    --less          Create a Kelp::Less application files
    --(no)verbose   Display information
    --force         Force overwriting existing files
    --help          This help screen
];
    say $usage;
    exit;
}

# Remove the slash at the end
$path =~ s{/$}{};

# Get module path and name
my @parts = split( /::/, $name );
my $module_file = pop @parts;
my $module_path = join( '/', @parts );

# Template
my $files = do {
    my $template = Kelp::Template->new();
    my $output = $template->process(
        \*DATA, {
            module_file => $module_file,
            module_path => $module_path,
            path        => $path,
            name        => $name,
            less        => $less
        }
    );
    my %result = ();
    my $key;
    for my $line (split(/\n/, $output)) {
        if ( $line =~ /^\s*\:(.+)/ ) {
            $key = $1;
            $result{$key} = [];
            next;
        }
        if ( defined $key ) {
            push @{$result{$key}}, $line, "\n";
        }
        else {
            die "No filename for line [$line]";
        }
    }

    \%result;
};

while ( my ( $filename, $lines ) = each %$files ) {
    my $dir = $path . '/' . dirname($filename);
    $filename = $path . '/' . $filename;
    if ( !-d $dir ) {
        _say("Creating folder: $dir");
        make_path($dir);
    }

    if ( -e $filename && !$force) {
        say "File $filename exists. Use --force to overwrite.";
        next;
    }

    _say("Writing file: $filename");
    write_file( $filename, $lines );
}

sub _say {
    my $what = shift;
    if ($verbose) {
        say $what;
    }
}

__DATA__
[% IF less -%]
:[% name %].psgi
use Kelp::Less;

module 'Logger::Simple';

get '/' => sub {
    "Hello, world!";
};

run;
[% ELSE -%]
:app.psgi
use lib 'lib';
use [% name %];

my $app = [% name %]->new();
$app->run;
:lib/[% module_path %]/[% module_file %].pm
package [% name %];
use Kelp::Base 'Kelp';

sub build {
    my $self = shift;
    my $r    = $self->routes;
    $r->add( '/home', 'home' );
    $r->add( '/config', sub { $_[0]->config_hash } );
}

sub home {
    my $self = shift;
    $self->template('home');
}

1;
:conf/config.pl
# Common settings
{
    modules      => [qw/Template JSON Logger/],
    modules_init => {

        # One log for errors and one for debug
        Logger => {
            outputs => [
                [
                    'File',
                    name      => 'debug',
                    filename  => 'log/debug.log',
                    min_level => 'debug',
                    mode      => '>>',
                    newline   => 1,
                    binmode   => ":encoding(UTF-8)"
                ], [
                    'File',
                    name      => 'error',
                    filename  => 'log/error.log',
                    min_level => 'error',
                    mode      => '>>',
                    newline   => 1,
                    binmode   => ":encoding(UTF-8)"
                ],
            ]
        },

        # JSON prints pretty
        JSON => {
            pretty => 1
        },

        # Enable UTF-8 in Template
        Template => {
            encoding => 'utf8'
        }
    }
};
:conf/deployment.pl
# Options specific to deployment only
{
    modules_init => {

        # In deployment, only log the errors
        Logger => {
            outputs => [
                [
                    'File',
                    name      => 'error',
                    filename  => 'log/error.log',
                    min_level => 'error',
                    mode      => '>>',
                    newline   => 1,
                    binmode   => ":encoding(UTF-8)"
                ],
            ]
        },

        # Compress JSON output in deployment
        JSON => {
            pretty => 0
        },
    }
};
:conf/development.pl
# Options specific to development only
{
    # Add StackTrace in development
    "+middleware"   => ['StackTrace'],
    middleware_init => {
        StackTrace => {
            force => 1
        }
    }
};
:conf/test.pl
# Options specific to testing only
{
    # No Logger when testing
    "-modules" => ['Logger']
}
:views/home.tt
Hello, world!
:log/keep
keeper
:t/main.t
use Kelp::Base -strict;
use Kelp::Test;
use Test::More;
use HTTP::Request::Common;
use [% name %];

# Create an application object
my $app = [% name %]->new( mode => 'test' );

# Feed it into a test object
my $t = Kelp::Test->new( app => $app );

# Send a GET request to /home and test the response
$t->request( GET '/home' )
  ->code_is(200)
  ->content_type_is('text/html')
  ->content_like(qr/Hello, world!/);

done_testing;
[% END %]