Mojolicious-Plugin-Cron

 view release on metacpan or  search on metacpan

lib/Mojolicious/Plugin/Cron.pm  view on Meta::CPAN

package Mojolicious::Plugin::Cron;
use Mojo::Base 'Mojolicious::Plugin';
use File::Spec;
use Fcntl ':flock';
use Mojo::File 'path';
use Mojo::IOLoop;
use Algorithm::Cron;

use Carp 'croak';

our $VERSION = "0.035";
use constant CRON_DIR => 'mojo_cron_';
my $crondir;

sub register {
  my ($self, $app, $cronhashes) = @_;
  croak "No schedules found" unless ref $cronhashes eq 'HASH';

# for *nix systems, getpwuid takes precedence
# for win systems or wherever getpwuid is not implemented,
# eval returns undef so getlogin takes precedence
  $crondir
    = path($app->config->{cron}{dir} // File::Spec->tmpdir)
    ->child(CRON_DIR . (eval { scalar getpwuid($<) } || getlogin || 'nobody'),
    $app->mode);
  Mojo::IOLoop->next_tick(sub {
    if (ref((values %$cronhashes)[0]) eq 'CODE') {

      # special case, plugin => 'mm hh dd ...' => sub {}
      $self->_cron($app->moniker,
        {crontab => (keys %$cronhashes)[0], code => (values %$cronhashes)[0]},
        $app);
    }
    else {
      $self->_cron($_, $cronhashes->{$_}, $app) for keys %$cronhashes;
    }
  });
}

sub _cron {
  my ($self, $sckey, $cronhash, $app) = @_;
  my $code     = delete $cronhash->{code};
  my $all_proc = delete $cronhash->{all_proc} // '';
  my $test_key
    = delete $cronhash->{__test_key};    # __test_key is for test case only
  $sckey = $test_key // $sckey;

  $cronhash->{base} //= 'local';

  ref $cronhash->{crontab} eq ''
    or croak "crontab parameter for schedule $sckey not a string";
  ref $code eq 'CODE' or croak "code parameter for schedule $sckey is not CODE";

  my $cron = Algorithm::Cron->new(%$cronhash);
  my $time = time;

  # $all_proc, $code, $cron, $sckey and $time will be part of the $task clojure
  my $task;
  $task = sub {
    $time = $cron->next_time($time);
    if (!$all_proc) {
    }
    Mojo::IOLoop->timer(
      ($time - time) => sub {
        my $fire;
        if ($all_proc) {
          $fire = 1;
        }
        else {
          my $dat = $crondir->child("$sckey.time");
          my $sem = $crondir->child("$sckey.time.lock");
          $crondir->make_path;    # ensure path exists
          my $handle_sem = $sem->open('>')
            or croak "Cannot open semaphore file $!";
          flock($handle_sem, LOCK_EX);
          my $rtime = $1
            if (-e $dat && $dat->slurp // '') =~ /(\d+)/;   # do some untainting
          $rtime //= '0';
          if ($rtime != $time) {
            $dat->spew($time);
            $fire = 1;
          }
          undef $dat;
          undef $sem;                                       # unlock



( run in 2.522 seconds using v1.01-cache-2.11-cpan-98e64b0badf )