Hello community,

here is the log from the commit of package perl-Minion for openSUSE:Factory 
checked in at 2018-04-16 12:52:32
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-Minion (Old)
 and      /work/SRC/openSUSE:Factory/.perl-Minion.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "perl-Minion"

Mon Apr 16 12:52:32 2018 rev:41 rq:596920 version:9.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/perl-Minion/perl-Minion.changes  2018-03-14 
19:38:10.683034534 +0100
+++ /work/SRC/openSUSE:Factory/.perl-Minion.new/perl-Minion.changes     
2018-04-16 12:52:42.404998235 +0200
@@ -1,0 +2,26 @@
+Mon Apr 16 05:35:52 UTC 2018 - co...@suse.com
+
+- updated to 9.0
+   see /usr/share/doc/packages/perl-Minion/Changes
+
+  9.0  2018-04-15
+    - Replaced queue, state and task options of list_jobs method in
+      Minion::Backend::Pg with queues, states and tasks options.
+    - Replaced name option of list_locks method in Minion::Backend::Pg with 
names
+      option.
+    - Replaced key/value argument of note method in Minion::Backend::Pg with a
+      hash reference.
+    - Added EXPERIMENTAL support for displaying a 24 hour history graph on the
+      Mojolicious::Plugin::Minion::Admin dashboard.
+    - Added EXPERIMENTAL finish event to Minion::Job.
+    - Added EXPERIMENTAL history methods to Minion and Minion::Backend::Pg.
+    - Added execute method to Minion::Job.
+    - Added -H option to job command.
+    - Improved note method in Minion::Job to allow for multiple metadata 
fields to
+      be changed at once.
+    - Fixed a bug where the job command could remove all parents from retried
+      jobs.
+    - Fixed filtering of jobs by queue and state in
+      Mojolicious::Plugin::Minion::Admin.
+
+-------------------------------------------------------------------

Old:
----
  Minion-8.12.tar.gz

New:
----
  Minion-9.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ perl-Minion.spec ++++++
--- /var/tmp/diff_new_pack.W5f68v/_old  2018-04-16 12:52:43.292965902 +0200
+++ /var/tmp/diff_new_pack.W5f68v/_new  2018-04-16 12:52:43.296965757 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           perl-Minion
-Version:        8.12
+Version:        9.0
 Release:        0
 %define cpan_name Minion
 Summary:        Job queue

++++++ Minion-8.12.tar.gz -> Minion-9.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Minion-8.12/Changes new/Minion-9.0/Changes
--- old/Minion-8.12/Changes     2018-03-07 10:51:42.000000000 +0100
+++ new/Minion-9.0/Changes      2018-04-15 23:02:18.000000000 +0200
@@ -1,4 +1,24 @@
 
+9.0  2018-04-15
+  - Replaced queue, state and task options of list_jobs method in
+    Minion::Backend::Pg with queues, states and tasks options.
+  - Replaced name option of list_locks method in Minion::Backend::Pg with names
+    option.
+  - Replaced key/value argument of note method in Minion::Backend::Pg with a
+    hash reference.
+  - Added EXPERIMENTAL support for displaying a 24 hour history graph on the
+    Mojolicious::Plugin::Minion::Admin dashboard.
+  - Added EXPERIMENTAL finish event to Minion::Job.
+  - Added EXPERIMENTAL history methods to Minion and Minion::Backend::Pg.
+  - Added execute method to Minion::Job.
+  - Added -H option to job command.
+  - Improved note method in Minion::Job to allow for multiple metadata fields 
to
+    be changed at once.
+  - Fixed a bug where the job command could remove all parents from retried
+    jobs.
+  - Fixed filtering of jobs by queue and state in
+    Mojolicious::Plugin::Minion::Admin.
+
 8.12  2018-03-07
   - Added parents option to retry and retry_job methods in Minion::Job and
     Minion::Backend::Pg. (CandyAngel)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Minion-8.12/META.json new/Minion-9.0/META.json
--- old/Minion-8.12/META.json   2018-03-07 22:29:48.000000000 +0100
+++ new/Minion-9.0/META.json    2018-04-15 23:43:15.000000000 +0200
@@ -4,7 +4,7 @@
       "Sebastian Riedel <s...@cpan.org>"
    ],
    "dynamic_config" : 0,
-   "generated_by" : "ExtUtils::MakeMaker version 7.32, CPAN::Meta::Converter 
version 2.150010",
+   "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter 
version 2.150010",
    "license" : [
       "artistic_2"
    ],
@@ -54,6 +54,6 @@
       },
       "x_IRC" : "irc://irc.perl.org/#mojo"
    },
-   "version" : "8.12",
+   "version" : "9.0",
    "x_serialization_backend" : "JSON::PP version 2.97001"
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Minion-8.12/META.yml new/Minion-9.0/META.yml
--- old/Minion-8.12/META.yml    2018-03-07 22:29:48.000000000 +0100
+++ new/Minion-9.0/META.yml     2018-04-15 23:43:15.000000000 +0200
@@ -7,7 +7,7 @@
 configure_requires:
   ExtUtils::MakeMaker: '0'
 dynamic_config: 0
-generated_by: 'ExtUtils::MakeMaker version 7.32, CPAN::Meta::Converter version 
2.150010'
+generated_by: 'ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 
2.150010'
 license: artistic_2
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -27,5 +27,5 @@
   homepage: http://mojolicious.org
   license: http://www.opensource.org/licenses/artistic-license-2.0
   repository: https://github.com/kraih/minion.git
-version: '8.12'
+version: '9.0'
 x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
