DynGig-Schedule

 view release on metacpan or  search on metacpan

lib/DynGig/Schedule/Policy.pm  view on Meta::CPAN


use strict;
use warnings;

use Carp;
use DateTime;
use YAML::XS;

use DynGig::Range::Time::Day;
use DynGig::Range::Time::Date;

=head1 SYNOPSIS

 use DynGig::Schedule::Policy;

 my $policy = DynGig::Schedule::Policy->new
 (
     config => '/config/file',
     level => 3,
     cycle => 7,
     timezone => 'UTC',
 );

=cut
sub new
{
    my ( $class, %param ) = @_;

    map { croak "'$_' not defined" unless $param{$_} }
        qw( level cycle config timezone );

    my $level = $param{level};
    my $config = YAML::XS::LoadFile $param{config};

    croak 'policy is not HASH' if ref $config ne 'HASH';
    croak '"default" not defined' unless $config->{default};

    map { delete $config->{default}{$_} } qw( period redirect );

    for my $label ( keys %$config )
    {
        my $policy = $config->{$label};

        map { croak "$label: $_ not defined" unless $policy->{$_} }
            qw( queue epoch );

        my $queue = $policy->{queue};
        my $redirect = $policy->{redirect} || {};

        croak "$label: invalid redirect" if ref $redirect ne 'HASH';
        croak "$label: invalid queue" if ref $queue ne 'ARRAY';
        croak "$label: invalid queue" unless my @queue =
            map { grep { $_ !~ /:/ } split ',', $_ }
                ref $queue ? @$queue : $queue;

        map { $policy->{$_} ||= $param{$_} } qw( cycle timezone );

        $policy->{queue} = \@queue;

        $policy->{period} &&= DynGig::Range::Time::Day
            ->setenv( cycle => $policy->{cycle} )->new( $policy->{period} );

        $policy->{epoch} = DynGig::Range::Time::Date
            ->setenv( timezone => $policy->{timezone} )
            ->new( $policy->{epoch} )->abs()->min();

        for my $i ( keys %$redirect )
        {
            if ( $i =~ /^\d+$/ && $i > 1 && $i <= $level
                && $redirect->{$i} =~ /^([^:]+):(\d+)$/ && $config->{$1}
                && $1 ne $label && $2 && $2 <= $param{level} 
                && ! $config->{$1}{redirect}{$2 - 1} )
            {
                my $j = $i + 0;

                delete $redirect->{$i} if $i ne $j;
                $redirect->{$j} = [ $1, $2 - 1 ];
            }
            else
            {
                delete $redirect->{$i};
                carp "$label: invalid redirect '$i' ignored";
            }
        }
    }

    map { delete $config->{default}{$_} } qw( period redirect );

    bless { policy => $config, level => $level, timezone => $param{timezone} };
}

=head1 METHODS

=head2 period( start, end )

Determine the policy in specified period. Returns HASH reference.

=cut
sub period
{
    my ( $this, @period ) = @_;
    my ( %policy, %period );

    while ( my ( $name, $policy ) = each %{ $this->{policy} } )
    {
        my $period = DynGig::Range::Integer->new();
        my @queue = @{ $policy->{queue} };
        my $cycle = @queue * $policy->{cycle};
        my $dt = DateTime->from_epoch( epoch => $policy->{epoch} );

        $dt->add( days => $cycle ) while $dt->epoch() < $period[0];
        $dt->subtract( days => $cycle ) while $dt->epoch() > $period[0];

        my @epoch = $dt->epoch();

        for ( my $i = 1, my $dt = $dt->clone(); $i < $this->{level}; $i ++ )
        {
            $dt->add( days => $policy->{cycle} );
            $dt->subtract( days => $cycle ) if $dt->epoch() > $period[0];
            $epoch[$i] = $dt->epoch();
        }

        unless ( $policy->{period} )
        {



( run in 1.010 second using v1.01-cache-2.11-cpan-d8267643d1d )