Chronic
NAME
Chronic - provide low level scheduling facility
SYNOPSIS
# Static configuration;
use Chronic;
react {
# Every minute
whenever Chronic.every() -> $v {
say "One: $v";
}
# Every five minutes
whenever Chronic.every(minute => '*/5') -> $v {
say "Five: $v";
}
# At 21:31 every day
whenever Chronic.every(minute => 31, hour => 21) -> $v {
say "21:31 $v";
}
}
# Dynamic configuration
use Chronic;
my @events = (
{
schedule => {},
code => sub ($v) { say "One: $v" },
},
{
schedule => { minute => '*/2' },
code => sub ($v) { say "Two: $v" },
},
{
schedule => { minute => '*/5' },
code => sub ($v) { say "Five: $v" },
},
{
schedule => { minute => 31, hour => 21 },
code => sub ($v) { say "21:31 $v"; },
},
);
for @events -> $event {
Chronic.every(|$event<schedule>).tap($event<code>);
}
# This has the effect of waiting forever
Chronic.supply.wait;
DESCRIPTION
This module provides a low-level scheduling mechanism, that be used to create cron-like schedules, the specifications can be provided as cron expression strings, lists of integer values or Junctions of values.
There is a class method every
that takes a schedule specification
and returns a Supply that will emit a value (a DateTime) on the
schedule specified. There is also a class method at
that returns
a Promise that will be kept at the specified time.
This can be used to build custom scheduling services like cron
with
additional code to read the specification from a file and arrange the
execution of the required thing or it could be used in a larger program
that may require to execute some code asynchronously periodically.
There is a single base Supply that emits an event at a 1 second frequency
in order to preserve the accuracy of the timings (in testing it may drift
by up to 59 seconds on a long run due to system latency if it didn't
match the seconds too,) so this may be a problem on a heavily loaded
single core computer. The sub-minute granularity isn't provided for in
the interface as it is easily achieved anyway with a basic supply, it
isn't supported by a standard cron
and I think most code that would
want to be executed with that frequency would be more highly optimised then
this may allow.
METHODS
method every
method every(*%args) returns Supply
This returns a Supply that will emit a value (a DateTime
indicating when the event was fired,) at the frequency specified by the
arguments. The arguments are infact passed directly to the constructor
of Chronic::Description
(described below,) which is used to match
the times that an event should occur.
method at
multi method at(Int $i) returns Promise
multi method at(Instant:D $i) returns Promise
multi method at(Str:D $d) returns Promise
multi method at(DateTime $d) returns Promise
This takes a datetime specification (either a DateTime object,
a Str that can be parsed as a DateTime, an Instant or an Int
representing the epoch seconds,) and returns a Promise which will be
kept with a DateTime when that time arrives. If the supplied datetime
specification resolves to the current time or is in the past the Promise
will be returned Kept. The resolution of the comparison is at the second
level (as with every
above,) and any fractional part in the presented
DateTime will be truncated.
If you want to do something at some time period from the current time
then you may actually be better off using the in
method of Promise
which returns a Promise that will be kept a certain number of seconds
in the future.
method supply
method supply() returns Supply
This is the base supply that emits the DateTime at 1 second intervals, it is used internally but exposed in the possibility that it may be useful as in the synopsis for example.
Chronic::Description
This is the class that is used to match the DateTime against the frequency
specification, all of the attributes are an any
Junction and by default
will match any allowed value for the period unit (hence the default is a one
minute frequency.)
The constructor allows the unit specifications to provided as Junctions, lists of integers, single integer values or strings in the form of cron specifications for that unit:
A field may be an asterisk (*), which always stands for ``first-last''.
Ranges of numbers are allowed. Ranges are two numbers separated with a
hyphen. The specified range is inclusive. For example, 8-11 for an
``hours'' entry specifies execution at hours 8, 9, 10 and 11.
Lists are allowed. A list is a set of numbers (or ranges) separated by
commas. Examples: ``1,2,5,9'', ``0-4,8-12''.
Step values can be used in conjunction with ranges. Following a range
with ``/<number>'' specifies skips of the number's value through the
range. For example, ``0-23/2'' can be used in the hours field to specify
command execution every other hour (the alternative in the V7 standard is
``0,2,4,6,8,10,12,14,16,18,20,22''). Steps are also permitted after an
asterisk, so if you want to say ``every two hours'', just use ``*/2''.
(From the FreeBSD manpage from
crontab(5)). For brevity
only the names and ranges of the values are described. The "name" forms
for month
and day-of-week
are currently not supported because the
localisation issues seemed more trouble than it's worth.
The allowed arguments to the constructor (and attributes of the class) are:
minute
The minutes in the specifcation should be matched in the range 0 .. 59
hour
The hours that should be matched in the specification in the range 0 .. 23
day
The days that should be matched in the specification, in the range 0 .. 31 clearly not all months have all those days, but "step" specifications should have the same effect for e.g. "every three days".
month
The months that should be matched in the specification in the range 1 .. 12
day-of-week
The day of the week (starting on Monday) in the range 1 .. 7