Binary files old/Minion-8.12/examples/admin.png and 
new/Minion-9.0/examples/admin.png differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Minion-8.12/examples/linkcheck/lib/LinkCheck/Controller/Links.pm 
new/Minion-9.0/examples/linkcheck/lib/LinkCheck/Controller/Links.pm
--- old/Minion-8.12/examples/linkcheck/lib/LinkCheck/Controller/Links.pm        
2018-02-19 00:53:37.000000000 +0100
+++ new/Minion-9.0/examples/linkcheck/lib/LinkCheck/Controller/Links.pm 
2018-04-04 02:32:41.000000000 +0200
@@ -4,11 +4,11 @@
 sub check {
   my $self = shift;
 
-  my $validation = $self->validation;
-  $validation->required('url');
-  return $self->render(action => 'index') if $validation->has_error;
+  my $v = $self->validation;
+  $v->required('url');
+  return $self->render(action => 'index') if $v->has_error;
 
-  my $id = $self->minion->enqueue(check_links => [$validation->param('url')]);
+  my $id = $self->minion->enqueue(check_links => [$v->param('url')]);
   $self->redirect_to('result', id => $id);
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Minion-8.12/examples/linkcheck/templates/links/index.html.ep 
new/Minion-9.0/examples/linkcheck/templates/links/index.html.ep
--- old/Minion-8.12/examples/linkcheck/templates/links/index.html.ep    
2017-11-10 01:02:16.000000000 +0100
+++ new/Minion-9.0/examples/linkcheck/templates/links/index.html.ep     
2018-03-11 18:20:07.000000000 +0100
@@ -1,5 +1,5 @@
 % layout 'linkcheck', title => 'Check links';
 %= form_for 'check' => begin
-  %= url_field url => 'http://mojolicious.org'
+  %= url_field url => 'http://mojolicious.org/perldoc'
   %= submit_button 'Check'
 % end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Minion-8.12/lib/Minion/Backend/Pg.pm 
new/Minion-9.0/lib/Minion/Backend/Pg.pm
--- old/Minion-8.12/lib/Minion/Backend/Pg.pm    2018-03-07 10:49:28.000000000 
+0100
+++ new/Minion-9.0/lib/Minion/Backend/Pg.pm     2018-04-15 22:57:46.000000000 
+0200
@@ -50,6 +50,31 @@
 sub fail_job   { shift->_update(1, @_) }
 sub finish_job { shift->_update(0, @_) }
 
+sub history {
+  my $self = shift;
+
+  my $daily = $self->pg->db->query(
+    "select extract(day from ts) as day, extract(hour from ts) as hour,
+       coalesce(failed_jobs, 0) as failed_jobs,
+       coalesce(finished_jobs, 0) as finished_jobs
+     from (
+       select extract (day from finished) as day,
+         extract(hour from finished) as hour,
+         count(case when state = 'failed' then 1 end) as failed_jobs,
+         count(case when state = 'finished' then 1 end) as finished_jobs
+       from minion_jobs
+       where finished > now() - interval '23 hours'
+       group by day, hour
+     ) as j right outer join (
+       select *
+       from generate_series(now() - interval '23 hour', now(), '1 hour') as ts
+     ) as s on extract(hour from ts) = j.hour and extract(day from ts) = j.day
+     order by day, hour asc"
+  )->hashes->to_array;
+
+  return {daily => $daily};
+}
+
 sub list_jobs {
   my ($self, $offset, $limit, $options) = @_;
 
@@ -63,10 +88,11 @@
        extract(epoch from started) as started, state, task,
        count(*) over() as total, worker
      from minion_jobs as j
-     where (id = any ($1) or $1 is null) and (queue = $2 or $2 is null)
-       and (state = $3 or $3 is null) and (task = $4 or $4 is null)
+     where (id = any ($1) or $1 is null) and (queue = any ($2) or $2 is null)
+       and (state = any ($3) or $3 is null) and (task = any ($4) or $4 is null)
      order by id desc
-     limit $5 offset $6', @$options{qw(ids queue state task)}, $limit, $offset
+     limit $5 offset $6', @$options{qw(ids queues states tasks)}, $limit,
+    $offset
   )->expand->hashes->to_array;
   return _total('jobs', $jobs);
 }
@@ -77,8 +103,8 @@
   my $locks = $self->pg->db->query(
     'select name, extract(epoch from expires) as expires,
        count(*) over() as total from minion_locks
-     where expires > now() and (name = $1 or $1 is null)
-     order by id desc limit $2 offset $3', $options->{name}, $limit, $offset
+     where expires > now() and (name = any ($1) or $1 is null)
+     order by id desc limit $2 offset $3', $options->{names}, $limit, $offset
   )->hashes->to_array;
   return _total('locks', $locks);
 }
