package Mojolicious::Plugin::Minion::Workers;
use Mojo::Base 'Mojolicious::Plugin::Minion';

use Mojo::Util 'monkey_patch';
use Mojo::File 'path';

sub register {
  my ($self, $app, $conf) = @_;

  my $conf_workers = delete $conf->{workers};
  $self->SUPER::register($app, $conf)
    unless $app->renderer->get_helper('minion');

  my $is_manage = !$ARGV[0]
                                  || $ARGV[0] eq 'daemon'
                                  || $ARGV[0] eq 'prefork';
  my $is_prefork = $ENV{HYPNOTOAD_APP}
                                  || ($ARGV[0] && $ARGV[0] eq 'prefork');

  monkey_patch 'Minion',
    'manage_workers' => sub {
        unless $is_manage;

      my $minion = shift;
      my $workers = shift || $conf_workers
        or return;

      if ($is_prefork) {
        $minion->${ \\&prefork }($workers);
      } else {
        $minion->${ \\&subprocess }();

  return $self;

# Cases: hypnotoad script/ | perl script/ prefork
sub prefork {
  my ($minion, $workers) = @_;

  my $hypnotoad_pid = check_pid(
    ? path($minion->app->config->{hypnotoad}{pid_file})
    : path($ENV{HYPNOTOAD_APP})->sibling('')
  # Minion job here would be better for graceful restart worker
  # when hot deploy hypnotoad (TODO)
    if $hypnotoad_pid && !$ENV{HYPNOTOAD_STOP};


  while ($workers--) {
    defined(my $pid = fork())   || die "Can't fork: $!";
      if $pid;

    $0 = "$0 minion worker";
    $ENV{MINION_PID} = $$;
    $minion->app->log->error("Minion worker (pid $$) as prefork starting now ...");

# Cases: morbo script/ | perl script/ daemon
sub subprocess {
  my ($minion) = @_;


  # subprocess allow run/restart worker later inside app worker
  my $subprocess = Mojo::IOLoop::Subprocess->new();
    sub {
      my $subprocess = shift;
      $ENV{MINION_PID} = $$;
      $0 = "$0 minion worker";
      $minion->app->log->error("Minion worker (pid $$) as subprocess starting  now ...");
      return $$;
    sub {1}
  # Dont $subprocess->ioloop->start here!

# check process
sub check_pid {
  my ($pid_path) = @_;
  return undef unless -r $pid_path;
  my $pid = $pid_path->slurp;
  chomp $pid;
  # Running
  return $pid if $pid && kill 0, $pid;
  # Not running
  return undef;

# kill all prev workers
sub kill_all {
  my ($minion) = @_;

  kill 'QUIT', $_->{pid}
    and $minion->app->log->error("Minion worker (pid $_->{pid}) was stoped")
    for @{$minion->backend->list_workers()->{workers}};

our $VERSION = '0.09072';# as to Minion/100+0.000<minor>

=encoding utf8

=head1 Mojolicious::Plugin::Minion::Workers

Доброго всем


=head1 VERSION

0.09072 (up to Minion 9.07)

=head1 NAME

Mojolicious::Plugin::Minion::Workers - does extend base Mojolicious::Plugin::Minion
on manage Minion workers.


  # Mojolicious (define amount workers in config)
  $self->plugin('Minion::Workers' => {Pg => ..., workers=>2});
  # or pass to $app->minion->manage_workers(<num>) later
  $self->plugin('Minion::Workers' => {Pg => ...});

  # Mojolicious::Lite (define amount workers in config)
  plugin 'Minion::Workers' => {Pg => ..., workers=>2};

  # Add tasks to your application
  app->minion->add_task(slow_log => sub {
    my ($job, $msg) = @_;
    sleep 5;
    $job->app->log->debug(qq{Received message "$msg"});
  # Allow manage with amount workers (gets from config)
  # or override config workers by pass

  # Start jobs from anywhere in your application


L<Mojolicious::Plugin::Minion::Workers> is a L<Mojolicious> plugin for the L<Minion> job
queue and has extending base L<Mojolicious::Plugin::Minion> for enable workers managment.

=head1 Manage workers

L<Mojolicious::Plugin::Minion::Workers> has patch the L<Minion> module on the following new one method.

=head2 manage_workers(int)

Start/restart Minion passed amount workers or get its from plugin config.
None workers mean skip managment.


Tested on standard commands:

  $ perl script/ daemon
  $ perl script/ prefork
  $ morbo script/ # yes,  worker will restarted when morbo restarts on watch changes
  $ hypnotoad script/
  $ hypnotoad script/ # hot deploy (TODO: graceful restarting minion workers)
  $ hypnotoad -s script/ # yes, minion workers will stoped too

B<NOTE> for commands C<$ morbo script/> and C<$ perl script/ daemon>
workers always one.

=head1 HELPERS

L<Mojolicious::Plugin::Minion::Workers> enable all helpers from base plugin L<Mojolicious::Plugin::Minion>,
thus you dont need apply base plugin (auto register).

=head1 METHODS

L<Mojolicious::Plugin::Minion::Workers> inherits all methods from
L<Mojolicious::Plugin::Minion> and override the following new ones.

=head2 register

  $plugin->register(Mojolicious->new, {Pg => ..., worker=>1});

Register plugin in L<Mojolicious> application.

=head1 SEE ALSO

L<Mojolicious::Plugin::Minion>, L<Minion>, L<Mojolicious::Guides>, L<>.

=head1 AUTHOR

Михаил Че (Mikhail Che), C<< <mche[-at-]> >>


Please report any bugs or feature requests at L<>.
Pull requests also welcome.


Copyright 2019+ Mikhail Che.

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