--- /dev/null
+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