@@ -119,11 +145,10 @@
 }
 
 sub note {
-  my ($self, $id, $key, $value) = @_;
+  my ($self, $id, $merge) = @_;
   return !!$self->pg->db->query(
-    'update minion_jobs set notes = jsonb_set(notes, ?, ?, true) where id = ?',
-    [$key], {json => $value}, $id
-  )->rows;
+    'update minion_jobs set notes = notes || ? where id = ?',
+    {json => $merge}, $id)->rows;
 }
 
 sub receive {
@@ -464,10 +489,29 @@
 
 Transition from C<active> to C<finished> state.
 
+=head2 history
+
+  my $history = $backend->history;
+
+Get history information for job queue. Note that this method is EXPERIMENTAL 
and
+might change without warning!
+
+These fields are currently available:
+
+=over 2
+
+=item daily
+
+  daily => [{day => 12, hour => 20, finished_jobs => 95, failed_jobs => 2}, 
...]
+
+Hourly counts for processed jobs from the past day.
+
+=back
+
 =head2 list_jobs
 
   my $results = $backend->list_jobs($offset, $limit);
-  my $results = $backend->list_jobs($offset, $limit, {state => 'inactive'});
+  my $results = $backend->list_jobs($offset, $limit, {states => ['inactive']});
 
 Returns the information about jobs in batches.
 
@@ -489,23 +533,23 @@
 
 List only jobs with these ids.
 
-=item queue
+=item queues
 
-  queue => 'important'
+  queues => ['important', 'unimportant']
 
-List only jobs in this queue.
+List only jobs in these queues.
 
-=item state
+=item states
 
-  state => 'inactive'
+  states => ['inactive', 'active']
 
-List only jobs in this state.
+List only jobs in these states.
 
-=item task
+=item tasks
 
-  task => 'test'
+  tasks => ['foo', 'bar']
 
-List only jobs for this task.
+List only jobs for these tasks.
 
 =back
 
@@ -620,21 +664,21 @@
 =head2 list_locks
 
   my $results = $backend->list_locks($offset, $limit);
-  my $results = $backend->list_locks($offset, $limit, {name => 'foo'});
+  my $results = $backend->list_locks($offset, $limit, {names => ['foo']});
 
 Returns information about locks in batches.
 
   # Check expiration time
-  my $results = $backend->list_locks(0, 1, {name => 'foo'});
+  my $results = $backend->list_locks(0, 1, {names => ['foo']});
   my $expires = $results->{locks}[0]{expires};
 
 These options are currently available:
 
 =over 2
 
-=item name
+=item names
 
-  name => 'foo'
+  names => ['foo', 'bar']
 
 List only locks with this name.
 
@@ -754,9 +798,9 @@
 
 =head2 note
 
-  my $bool = $backend->note($job_id, foo => 'bar');
+  my $bool = $backend->note($job_id, {mojo => 'rocks', minion => 'too'});
 
-Change a metadata field for a job.
+Change one or more metadata fields for a job.
 
 =head2 receive
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Minion-8.12/lib/Minion/Backend.pm 
new/Minion-9.0/lib/Minion/Backend.pm
--- old/Minion-8.12/lib/Minion/Backend.pm       2018-03-07 10:49:41.000000000 
+0100
+++ new/Minion-9.0/lib/Minion/Backend.pm        2018-04-15 22:17:32.000000000 
+0200
@@ -5,11 +5,15 @@
 
 has 'minion';
 
-sub broadcast    { croak 'Method "broadcast" not implemented by subclass' }
-sub dequeue      { croak 'Method "dequeue" not implemented by subclass' }
-sub enqueue      { croak 'Method "enqueue" not implemented by subclass' }
-sub fail_job     { croak 'Method "fail_job" not implemented by subclass' }
-sub finish_job   { croak 'Method "finish_job" not implemented by subclass' }
+sub broadcast  { croak 'Method "broadcast" not implemented by subclass' }
+sub dequeue    { croak 'Method "dequeue" not implemented by subclass' }
+sub enqueue    { croak 'Method "enqueue" not implemented by subclass' }
+sub fail_job   { croak 'Method "fail_job" not implemented by subclass' }
+sub finish_job { croak 'Method "finish_job" not implemented by subclass' }
+
+# TODO: This method will croak after the experimentation period
+sub history { {day => []} }
+
 sub list_jobs    { croak 'Method "list_jobs" not implemented by subclass' }
 sub list_locks   { croak 'Method "list_locks" not implemented by subclass' }
 sub list_workers { croak 'Method "list_workers" not implemented by subclass' }
@@ -50,6 +54,7 @@
   sub enqueue           {...}
   sub fail_job          {...}
   sub finish_job        {...}
+  sub history           {...}
   sub list_jobs         {...}
   sub list_locks        {...}
   sub list_workers      {...}
@@ -225,10 +230,29 @@
 Transition from C<active> to C<finished> state. Meant to be overloaded in a
 subclass.
 
+=head2 history
+
+  my $history = $backend->history;
+
+Get history information for job queue. Meant to be overloaded in a subclass.
+Note that this method is EXPERIMENTAL and might change without warning!
+
+These fields are currently available:
+
+=over 2
+
+=item daily
+
+  daily => [{day => 12, hour => 20, finished_jobs => 95, failed_jobs => 2}, 
...]
+
+Hourly counts for processed jobs from the past day.
+
+=back
+
 =head2 list_jobs
 
   my $results = $backend->list_jobs($offset, $limit);
-  my $results = $backend->list_jobs($offset, $limit, {state => 'inactive'});
+  my $results = $backend->list_jobs($offset, $limit, {states => ['inactive']});
 
 Returns the information about jobs in batches. Meant to be overloaded in a
 subclass.
@@ -251,23 +275,23 @@
 
 List only jobs with these ids.
 
-=item queue
+=item queues
 
-  queue => 'important'
+  queues => ['important', 'unimportant']
 
-List only jobs in this queue.
+List only jobs in these queues.
 
-=item state
+=item states
 
-  state => 'inactive'
+  states => ['inactive', 'active']
 
-List only jobs in this state.
+List only jobs in these states.
 
-=item task
+=item tasks
 
-  task => 'test'
+  tasks => ['foo', 'bar']
 
-List only jobs for this task.
+List only jobs for these tasks.
 
 =back
 
@@ -382,24 +406,24 @@
 =head2 list_locks
 
   my $results = $backend->list_locks($offset, $limit);
-  my $results = $backend->list_locks($offset, $limit, {name => 'foo'});
+  my $results = $backend->list_locks($offset, $limit, {names => ['foo']});
 
 Returns information about locks in batches. Meant to be overloaded in a
 subclass.
 
   # Check expiration time
-  my $results = $backend->list_locks(0, 1, {name => 'foo'});
+  my $results = $backend->list_locks(0, 1, {names => ['foo']});
   my $expires = $results->{locks}[0]{expires};
 
 These options are currently available:
 
 =over 2
 
-=item name
+=item names
 
-  name => 'foo'
+  names => ['foo', 'bar']
 
-List only locks with this name.
+List only locks with these names.
 
 =back
 
@@ -512,9 +536,10 @@
 
 =head2 note
 
-  $backend->note($job_id, foo => 'bar');
+  my $bool = $backend->note($job_id, {mojo => 'rocks', minion => 'too'});
 
-Change a metadata field for a job. Meant to be overloaded in a subclass.
+Change one or more metadata fields for a job. Meant to be overloaded in a
+subclass.
 
 =head2 receive
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Minion-8.12/lib/Minion/Command/minion/job.pm 
new/Minion-9.0/lib/Minion/Command/minion/job.pm
--- old/Minion-8.12/lib/Minion/Command/minion/job.pm    2018-02-19 
00:53:39.000000000 +0100
+++ new/Minion-9.0/lib/Minion/Command/minion/job.pm     2018-04-15 
23:03:50.000000000 +0200
@@ -10,25 +10,26 @@
 sub run {
   my ($self, @args) = @_;
 
-  my ($args, $options) = ([], {});
+  my ($args, $opts) = ([], {});
   getopt \@args,
-    'A|attempts=i'  => \$options->{attempts},
+    'A|attempts=i'  => \$opts->{attempts},
     'a|args=s'      => sub { $args = decode_json($_[1]) },
     'b|broadcast=s' => (\my $command),
-    'd|delay=i'     => \$options->{delay},
+    'd|delay=i'     => \$opts->{delay},
     'e|enqueue=s'   => \my $enqueue,
     'f|foreground'  => \my $foreground,
+    'H|history'     => \my $history,
     'L|locks'       => \my $locks,
     'l|limit=i'     => \(my $limit = 100),
     'o|offset=i'    => \(my $offset = 0),
-    'P|parent=s'    => ($options->{parents} = []),
-    'p|priority=i'  => \$options->{priority},
-    'q|queue=s'     => \$options->{queue},
+    'P|parent=s'    => sub { push @{$opts->{parents}}, $_[1] },
+    'p|priority=i'  => \$opts->{priority},
+    'q|queue=s'     => sub { push @{$opts->{queues}}, $opts->{queue} = $_[1] },
     'R|retry'       => \my $retry,
     'remove'        => \my $remove,
-    'S|state=s'     => \$options->{state},
+    'S|state=s'     => sub { push @{$opts->{states}}, $_[1] },
     's|stats'       => \my $stats,
-    't|task=s'      => \$options->{task},
+    't|task=s'      => sub { push @{$opts->{tasks}}, $_[1] },
     'U|unlock=s'    => \my $unlock,
     'w|workers'     => \my $workers;
 
@@ -37,29 +38,33 @@
   return $minion->backend->broadcast($command, $args, \@args) if $command;
 
   # Enqueue
-  return say $minion->enqueue($enqueue, $args, $options) if $enqueue;
+  return say $minion->enqueue($enqueue, $args, $opts) if $enqueue;
 
   # Show stats
   return $self->_stats if $stats;
 
+  # Show history
+  return print dumper $minion->history if $history;
+
+  # Locks
+  return $minion->unlock($unlock) if $unlock;
+  return $self->_list_locks($offset, $limit, @args ? {names => \@args} : ())
+    if $locks;
+
   # Workers
   my $id = @args ? shift @args : undef;
   return $id ? $self->_worker($id) : $self->_list_workers($offset, $limit)
     if $workers;
 
-  # Locks
-  return $minion->unlock($unlock) if $unlock;
-  return $self->_list_locks($offset, $limit, {name => $id}) if $locks;
-
   # List jobs
-  return $self->_list_jobs($offset, $limit, $options) unless defined $id;
+  return $self->_list_jobs($offset, $limit, $opts) unless defined $id;
   die "Job does not exist.\n" unless my $job = $minion->job($id);
 
   # Remove job
   return $job->remove || die "Job is active.\n" if $remove;
 
   # Retry job
-  return $job->retry($options) || die "Job is active.\n" if $retry;
+  return $job->retry($opts) || die "Job is active.\n" if $retry;
 
   # Perform job in foreground
   return $minion->foreground($id) || die "Job is not ready.\n" if $foreground;
@@ -112,13 +117,13 @@
     ./myapp.pl minion job -w 23
     ./myapp.pl minion job -s
     ./myapp.pl minion job -f 10023
-    ./myapp.pl minion job -q important -t foo -S inactive
+    ./myapp.pl minion job -q important -t foo -t bar -S inactive
     ./myapp.pl minion job -e foo -a '[23, "bar"]'
     ./myapp.pl minion job -e foo -P 10023 -P 10024 -p 5 -q important
     ./myapp.pl minion job -R -d 10 10023
     ./myapp.pl minion job --remove 10023
     ./myapp.pl minion job -L
-    ./myapp.pl minion job -L some_lock
+    ./myapp.pl minion job -L some_lock some_other_lock
     ./myapp.pl minion job -b jobs -a '[12]'
     ./myapp.pl minion job -b jobs -a '[12]' 23 24 25
 
@@ -134,6 +139,7 @@
     -f, --foreground            Retry job in "minion_foreground" queue and
                                 perform it right away in the foreground (very
                                 useful for debugging)
+    -H, --history               Show queue history
     -h, --help                  Show this summary of available options
         --home <path>           Path to home directory of your application,
                                 defaults to the value of MOJO_HOME or
@@ -149,12 +155,12 @@
     -P, --parent <id>           One or more jobs the new job depends on
     -p, --priority <number>     Priority of new job, defaults to 0
     -q, --queue <name>          Queue to put new job in, defaults to "default",
-                                or list only jobs in this queue
+                                or list only jobs in these queues
     -R, --retry                 Retry job
         --remove                Remove job
-    -S, --state <name>          List only jobs in this state
+    -S, --state <name>          List only jobs in these states
     -s, --stats                 Show queue statistics
-    -t, --task <name>           List only jobs for this task
+    -t, --task <name>           List only jobs for these tasks
     -U, --unlock <name>         Release named lock
     -w, --workers               List workers instead of jobs, or show
                                 information for a specific worker
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Minion-8.12/lib/Minion/Job.pm 
new/Minion-9.0/lib/Minion/Job.pm
--- old/Minion-8.12/lib/Minion/Job.pm   2018-03-07 10:48:45.000000000 +0100
+++ new/Minion-9.0/lib/Minion/Job.pm    2018-04-15 14:52:58.000000000 +0200
@@ -8,6 +8,14 @@
 
 sub app { shift->minion->app }
 
+sub execute {
+  my $self = shift;
+  return eval {
+    $self->minion->tasks->{$self->emit('start')->task}->($self, 
@{$self->args});
+    !!$self->emit('finish');
+  } ? undef : $@;
+}
+
 sub fail {
   my ($self, $err) = (shift, shift // 'Unknown error');
   my $ok = $self->minion->backend->fail_job($self->id, $self->retries, $err);
@@ -32,7 +40,10 @@
   return 1;
 }
 
-sub note { $_[0]->minion->backend->note($_[0]->id, @_[1, 2]) }
+sub note {
+  my $self = shift;
+  return $self->minion->backend->note($self->id, {@_});
+}
 
 sub perform {
   my $self = shift;
@@ -56,8 +67,12 @@
   die "Can't fork: $!" unless defined(my $pid = fork);
   return $self->emit(spawn => $pid) if $self->{pid} = $pid;
 
+  # Reset event loop
+  Mojo::IOLoop->reset;
+  local @{$SIG}{qw(CHLD INT TERM QUIT)} = ('default') x 4;
+
   # Child
-  $self->_run;
+  if (defined(my $err = $self->execute)) { $self->fail($err) }
   POSIX::_exit(0);
 }
 
@@ -69,22 +84,6 @@
   $? ? $self->fail("Non-zero exit status (@{[$? >> 8]})") : $self->finish;
 }
 
-sub _run {
-  my $self = shift;
-
-  return undef if eval {
-
-    # Reset event loop
-    Mojo::IOLoop->reset;
-    local @{$SIG}{qw(CHLD INT TERM QUIT)} = ('default') x 4;
-    $self->minion->tasks->{$self->emit('start')->task}->($self, 
@{$self->args});
-
-    1;
-  };
-  $self->fail(my $err = $@);
-  return $err;
-}
-
 1;
 
 =encoding utf8
@@ -123,6 +122,23 @@
     say "Something went wrong: $err";
   });
 
