Initial import of Scheduler plugin docs
Andy Grundman [Tue, 13 Dec 2005 15:38:00 +0000 (15:38 +0000)]
Build.PL [new file with mode: 0644]
Changes [new file with mode: 0644]
MANIFEST [new file with mode: 0644]
MANIFEST.SKIP [new file with mode: 0644]
Makefile.PL [new file with mode: 0644]
lib/Catalyst/Plugin/Scheduler.pm [new file with mode: 0644]
t/01use.t [new file with mode: 0644]
t/02pod.t [new file with mode: 0644]
t/03podcoverage.t [new file with mode: 0644]

diff --git a/Build.PL b/Build.PL
new file mode 100644 (file)
index 0000000..9ed9362
--- /dev/null
+++ b/Build.PL
@@ -0,0 +1,17 @@
+use strict;
+use Module::Build;
+
+my $build = Module::Build->new(
+    create_makefile_pl => 'passthrough',
+    license            => 'perl',
+    module_name        => 'Catalyst::Plugin::Scheduler',
+    requires           => { 
+        'Catalyst' => '5.61',
+    },
+    create_makefile_pl => 'passthrough',
+    create_readme      => 1,
+    test_files   => [
+        glob('t/*.t') 
+    ]
+);
+$build->create_build_script;
diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..f6848da
--- /dev/null
+++ b/Changes
@@ -0,0 +1,4 @@
+Revision history for Perl extension Catalyst::Plugin::Scheduler
+
+0.01
+        - initial release
diff --git a/MANIFEST b/MANIFEST
new file mode 100644 (file)
index 0000000..c5fa8ad
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,10 @@
+Build.PL
+Changes
+lib/Catalyst/Plugin/Scheduler.pm
+Makefile.PL
+MANIFEST                       This list of files
+t/01use.t
+t/02pod.t
+t/03podcoverage.t
+META.yml
+README
diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP
new file mode 100644 (file)
index 0000000..b804470
--- /dev/null
@@ -0,0 +1,27 @@
+# Avoid version control files.
+\bRCS\b
+\bCVS\b
+,v$
+\B\.svn\b
+
+# Avoid Makemaker generated and utility files.
+\bMakefile$
+\bblib
+\bMakeMaker-\d
+\bpm_to_blib$
+\bblibdirs$
+^MANIFEST\.SKIP$
+
+# Avoid Module::Build generated and utility files.
+\bBuild$
+\b_build
+
+# Avoid temp and backup files.
+~$
+\.tmp$
+\.old$
+\.bak$
+\#$
+\b\.#
+
+t/var
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644 (file)
index 0000000..51d31fd
--- /dev/null
@@ -0,0 +1,31 @@
+# Note: this file was auto-generated by Module::Build::Compat version 0.03
+    
+    unless (eval "use Module::Build::Compat 0.02; 1" ) {
+      print "This module requires Module::Build to install itself.\n";
+      
+      require ExtUtils::MakeMaker;
+      my $yn = ExtUtils::MakeMaker::prompt
+       ('  Install Module::Build now from CPAN?', 'y');
+      
+      unless ($yn =~ /^y/i) {
+       die " *** Cannot install without Module::Build.  Exiting ...\n";
+      }
+      
+      require Cwd;
+      require File::Spec;
+      require CPAN;
+      
+      # Save this 'cause CPAN will chdir all over the place.
+      my $cwd = Cwd::cwd();
+      my $makefile = File::Spec->rel2abs($0);
+      
+      CPAN::Shell->install('Module::Build::Compat')
+       or die " *** Cannot install without Module::Build.  Exiting ...\n";
+      
+      chdir $cwd or die "Cannot chdir() back to $cwd: $!";
+    }
+    eval "use Module::Build::Compat 0.02; 1" or die $@;
+    use lib '_build/lib';
+    Module::Build::Compat->run_build_pl(args => \@ARGV);
+    require Module::Build;
+    Module::Build::Compat->write_makefile(build_class => 'Module::Build');
diff --git a/lib/Catalyst/Plugin/Scheduler.pm b/lib/Catalyst/Plugin/Scheduler.pm
new file mode 100644 (file)
index 0000000..ece2e92
--- /dev/null
@@ -0,0 +1,239 @@
+package Catalyst::Plugin::Scheduler;
+
+use strict;
+use warnings;
+use NEXT;
+
+our $VERSION = '0.01';
+
+1;
+__END__
+
+=pod
+
+=head1 NAME
+
+Catalyst::Plugin::Scheduler - Schedule events to run in a cron-like fashion
+
+=head1 SYNOPSIS
+
+    use Catalyst qw/Scheduler/;
+    
+    # run remove_sessions in the Cron controller every hour
+    __PACKAGE__->schedule(
+        at    => '0 * * * *',
+        event => '/cron/remove_sessions'
+    );
+    
+    # Run a subroutine at 4:05am every Sunday
+    __PACKAGE__->schedule(
+        at    => '5 4 * * sun',
+        event => \&do_stuff,
+    );
+    
+=head1 DESCRIPTION
+
+This plugin allows you to schedule events to run at recurring intervals.
+Events will run during the first request which meets or exceeds the specified
+time.  Depending on the level of traffic to the application, events may or may
+not run at exactly the correct time, but it should be enough to satisfy many
+basic scheduling needs.
+
+=head1 CONFIGURATION
+
+Configuration is optional and is specified in MyApp->config->{scheduler}.
+
+=head2 logging
+
+Set to 1 to enable logging of events as they are executed.  This option is
+enabled by default when running under -Debug mode.  Errors are always logged
+regardless of the value of this option.
+
+=head2 state_file
+
+The current state of every event is stored in a file.  By default this is
+$APP_HOME/scheduler.state.  If this file cannot be read or created at
+startup, your app will die.
+
+=head2 hosts_allow
+
+This option specifies IP addresses for trusted users.  This option defaults
+to 127.0.0.1.  Multiple addresses can be specified by using an array
+reference.  This option is used for both events where auto_run is set to 0
+and for manually-triggered events.
+
+    __PACKAGE__->config->{scheduler}->{hosts_allow} = '192.168.1.1';
+    __PACKAGE__->config->{scheduler}->{hosts_allow} = [ 
+        '127.0.0.1',
+        '192.168.1.1'
+    ];
+
+=head1 SCHEDULING
+
+=head2 AUTOMATED EVENTS
+
+Events are scheduled by calling the class method C<schedule>.
+    
+    MyApp->schedule(
+        at       => '0 * * * *',
+        event    => '/cron/remove_sessions',
+    );
+    
+    package MyApp::Controller::Cron;
+    
+    sub remove_sessions : Private {
+        my ( $self, $c ) = @_;
+        
+        $c->delete_expired_sessions;
+    }
+
+=head3 at
+
+The time to run an event is specified using L<crontab(5)>-style syntax.
+
+    5 0 * * *      # 5 minutes after midnight, every day
+    15 14 1 * *    # run at 2:15pm on the first of every month
+    0 22 * * 1-5   # run at 10 pm on weekdays
+    5 4 * * sun    # run at 4:05am every Sunday
+
+From crontab(5):
+
+    field          allowed values
+    -----          --------------
+    minute         0-59
+    hour           0-23
+    day of month   1-31
+    month          0-12 (or names, see below)
+    day of week    0-7 (0 or 7 is Sun, or use names)
+    
+Instead of the first five fields, one of seven special strings may appear:
+
+    string         meaning
+    ------         -------
+    @yearly        Run once a year, "0 0 1 1 *".
+    @annually      (same as @yearly)
+    @monthly       Run once a month, "0 0 1 * *".
+    @weekly        Run once a week, "0 0 * * 0".
+    @daily         Run once a day, "0 0 * * *".
+    @midnight      (same as @daily)
+    @hourly        Run once an hour, "0 * * * *".
+
+=head3 event
+
+The event to run at the specified time can be either a Catalyst private
+action path or a coderef.  Both types of event methods will receive the $c
+object from the current request, but you must not rely on any request-specific
+information present in $c as it will be from a random user request at or near
+the event's specified run time.
+
+Important: Methods used for events should be marked C<Private> so that
+they can not be executed via the browser.
+
+=head3 auto_run
+
+The auto_run parameter specifies when the event is allowed to be executed.
+By default this option is set to 1, so the event will be executed during the
+first request that matches the specified time in C<at>.
+
+If set to 0, the event will only run when a request is made by a user from
+an authorized address.  The purpose of this option is to allow long-running
+tasks to execute only for certain users.
+
+    MyApp->schedule(
+        at       => '0 0 * * *',
+        event    => '/cron/rebuild_search_index',
+        auto_run => 0,
+    );
+    
+    package MyApp::Controller::Cron;
+    
+    sub rebuild_search_index : Private {
+        my ( $self, $c ) = @_;
+        
+        # rebuild the search index, this may take a long time
+    }
+    
+Now, the search index will only be rebuilt when a request is made from a user
+whose IP address matches the list in the C<hosts_allow> config option.  To
+run this event, you probably want to ping the app from a cron job.
+
+    0 0 * * * wget -q http://www.myapp.com/    
+
+=head2 MANUAL EVENTS
+
+To create an event that does not run on a set schedule and must be manually
+triggered, you can specify the C<trigger> option instead of C<at>.
+
+    __PACKAGE__->schedule(
+        trigger => 'send_email',
+        event   => '/events/send_email',
+    );
+    
+The event may then be triggered by a standard web request from an authorized
+user.  The trigger to run is specified by using a special GET parameter,
+'schedule_trigger'; the path requested does not matter.
+
+    http://www.myapp.com/?schedule_trigger=send_email
+    
+By default, manual events may only be triggered by requests made from
+localhost (127.0.0.1).  To allow other addresses to run events, use the
+configuration option C<hosts_allow>.
+    
+=head1 SECURITY
+
+All events are run inside of an eval container.  This protects the user from
+receiving any error messages or page crashes if an event fails to run
+properly.  All event errors are logged, even if logging is disabled.
+
+=head1 CAVEATS
+
+The time at which an event will run is determined completely by the requests
+made to the application.  Apps with heavy traffic may have events run at very
+close to the correct time, whereas apps with low levels of traffic may see
+events running much later than scheduled.  If this is a problem, you can use
+a real cron entry that simply hits your application at the desired time.
+
+    0 * * * * wget -q http://www.myapp.com/
+
+Events which consume a lot of time will slow the request processing for the
+user who triggers the event.  For these types of events, you should use
+auto_run => 0 or manual event triggering.
+
+=head1 PLUGIN SUPPORT
+
+Other plugins may register scheduled events if they need to perform periodic
+maintenance.  Plugin authors, B<be sure to inform your users> if you do this!
+Events should be registered from a plugin's C<setup> method.
+
+    sub setup {
+        my $c = shift;
+        $c->NEXT::setup(@_);
+        
+        if ( $c->can('schedule') ) {
+            $c->schedule(
+                at    => '0 * * * *',
+                event => \&cleanup,
+            );
+        }
+    }
+
+=head1 TODO
+
+Support storing all scheduled events in an external YAML file.  This would
+only allow private actions, not coderefs.  It would also allow changes to
+the schedule to take effect in realtime.
+    
+=head1 SEE ALSO
+
+L<crontab(5)>
+
+=head1 AUTHOR
+
+Andy Grundman, <andy@hybridized.org>
+
+=head1 COPYRIGHT
+
+This program is free software, you can redistribute it and/or modify it
+under the same terms as Perl itself.
+
+=cut
diff --git a/t/01use.t b/t/01use.t
new file mode 100644 (file)
index 0000000..f4bcf10
--- /dev/null
+++ b/t/01use.t
@@ -0,0 +1,3 @@
+use Test::More tests => 1;
+
+use_ok('Catalyst::Plugin::Scheduler');
diff --git a/t/02pod.t b/t/02pod.t
new file mode 100644 (file)
index 0000000..1647794
--- /dev/null
+++ b/t/02pod.t
@@ -0,0 +1,7 @@
+use Test::More;
+
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+all_pod_files_ok();
diff --git a/t/03podcoverage.t b/t/03podcoverage.t
new file mode 100644 (file)
index 0000000..d91be5e
--- /dev/null
@@ -0,0 +1,7 @@
+use Test::More;
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@;
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+all_pod_coverage_ok();