Revert Scheduler plugin back to 0.07 release state
[catagits/Catalyst-Plugin-Scheduler.git] / lib / Catalyst / Plugin / Scheduler / Base.pm
diff --git a/lib/Catalyst/Plugin/Scheduler/Base.pm b/lib/Catalyst/Plugin/Scheduler/Base.pm
deleted file mode 100755 (executable)
index 1a56b72..0000000
+++ /dev/null
@@ -1,404 +0,0 @@
-package Catalyst::Plugin::Scheduler::Base;
-
-use Data::Dumper;
-use DateTime;
-use DateTime::Event::Cron;
-use DateTime::TimeZone;
-use File::stat;
-use Set::Scalar;
-use base        qw/Catalyst::Plugin::Scheduler/;
-use Catalyst::Plugin::Scheduler::Event;
-
-__PACKAGE__->mk_classdata(_events      => []);
-__PACKAGE__->mk_classdata(_event_class => 'Catalyst::Plugin::Scheduler::Event');
-__PACKAGE__->mk_classdata('_app'            );
-
-=head1 NAME
-
-Catalyst::Plugin::Scheduler::Base - Base class for the Catalyst Scheduler
-
-=head1 SYNOPSIS
-
-    MyApp->scheduler->schedule( at => '0 0 * * *', event => '/cron/ping' );
-    
-    ### return all scheduled events as ::Event objects
-    @events     = MyApp->scheduler->list_events;
-
-    ### return all pending scheduled events as ::Event objects
-    @pending    = MyApp->scheduler->list_pending_events;
-    
-    ### a dump of the current scheduler state
-    $aref       = MyApp->scheduler->state;
-
-=head1 METHODS
-
-=head2 $bool = MyApp->scheduler->schedule
-
-Allows you to schedule events. For full usage and documentation, consult
-the C<Catalyst::Plugin::Scheduler> documentation on method C<schedule>.
-
-=cut
-
-sub schedule {
-    my $self    = shift;
-    my $c       = $self->_app;
-    my %args    = @_;
-
-    ### XXX more input checks?
-
-    unless ( $args{event} ) {
-        Catalyst::Exception->throw(
-            message => 'The schedule method requires an event parameter' );
-    }
-    
-    ### default to '1'
-    $args{'auto_run'} = 1 unless defined $args{'auto_run'};
-
-    if ( $args{at} ) {
-
-        # replace keywords that Set::Crontab doesn't support
-        $args{at} = $self->_prepare_cron( $args{at} );
-        
-        # parse the cron entry into a DateTime::Set
-        $args{set} = eval { DateTime::Event::Cron->from_cron( $args{at} ) };
-
-        Catalyst::Exception->throw(
-            "Scheduler: Unable to parse 'at' value $args{at}: $@"
-        ) if $@;
-
-    }
-
-    my $who  = $self->_caller_string;
-    push @{ $self->_events }, 
-        Catalyst::Plugin::Scheduler::Event->new( scheduled_by => $who, %args );
-
-    return 1;
-}
-
-### create a caller string like: "package (file.pm:#line)"
-sub _caller_string { return sprintf "%s (%s:%s)", @{[caller(1)]}[0,1,2]; }
-
-=head2 @events = $c->scheduler->list_events;
-
-Returns an array of C<Catalyst::Plugin::Scheduler::Event> objects,
-representing all the scheduled events in this application.
-
-See the C<Catalyst::Plugin::Scheduler::Event> documentation on how to use
-these objects.
-
-=cut
-
-sub list_events { 
-    my $self = shift;
-    return @{ $self->_events || [] };
-};
-
-=head2 @events = $c->scheduler->list_pending_events;
-
-Returns an array of C<Catalyst::Plugin::Scheduler::Event> objects,
-representing all the pending events in this application. They are the
-events that are due according to your cron specification, and will be run
-at the next dispatch, or can be run by you explicitly.
-
-See the C<Catalyst::Plugin::Scheduler::Event> documentation on how to use
-these objects.
-
-=cut
-
-sub list_pending_events {
-    my $self    = shift;
-    my $c       = $self->_app;
-    my $tz      = $self->_config('time_zone');
-    
-    ### there are no events scheduled?
-    my @events  = $self->list_events or return;
-    my $now     = DateTime->now( time_zone => $tz )->epoch;
-    
-    ### list of pending events
-    my @pending;
-    
-    ### XXX need NEXT RUN TIME??
-    EVENT: 
-    for my $event (@events) {
-    
-        ### this event is not active, so skip it
-        next EVENT unless $event->active;
-    
-        ### the proper trigger is being called
-        if( $event->trigger && $c->req->params->{schedule_trigger} &&
-            $event->trigger eq $c->req->params->{schedule_trigger} 
-        ) {
-            
-            ### if you're not authorized to call the trigger, skip it
-            next EVENT unless $self->_event_authorized;
-            
-            push @pending, $event;
-            next EVENT;
-        }
-        
-        ### we're due according to our cron-entry...
-        if( $event->set ) {
-            ### is the next run time now, or even before now?
-            push @pending, $event if $event->next_run <= $now;
-        }
-    }
-
-    ### sort them by priority
-    return sort { $a->priority <=> $b->priority } @pending;
-}
-
-
-
-sub _run_events {
-    my $self    = shift;
-    my $c       = $self->_app;
-    my %args    = @_;
-
-    $self->_check_yaml();
-
-    # check if a minute has passed since our last check
-    # This check is not run if the user is manually triggering an event
-    if ( time - $self->_last_check_time < $self->_config('check_every') ) {
-        return unless $c->req->params->{schedule_trigger};
-    }
-
-    my @events = $self->list_pending_events;
-
-    ### update the 'checked' time and save the state, so no more
-    ### processes are going to be running these events
-    ### the small race condition between the 'list_pending_events' call
-    ### and the updating of the check time is resolved by checking if a
-    ### job is running before executing it, so at worst, we have several
-    ### processes sharing the load of this cron run. --kane
-    $self->_last_check_time( time );
-
-    EVENT:
-    for my $event ( @events ) {
-
-        # do some security checking for non-auto-run events
-        ### XXX move this to $event->run? --kane
-        if ( !$event->auto_run ) {
-            next EVENT unless $self->_event_authorized;
-        }
-
-        $event->run;
-    }
-}
-
-=head2 $aref = MyApp->scheduler->state
-
-A dump of the current state of the scheudler. For full usage and 
-documentation, consult the C<Catalyst::Plugin::Scheduler> documentation on
-method C<scheduler+state>.
-
-=cut
-
-sub state {
-    my $self    = shift;
-    my $c       = $self->_app;
-
-    my $event_dump = [];
-    for my $event ( $self->list_events ) {
-        my $dump = {};
-        for my $key ( qw/at trigger event auto_run/ ) {
-            $dump->{$key} = $event->$key if $event->$key;
-        }
-
-        # display the next run time
-        $dump->{next_run} = $event->next_run_as_string;
-        
-        # display the last run time
-        $dump->{last_run} = $event->last_run_as_string; 
-        
-        # display the result of the last run
-        my $output = $event->output;
-        if ( $output ) {
-            $dump->{last_output} = $output;
-        }
-            
-        push @{$event_dump}, $dump;
-    }
-    
-    return $event_dump;
-}        
-
-sub _config {
-    my $self = shift;
-    my $key  = shift;
-    my $c    = $self->_app;
-    my $conf = $c->config->{scheduler};
-    my $rv   = $key ? $conf->{$key} : $conf;
-    
-    return $rv;
-}    
-
-### shorthand form
-sub _last_check_time {
-    my $self = shift;
-    return $self->_event_class->_last_check_time( @_ );
-}    
-
-# check and reload the YAML file with schedule data
-sub _check_yaml {
-    my $self    = shift;
-    my $c       = $self->_app;
-
-    $self->_event_class->_get_event_state();
-
-    # each process needs to load the YAML file independently
-    if ( $self->_event_class->_event_state->{yaml_mtime}->{$$} ||= 0 ) {
-        return if ( time - $self->_last_check_time < 60 );
-    }
-
-    my $file = $self->_config('yaml_file');
-    return unless -e $file;
-
-    eval {
-        my $mtime = ( stat $file )->mtime;
-        if ( $mtime > $self->_event_class->_event_state->{yaml_mtime}->{$$} ) {
-            $self->_event_class->_event_state->{yaml_mtime}->{$$} = $mtime;
-
-            # clean up old PIDs listed in yaml_mtime
-            for my $pid ( 
-                keys %{ $self->_event_class->_event_state->{yaml_mtime} } 
-            ) {
-                delete $self->_event_class->_event_state->{yaml_mtime}->{$pid}
-                    if $self->_event_class->_event_state->{yaml_mtime}->{$pid} 
-                        < $mtime 
-            }            
-            $self->_event_class->_save_event_state();
-            
-            # wipe out all current events and reload from YAML
-            $self->_events( [] );
-
-            my $yaml;
-
-            eval { require YAML::Syck; };
-            if( $@ ) {
-                require YAML;
-                $yaml = YAML::LoadFile( "$file" );
-            }
-            else {
-                open( my $fh, $file ) or die $!;
-                my $content = do { local $/; <$fh> };
-                close $fh;
-                $yaml = YAML::Syck::Load( $content );
-            }
-            
-            foreach my $event ( @{$yaml} ) {
-                $self->schedule( %{$event} );
-            }
-
-            $c->log->info( "Scheduler: PID $$ loaded "
-                    . scalar @{$yaml}
-                    . ' events from YAML file' )
-                if $self->_config('logging');
-        }
-    };
-    
-    $c->log->error("Scheduler: Error reading YAML file: $@") if $@;
-}
-
-# Detect the current time zone
-sub _detect_timezone {
-    my $self    = shift;
-    my $c       = $self->_app;
-
-    my $tz;
-    eval { $tz = DateTime::TimeZone->new( name => 'local' ) };
-    if ($@) {
-        $c->log->warn(
-            'Scheduler: Unable to autodetect local time zone, using UTC')
-            if $self->_config('logging'); 
-        return 'UTC';
-    }
-    else {
-        $c->log->debug(
-            'Scheduler: Using autodetected time zone: ' . $tz->name )
-            if $self->_config('logging');
-        return $tz->name;
-    }
-}
-
-# Check for authorized users on non-auto events
-sub _event_authorized {
-    my $self    = shift;
-    my $c       = $self->_app;
-
-    # this should never happen, but just in case...
-    return unless $c->req->address;
-
-    my $hosts_allow = $self->_config('hosts_allow');
-    $hosts_allow    = [$hosts_allow] unless ref($hosts_allow) eq 'ARRAY';
-    my $allowed     = Set::Scalar->new( @{$hosts_allow} );
-
-    return $allowed->contains( $c->req->address );
-}
-
-# Set::Crontab does not support day names, or '@' shortcuts
-{   my %replace = (
-        jan   => 1,
-        feb   => 2,
-        mar   => 3,
-        apr   => 4,
-        may   => 5,
-        jun   => 6,
-        jul   => 7,
-        aug   => 8,
-        sep   => 9,
-        'oct' => 10,
-        nov   => 11,
-        dec   => 12,
-
-        sun => 0,
-        mon => 1,
-        tue => 2,
-        wed => 3,
-        thu => 4,
-        fri => 5,
-        sat => 6,
-    );
-    
-    my %replace_at = (
-        'yearly'   => '0 0 1 1 *',
-        'annually' => '0 0 1 1 *',
-        'monthly'  => '0 0 1 * *',
-        'weekly'   => '0 0 * * 0',
-        'daily'    => '0 0 * * *',
-        'midnight' => '0 0 * * *',
-        'hourly'   => '0 * * * *',
-        'always'   => '* * * * *',
-    );
-
-    sub _prepare_cron {
-        my $self = shift;
-        my $c    = $self->_app;
-        my $cron = shift;
-    
-        return $cron unless $cron =~ /\w/;
-        
-        if ( $cron =~ /^\@/ ) {
-            $cron =~ s/^\@//;
-            return $replace_at{ $cron };
-        }
-    
-        for my $name ( keys %replace ) {
-            my $value = $replace{$name};
-            $cron =~ s/$name/$value/i;
-            last unless $cron =~ /\w/;
-        }
-
-        return $cron;
-    }
-}
-
-1;
-
-__END__
-
-=head1 SEE ALSO
-
-C<Catalyst::Plugin::Scheduler>, C<Catalyst::Plugin::Scheduler::Event>,
-
-=cut
-