+=head2 finish
+
+  $job->on(finish => sub {
+    my $job = shift;
+    ...
+  });
+
+Emitted in the process performing this job if the task was successful. Note 
that
+this event is EXPERIMENTAL and might change without warning!
+
+  $job->on(finish => sub {
+    my $job  = shift;
+    my $id   = $job->id;
+    my $task = $job->task;
+    $job->app->log->debug(qq{Job "$id" was performed with task "$task"});
+  });
+
 =head2 finished
 
   $job->on(finished => sub {
@@ -238,6 +254,17 @@
   # Longer version
   my $app = $job->minion->app;
 
+=head2 execute
+
+  my $err = $job->execute;
+
+Perform job in this process and return C<undef> if the task was successful or 
an
+exception otherwise.
+
+  # Perform job in foreground
+  if (my $err = $job->execute) { $job->fail($err) }
+  else                         { $job->finish }
+
 =head2 fail
 
   my $bool = $job->fail;
@@ -387,12 +414,12 @@
 
 =head2 note
 
-  my $bool = $job->note(foo => 'bar');
+  my $bool = $job->note(mojo => 'rocks', minion => 'too');
 
-Change a metadata field for this job. The new value will get serialized by
-L<Minion/"backend"> (often with L<Mojo::JSON>), so you shouldn't send objects
-and be careful with binary data, nested data structures with hash and array
-references are fine though.
+Change one or more metadata fields for this job. The new values will get
+serialized by L<Minion/"backend"> (often with L<Mojo::JSON>), so you shouldn't
+send objects and be careful with binary data, nested data structures with hash
+and array references are fine though.
 
   # Share progress information
   $job->note(progress => 95);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Minion-8.12/lib/Minion.pm 
new/Minion-9.0/lib/Minion.pm
--- old/Minion-8.12/lib/Minion.pm       2018-02-28 18:18:42.000000000 +0100
+++ new/Minion-9.0/lib/Minion.pm        2018-04-15 22:17:23.000000000 +0200
@@ -18,7 +18,7 @@
 has remove_after  => 172800;
 has tasks         => sub { {} };
 
-our $VERSION = '8.12';
+our $VERSION = '9.0';
 
 sub add_task { ($_[0]->tasks->{$_[1]} = $_[2]) and return $_[0] }
 
@@ -38,10 +38,14 @@
   return undef
     unless $job->retry({attempts => 1, queue => 'minion_foreground'});
 
+  # Reset event loop
+  Mojo::IOLoop->reset;
+  local @{$SIG}{qw(CHLD INT TERM QUIT)} = ('default') x 4;
+
   my $worker = $self->worker->register;
   $job = $worker->dequeue(0 => {id => $id, queues => ['minion_foreground']});
   my $err;
-  if ($job) { $job->finish unless defined($err = $job->_run) }
+  if ($job) { defined($err = $job->execute) ? $job->fail($err) : $job->finish }
   $worker->unregister;
 
   return defined $err ? die $err : !!$job;
@@ -54,6 +58,8 @@
   return Minion::_Guard->new(minion => $self, name => $name, time => $time);
 }
 
+sub history { shift->backend->history }
+
 sub job {
   my ($self, $id) = @_;
 
@@ -372,7 +378,8 @@
 
 Amount of time in seconds after which jobs that have reached the state
 C<finished> and have no unresolved dependencies will be removed automatically 
by
-L</"repair">, defaults to C<172800> (2 days).
+L</"repair">, defaults to C<172800> (2 days). It is not recommended to set this
+value below 2 days.
 
 =head2 tasks
 
@@ -504,6 +511,25 @@
     ...
   });
 
