package MooseX::StdDaemon;

our $VERSION   = '0.01';

use Moose::Role;
with 'MooseX::Daemonize';
with 'MooseX::Getopt';
with 'MooseX::ConfigFromFile';
with 'MooseX::LazyLogDispatch';

requires 'run';

has 'syslog_min_level' => (
    is => 'ro',
    isa => 'Str',
    default => 'info',
);

has 'syslog_facility' => (
    is => 'ro',
    isa => 'Str',
    default => 'daemon',
);

has 'syslog_ident' => (
    is => 'ro',
    isa => 'Str',
    default => $0,
);

has log_dispatch_conf => (
   is => 'ro',
   isa => 'HashRef',
   lazy => 1,
   required => 1,
   default => sub {
       my $self = shift;
       return $self->foreground
           ? {
               class     => 'Log::Dispatch::Screen',
               min_level => 'debug',
               stderr    => 1,
               format    => '[%p] %m at %F line %L%n',
           }
           : {
               class     => 'Log::Dispatch::Syslog',
               min_level => $self->syslog_min_level,
               facility  => $self->syslog_facility,
               ident     => $self->syslog_ident,
               format    => '[%p] %m',
           };
   },
);

sub daemon_action {
    my $self = shift;

    my ($command) = @{$self->extra_argv};
    defined $command || die "No command specified";

    $self->start   if $command eq 'start';
    $self->status  if $command eq 'status';
    $self->restart if $command eq 'restart';
    $self->stop    if $command eq 'stop';

    warn($self->status_message);
    exit($self->exit_code);
}

after start => sub {
    my $self = shift;

    return unless $self->is_daemon;

    $self->run();
    $self->quit();
};

sub quit {
    my $self = shift;

    $self->pidfile->remove if $self->pidfile->pid == $$;
    exit(0);
}

no Moose::Role; 1;

__END__

=pod

=head1 NAME

MooseX::StdDaemon - Boilerplate for standard *nix daemons

=head1 SYNOPSIS

  ###
  # myapp.pl - the script/daemon you execute:
  ###
  use MyApp;
  MyApp->new_with_options()->daemon_action();

  ###
  # MyApp.pm - the class where you put your code:
  ###

  use Moose;
  with 'MooseX::StdDaemon';

  # default the configfile
  has +configfile { default => '/etc/myapp.yaml' }

  # Your class attributes like these can be specified
  #  on the commandline or in the configfile:
  has something {
    is => 'ro',
    isa => 'Int',
    documentation => '--something|-s	Some Thing',
  }

  sub run {
    my $self = shift;

    $self->do_stuff_a_daemon_does();
    $self->do_stuff_a_daemon_does();
    $self->do_stuff_a_daemon_does();

    return; # exits daemon, clearing pidfile
  }

  ###
  # Invoke with explicit configfile and pidfile locations:
  ###
  ./myapp.pl --something 123 --configfile /tmp/foo.yaml --pidfile /var/run/x.pid start

  ###
  # Invoke in foreground mode (does not daemonize, log output
  #  goes to STDERR):
  ###
  ./myapp.pl --something 234 --configfile /tmp/foo.yaml -f start

=head1 DESCRIPTION

This role serves to pull together several useful MooseX roles
into a standardized boilerplate for normal run-of-the-mill
daemons.  It automatically gives you daemonization with a
pidfile, simple initscript integration, commandline flags
with usage information, configfile support, and logging
to syslog (or stderr if running in the foreground
for debug purposes).

Writing initscripts for your daemon is easy too.  All
of the hard parts have been wrapped up in this role already.

Just invoke the daemon (with any necessary options like configfile)
with any of the standard initscript commands
(stop/start/restart/status) as the initscript action, as in
(assuming you've got the rest of your normal OS initscript
down):

  case "$1" in
    start)
          echo -n "Starting ${DESC}: "
          myapp.pl $OPTIONS start
          exit $?
          ;;
    stop)
          echo -n "Stopping ${DESC}: "
          myapp.pl $OPTIONS stop
          exit $?
          ;;
    restart)
          echo -n "Restarting ${DESC}: "
          myapp.pl $OPTIONS restart
          exit $?
          ;;
    status)
          echo -n "$DESC Status: "
          myapp.pl $OPTIONS status
          exit $?
          ;;

=head1 ATTRIBUTES

=head2 syslog_min_level

Defaults to C<info>.  This is the minimum level of
logger messages that will actually be sent to syslog
by your daemon.

=head2 syslog_ident

Defaults to C<$0> (the program's name).  This is the
identifier that will be used in the syslog output.

=head2 syslog_facility

Defaults to C<daemon>.  This is the facility that
will be used for the syslog output.

=head1 METHODS

=head2 daemon_action

This checks for standard start/stop/status/restart on the
commandline and takes the appropriate action for you.

=head2 run

You provide this method, it's required.  Put the core of
your daemon (runloop or whatever) here, along with any
custom initialization like signal handlers.  When you
return from run(), the daemon will exit.

=head2 quit

This method wipes the pidfile and exits cleanly.  It's
what happens when you return from run() above.  It's
a good idea to use this when exiting via other means,
like signal handlers.

=head1 USEFUL STUFF

These are provided by the other MooseX roles listed in
the SEE ALSO section.  This is a quick recap of the
ones you'll want the most, but see those modules'
documentation for more details and more stuff

=head2 logger

Supplied by L<MooseX::LazyLogDispatch>.  Use this method
to send log messages to syslog (or the console if running
in the foreground), as in:

  $self->logger->notice("Notice: something happened");
  $self->logger->debug("Debug Stuff!");

=head2 configfile

This attribute is supplied by L<MooseX::ConfigFromFile>
via L<MooseX::SimpleConfig>.  If you want to have a default
configfile, specify it as so:

  has +configfile => ( default => '/etc/defcfg.yaml' );

Note that L<MooseX::SimpleConfig> uses L<Config::Any>, which
supports many configfile formats like YAML, JSON, and XML.
You're responsible for making sure the correct module to
parse the format is installed.  The format is detected from
the configfile extension, so YAML files need to be named
something C<.yaml>, etc.

=head1 SEE ALSO

L<Moose>

L<MooseX::Daemonize>

L<MooseX::Getopt>

L<MooseX::SimpleConfig>

L<MooseX::LazyLogDispatch>

=head1 AUTHOR

Brandon L. Black, E<lt>blblack@gmail.comE<gt>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=cut