+=head2 history
+
+  my $history = $minion->history;
+
+Get history information for job queue. Note that this method is EXPERIMENTAL 
and
+might change without warning!
+
+These fields are currently available:
+
+=over 2
+
+=item daily
+
+  daily => [{day => 12, hour => 20, finished_jobs => 95, failed_jobs => 2}, 
...]
+
+Hourly counts for processed jobs from the past day.
+
+=back
+
 =head2 job
 
   my $job = $minion->job($id);
@@ -708,21 +734,38 @@
 
 Build L<Minion::Worker> object.
 
-  # Start a worker
-  $minion->worker->run;
+  # Use the standard worker with all its features
+  my $worker = $minion->worker;
+  $worker->status->{jobs} = 12;
+  $worker->status->{queues} = ['important'];
+  $worker->run;
 
-  # Perform one job manually
+  # Perform one job manually in a separate process
   my $worker = $minion->repair->worker->register;
   my $job    = $worker->dequeue(5);
   $job->perform;
   $worker->unregister;
 
-  # Build a custom worker
-  my $worker = $minion->repair->worker;
-  while (int rand 2) {
-    next unless my $job = $worker->register->dequeue(5);
-    $job->perform;
-  }
+  # Perform one job manually in this process
+  my $worker = $minion->repair->worker->register;
+  my $job    = $worker->dequeue(5);
+  if (my $err = $job->execute) { $job->fail($err) }
+  else                         { $job->finish }
+  $worker->unregister;
+
+  # Build a custom worker performing multiple jobs at the same time
+  my %jobs;
+  my $worker = $minion->repair->worker->register;
+  do {
+    for my $id (keys %jobs) {
+      delete $jobs{$id} if $jobs{$id}->is_finished;
+    }
+    if (keys %jobs >= 4) { sleep 5 }
+    else {
+      my $job = $worker->dequeue(5);
+      $jobs{$job->id} = $job->start if $job;
+    }
+  } while keys %jobs;
   $worker->unregister;
 
 =head1 REFERENCE
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Minion-8.12/lib/Mojolicious/Plugin/Minion/Admin.pm 
new/Minion-9.0/lib/Mojolicious/Plugin/Minion/Admin.pm
--- old/Minion-8.12/lib/Mojolicious/Plugin/Minion/Admin.pm      2018-02-19 
00:53:39.000000000 +0100
+++ new/Minion-9.0/lib/Mojolicious/Plugin/Minion/Admin.pm       2018-04-14 
03:08:35.000000000 +0200
@@ -18,8 +18,7 @@
   push @{$app->renderer->paths}, $resources->child('templates')->to_string;
 
   # Routes
-  $prefix->get('/')->to(template => 'minion/dashboard')
-    ->name('minion_dashboard');
+  $prefix->get('/'      => \&_dashboard)->name('minion_dashboard');
   $prefix->get('/stats' => \&_stats)->name('minion_stats');
   $prefix->get('/jobs'  => \&_list_jobs)->name('minion_jobs');
   $prefix->patch('/jobs' => \&_manage_jobs)->name('minion_manage_jobs');
@@ -28,22 +27,27 @@
   $prefix->get('/workers' => \&_list_workers)->name('minion_workers');
 }
 
+sub _dashboard {
+  my $c = shift;
+  my $history = $c->minion->backend->history;
+  $c->render('minion/dashboard', history => $history);
+}
+
 sub _list_jobs {
   my $c = shift;
 
-  my $validation = $c->validation;
-  $validation->optional('id');
-  $validation->optional('limit')->num;
-  $validation->optional('offset')->num;
-  $validation->optional('queue');
-  $validation->optional('state')->in(qw(active failed finished inactive));
-  $validation->optional('task');
+  my $v = $c->validation;
+  $v->optional('id');
+  $v->optional('limit')->num;
+  $v->optional('offset')->num;
+  $v->optional('queue');
+  $v->optional('state')->in(qw(active failed finished inactive));
+  $v->optional('task');
   my $options = {};
-  $options->{$_} = $validation->param($_) for qw(queue state task);
-  $options->{ids} = $validation->every_param('id')
-    if $validation->is_valid('id');
-  my $limit  = $validation->param('limit')  || 10;
-  my $offset = $validation->param('offset') || 0;
+  $v->is_valid($_) && ($options->{"${_}s"} = $v->every_param($_))
+    for qw(id queue state task);
+  my $limit  = $v->param('limit')  || 10;
+  my $offset = $v->param('offset') || 0;
 
   my $results = $c->minion->backend->list_jobs($offset, $limit, $options);
   $c->render(
@@ -58,13 +62,14 @@
 sub _list_locks {
   my $c = shift;
 
-  my $validation = $c->validation;
-  $validation->optional('limit')->num;
-  $validation->optional('offset')->num;
-  $validation->optional('name');
-  my $options = {name => $validation->param('name')};
-  my $limit  = $validation->param('limit')  || 10;
-  my $offset = $validation->param('offset') || 0;
+  my $v = $c->validation;
+  $v->optional('limit')->num;
+  $v->optional('offset')->num;
+  $v->optional('name');
+  my $options = {};
+  $options->{names} = $v->every_param('name') if $v->is_valid('name');
+  my $limit  = $v->param('limit')  || 10;
+  my $offset = $v->param('offset') || 0;
 
   my $results = $c->minion->backend->list_locks($offset, $limit, $options);
   $c->render(
@@ -79,15 +84,14 @@
 sub _list_workers {
   my $c = shift;
 
-  my $validation = $c->validation;
-  $validation->optional('id');
-  $validation->optional('limit')->num;
-  $validation->optional('offset')->num;
-  my $limit  = $validation->param('limit')  || 10;
-  my $offset = $validation->param('offset') || 0;
+  my $v = $c->validation;
+  $v->optional('id');
+  $v->optional('limit')->num;
+  $v->optional('offset')->num;
+  my $limit  = $v->param('limit')  || 10;
+  my $offset = $v->param('offset') || 0;
   my $options = {};
-  $options->{ids} = $validation->every_param('id')
-    if $validation->is_valid('id');
+  $options->{ids} = $v->every_param('id') if $v->is_valid('id');
 
   my $results = $c->minion->backend->list_workers($offset, $limit, $options);
   $c->render(
@@ -102,15 +106,15 @@
 sub _manage_jobs {
   my $c = shift;
 
-  my $validation = $c->validation;
-  $validation->required('id');
-  $validation->required('do')->in('remove', 'retry', 'stop');
+  my $v = $c->validation;
+  $v->required('id');
+  $v->required('do')->in('remove', 'retry', 'stop');
 
-  $c->redirect_to('minion_jobs') if $validation->has_error;
+  $c->redirect_to('minion_jobs') if $v->has_error;
 
   my $minion = $c->minion;
-  my $ids    = $validation->every_param('id');
-  my $do     = $validation->param('do');
+  my $ids    = $v->every_param('id');
+  my $do     = $v->param('do');
   if ($do eq 'retry') {
     my $fail = grep { $minion->job($_)->retry ? () : 1 } @$ids;
     if   ($fail) { $c->flash(danger  => "Couldn't retry all jobs.") }
@@ -137,12 +141,12 @@
 sub _unlock {
   my $c = shift;
 
-  my $validation = $c->validation;
-  $validation->required('name');
+  my $v = $c->validation;
+  $v->required('name');
 
-  $c->redirect_to('minion_locks') if $validation->has_error;
+  $c->redirect_to('minion_locks') if $v->has_error;
 
-  my $names  = $validation->every_param('name');
+  my $names  = $v->every_param('name');
   my $minion = $c->minion;
   $minion->unlock($_) for @$names;
   $c->flash(success => 'All selected named locks released.');
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Minion-8.12/lib/Mojolicious/Plugin/Minion/resources/public/minion/app.css 
new/Minion-9.0/lib/Mojolicious/Plugin/Minion/resources/public/minion/app.css
--- 
old/Minion-8.12/lib/Mojolicious/Plugin/Minion/resources/public/minion/app.css   
    2018-02-18 00:41:52.000000000 +0100
+++ 
new/Minion-9.0/lib/Mojolicious/Plugin/Minion/resources/public/minion/app.css    
    2018-04-15 22:10:51.000000000 +0200
@@ -57,3 +57,9 @@
   stroke: #77c293;
   stroke-width: 8;
 }
+#history-chart {
+  height: 200px;
+}
+#history-chart .processed {
+  fill: #a9e3be;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Minion-8.12/lib/Mojolicious/Plugin/Minion/resources/templates/minion/dashboard.html.ep
 
new/Minion-9.0/lib/Mojolicious/Plugin/Minion/resources/templates/minion/dashboard.html.ep
--- 
old/Minion-8.12/lib/Mojolicious/Plugin/Minion/resources/templates/minion/dashboard.html.ep
  2017-12-09 03:08:08.000000000 +0100
+++ 
new/Minion-9.0/lib/Mojolicious/Plugin/Minion/resources/templates/minion/dashboard.html.ep
   2018-04-15 22:19:49.000000000 +0200
@@ -32,7 +32,23 @@
       }
       setTimeout(updateBacklogChart, 1000);
     }
-    $(function () { updateBacklogChart() });
+    $(function () {
+      updateBacklogChart();
+      $('#history-chart').epoch({
+        type: 'bar',
+        data: [{
+          label: 'processed',
+          values: [
+            % for my $hour (@{$history->{daily}}) {
+              {
+                x: '<%= sprintf '%02d:00', $hour->{hour} %>',
+                y: <%= $hour->{finished_jobs} + $hour->{failed_jobs} %>
+              },
+            % }
+          ]
+        }]
+      });
+    });
   </script>
 % end
 
@@ -96,3 +112,11 @@
     <div id="backlog-chart" class="epoch category20c"></div>
   </div>
 </div>
+
+<h3>History</h3>
+
+<div class="row">
+  <div class="col-md-12">
+    <div id="history-chart" class="epoch category20"></div>
+  </div>
+</div>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Minion-8.12/lib/Mojolicious/Plugin/Minion/resources/templates/minion/jobs.html.ep
 
new/Minion-9.0/lib/Mojolicious/Plugin/Minion/resources/templates/minion/jobs.html.ep
--- 
old/Minion-8.12/lib/Mojolicious/Plugin/Minion/resources/templates/minion/jobs.html.ep
       2018-02-18 00:30:49.000000000 +0100
+++ 
new/Minion-9.0/lib/Mojolicious/Plugin/Minion/resources/templates/minion/jobs.html.ep
        2018-04-13 19:35:22.000000000 +0200
@@ -76,7 +76,7 @@
         % my $i = 0;
         % for my $job (@$jobs) {
           % $i++;
-          % my $base = url_with->query(offset => 0);
+          % my $base = url_with->query([offset => 0]);
           <tbody>
             <tr>
               <td>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Minion-8.12/t/pg.t new/Minion-9.0/t/pg.t
--- old/Minion-8.12/t/pg.t      2018-03-07 10:48:25.000000000 +0100
+++ new/Minion-9.0/t/pg.t       2018-04-15 23:05:09.000000000 +0200
@@ -184,7 +184,7 @@
 like $results->{locks}[0]{expires}, qr/^[\d.]+$/, 'expires';
 is $results->{locks}[1], undef, 'no more locks';
 is $results->{total}, 3, 'three results';
-$results = $minion->backend->list_locks(0, 10, {name => 'yada'});
+$results = $minion->backend->list_locks(0, 10, {names => ['yada']});
 is $results->{locks}[0]{name},      'yada',       'right name';
 like $results->{locks}[0]{expires}, qr/^[\d.]+$/, 'expires';
 is $results->{locks}[1]{name},      'yada',       'right name';
@@ -195,7 +195,7 @@
   "update minion_locks set expires = now() - interval '1 second' * 1
    where name = 'yada'",
 );
-is $minion->backend->list_locks(0, 10, {name => 'yada'})->{total}, 0,
+is $minion->backend->list_locks(0, 10, {names => ['yada']})->{total}, 0,
   'no results';
 $minion->unlock('test');
 is $minion->backend->list_locks(0, 10)->{total}, 0, 'no results';
@@ -282,6 +282,30 @@
 is $stats->{inactive_jobs},    0, 'no inactive jobs';
 is $stats->{delayed_jobs},     0, 'no delayed jobs';
 
+# History
+$minion->enqueue('fail');
+$worker = $minion->worker->register;
+$job    = $worker->dequeue(0);
+ok $job->fail, 'job failed';
+$worker->unregister;
+my $history = $minion->history;
+is $#{$history->{daily}}, 23, 'data for 24 hours';
+is $history->{daily}[-1]{finished_jobs}, 3, 'one failed job in the last hour';
+is $history->{daily}[-1]{failed_jobs}, 1,
+  'three finished jobs in the last hour';
+is $history->{daily}[0]{finished_jobs}, 0, 'no finished jobs 24 hours ago';
+is $history->{daily}[0]{failed_jobs},   0, 'no failed jobs 24 hours ago';
+ok defined $history->{daily}[0]{day},   'has day value';
+ok defined $history->{daily}[0]{hour},  'has hour value';
+ok defined $history->{daily}[1]{day},   'has day value';
+ok defined $history->{daily}[1]{hour},  'has hour value';
+ok defined $history->{daily}[12]{day},  'has day value';
+ok defined $history->{daily}[12]{hour}, 'has hour value';
+ok defined $history->{daily}[-1]{day},  'has day value';
+ok defined $history->{daily}[-1]{hour}, 'has hour value';
+isnt $history->{daily}[0]{hour}, $history->{daily}[1]{hours}, 'different hour';
+$job->remove;
+
 # List jobs
 $id      = $minion->enqueue('add');
 $results = $minion->backend->list_jobs(0, 10);
@@ -313,22 +337,28 @@
 is $batch->[3]{state},      'finished',   'right state';
 is $batch->[3]{retries},    0,            'job has not been retried';
 ok !$batch->[4], 'no more results';
-$batch = $minion->backend->list_jobs(0, 10, {state => 'inactive'})->{jobs};
+$batch = $minion->backend->list_jobs(0, 10, {states => ['inactive']})->{jobs};
 is $batch->[0]{state},   'inactive', 'right state';
 is $batch->[0]{retries}, 0,          'job has not been retried';
 ok !$batch->[1], 'no more results';
-$batch = $minion->backend->list_jobs(0, 10, {task => 'add'})->{jobs};
+$batch = $minion->backend->list_jobs(0, 10, {tasks => ['add']})->{jobs};
 is $batch->[0]{task},    'add', 'right task';
 is $batch->[0]{retries}, 0,     'job has not been retried';
 ok !$batch->[1], 'no more results';
-$batch = $minion->backend->list_jobs(0, 10, {queue => 'default'})->{jobs};
+$batch = $minion->backend->list_jobs(0, 10, {tasks => ['add', 
'fail']})->{jobs};
+is $batch->[0]{task}, 'add',  'right task';
+is $batch->[1]{task}, 'fail', 'right task';
+is $batch->[2]{task}, 'fail', 'right task';
+is $batch->[3]{task}, 'fail', 'right task';
+ok !$batch->[4], 'no more results';
+$batch = $minion->backend->list_jobs(0, 10, {queues => ['default']})->{jobs};
 is $batch->[0]{queue}, 'default', 'right queue';
 is $batch->[1]{queue}, 'default', 'right queue';
 is $batch->[2]{queue}, 'default', 'right queue';
 is $batch->[3]{queue}, 'default', 'right queue';
 ok !$batch->[4], 'no more results';
 $batch
-  = $minion->backend->list_jobs(0, 10, {queue => 'does_not_exist'})->{jobs};
+  = $minion->backend->list_jobs(0, 10, {queues => ['does_not_exist']})->{jobs};
 is_deeply $batch, [], 'no results';
 $results = $minion->backend->list_jobs(0, 1);
 $batch = $results->{jobs};
@@ -493,6 +523,13 @@
             $job->task('add')->args->[-1] += 1;
           }
         );
+        $job->on(
+          finish => sub {
+            my $job = shift;
+            return unless defined(my $old = $job->info->{notes}{finish_count});
+            $job->note(finish_count => $old + 1, pid => $$);
+          }
+        );
       }
     );
   }
@@ -523,10 +560,15 @@
 is $failed,   1,        'failed event has been emitted once';
 is $finished, 1,        'finished event has been emitted once';
 $minion->add_task(switcheroo => sub { });
-$minion->enqueue(switcheroo => [5, 3]);
+$minion->enqueue(
+  switcheroo => [5, 3] => {notes => {finish_count => 0, before => 23}});
 $job = $worker->dequeue(0);
 $job->perform;
 is_deeply $job->info->{result}, {added => 9}, 'right result';
+is $job->info->{notes}{finish_count}, 1, 'finish event has been emitted once';
+ok $job->info->{notes}{pid},    'has a process id';
+isnt $job->info->{notes}{pid},  $$, 'different process id';
+is $job->info->{notes}{before}, 23, 'value still exists';
 $worker->unregister;
 
 # Queues


Reply via email to