[BACK]Return to Todo.pm CVS log [TXT][DIR] Up to [local] / todotxt / Text-Todo / lib / Text

Annotation of todotxt/Text-Todo/lib/Text/Todo.pm, Revision 1.5

1.1       andrew      1: package Text::Todo;
                      2:
1.5     ! andrew      3: # $RedRiver: Todo.pm,v 1.4 2010/01/06 20:07:16 andrew Exp $
1.1       andrew      4:
                      5: use warnings;
                      6: use strict;
                      7: use Carp;
                      8:
1.2       andrew      9: use Class::Std::Utils;
                     10: use Text::Todo::Entry;
1.5     ! andrew     11: use File::Spec;
        !            12:
        !            13: use Data::Dumper;
1.2       andrew     14:
1.1       andrew     15: use version; our $VERSION = qv('0.0.1');
                     16:
1.2       andrew     17: {
                     18:
1.5     ! andrew     19:     my %path_of;
1.2       andrew     20:     my %list_of;
1.1       andrew     21:
1.2       andrew     22:     sub new {
1.5     ! andrew     23:         my ( $class, $options ) = @_;
1.1       andrew     24:
1.2       andrew     25:         my $self = bless anon_scalar(), $class;
                     26:         my $ident = ident($self);
                     27:
1.5     ! andrew     28:         $path_of{$ident} = {
        !            29:             todo_dir    => undef,
        !            30:             todo_file   => 'todo.txt',
        !            31:             done_file   => undef,
        !            32:             report_file => undef,
        !            33:         };
        !            34:
        !            35:         if ($options) {
        !            36:             if ( ref $options eq 'HASH' ) {
        !            37:                 foreach my $opt ( keys %{$options} ) {
        !            38:                     if ( exists $path_of{$ident}{$opt} ) {
        !            39:                         $self->_path_to( $opt, $options->{$opt} );
        !            40:                     }
        !            41:                     else {
        !            42:                         carp "Invalid option [$opt]";
        !            43:                     }
        !            44:                 }
        !            45:             }
        !            46:             else {
        !            47:                 if ( -d $options ) {
        !            48:                     $self->_path_to( 'todo_dir', $options );
        !            49:                 }
        !            50:                 elsif ( $options =~ /\.txt$/ixms ) {
        !            51:                     $self->_path_to( 'todo_file', $options );
        !            52:                 }
        !            53:                 else {
        !            54:                     carp "Unknown options [$options]";
        !            55:                 }
        !            56:             }
        !            57:         }
        !            58:
        !            59:         my $file = $self->_path_to('todo_file');
        !            60:         if ( defined $file && -e $file ) {
        !            61:             $self->load();
        !            62:         }
1.2       andrew     63:
                     64:         return $self;
                     65:     }
                     66:
1.5     ! andrew     67:     sub _path_to {
        !            68:         my ( $self, $type, $path ) = @_;
        !            69:         my $ident = ident($self);
        !            70:
        !            71:         if ( $type eq 'todo_dir' ) {
        !            72:             if ($path) {
        !            73:                 $path_of{$ident}{$type} = $path;
        !            74:             }
        !            75:             return $path_of{$ident}{$type};
        !            76:         }
        !            77:
        !            78:         if ($path) {
        !            79:             my ( $volume, $directories, $file )
        !            80:                 = File::Spec->splitpath($path);
        !            81:             $path_of{$ident}{$type} = $file;
        !            82:
        !            83:             if ($volume) {
        !            84:                 $directories = File::Spec->catdir( $volume, $directories );
        !            85:             }
        !            86:
        !            87:             # XXX Should we save complete paths to each file, mebbe only if
        !            88:             # the dirs are different?
        !            89:             if ($directories) {
        !            90:                 $path_of{$ident}{todo_dir} = $directories;
        !            91:             }
        !            92:         }
        !            93:
        !            94:         if ( $type =~ /(todo|done|report)_file/xms ) {
        !            95:             if ( my ( $pre, $post )
        !            96:                 = $path_of{$ident}{$type} =~ /^(.*)$1(.*)\.txt$/ixms )
        !            97:             {
        !            98:                 foreach my $f qw( todo done report ) {
        !            99:                     if ( !defined $path_of{$ident}{ $f . '_file' } ) {
        !           100:                         $path_of{$ident}{ $f . '_file' }
        !           101:                             = $pre . $f . $post . '.txt';
        !           102:                     }
        !           103:                 }
        !           104:             }
        !           105:         }
        !           106:
        !           107:         if ( defined $path_of{$ident}{todo_dir} ) {
        !           108:             return File::Spec->catfile( $path_of{$ident}{todo_dir},
        !           109:                 $path_of{$ident}{$type} );
        !           110:         }
        !           111:
        !           112:         return;
        !           113:     }
        !           114:
1.3       andrew    115:     sub file {
1.2       andrew    116:         my ( $self, $file ) = @_;
                    117:         my $ident = ident($self);
                    118:
1.5     ! andrew    119:         if ( defined $file && exists $path_of{$ident}{$file} ) {
        !           120:             $file = $self->_path_to($file);
        !           121:         }
        !           122:         else {
        !           123:             $file = $self->_path_to( 'todo_file', $file );
1.2       andrew    124:         }
                    125:
1.5     ! andrew    126:         return $file;
1.3       andrew    127:     }
                    128:
                    129:     sub load {
                    130:         my ( $self, $file ) = @_;
                    131:         my $ident = ident($self);
                    132:
1.5     ! andrew    133:         $file = $self->file($file);
        !           134:
        !           135:         if ( !defined $file ) {
        !           136:             croak "todo file can't be found";
        !           137:         }
        !           138:
        !           139:         if ( !-e $file ) {
        !           140:             carp "todo file [$file] does not exist";
        !           141:             return;
        !           142:         }
1.2       andrew    143:
                    144:         my @list;
1.5     ! andrew    145:         my $line = 1;
1.2       andrew    146:         open my $fh, '<', $file or croak "Couldn't open [$file]: $!";
                    147:         while (<$fh>) {
                    148:             s/\r?\n$//xms;
                    149:             push @list, Text::Todo::Entry->new($_);
                    150:         }
                    151:         close $fh or croak "Couldn't close [$file]: $!";
                    152:         $list_of{$ident} = \@list;
                    153:
                    154:         return 1;
                    155:     }
                    156:
                    157:     sub save {
                    158:         my ( $self, $file ) = @_;
                    159:         my $ident = ident($self);
                    160:
1.5     ! andrew    161:         $file = $self->file($file);
        !           162:         if ( !defined $file ) {
        !           163:             croak "todo file can't be found";
        !           164:         }
1.2       andrew    165:
                    166:         open my $fh, '>', $file or croak "Couldn't open [$file]: $!";
                    167:         foreach my $e ( @{ $list_of{$ident} } ) {
1.3       andrew    168:             print {$fh} $e->text . "\n"
                    169:                 or croak "Couldn't print to [$file]: $!";
1.2       andrew    170:         }
                    171:         close $fh or croak "Couldn't close [$file]: $!";
                    172:
                    173:         return 1;
                    174:     }
                    175:
                    176:     sub list {
1.3       andrew    177:         my ($self) = @_;
1.2       andrew    178:         my $ident = ident($self);
                    179:         return if !$list_of{$ident};
                    180:
1.5     ! andrew    181:         my @list = @{ $list_of{$ident} };
1.2       andrew    182:
1.5     ! andrew    183:         return wantarray ? @list : \@list;
        !           184:     }
        !           185:
        !           186:     sub listpri {
        !           187:         my ($self) = @_;
        !           188:
        !           189:         my @list = grep { $_->priority } $self->list;
        !           190:
        !           191:         return wantarray ? @list : \@list;
1.2       andrew    192:     }
1.1       andrew    193:
1.3       andrew    194:     sub add {
                    195:         my ( $self, $entry ) = @_;
                    196:         my $ident = ident($self);
                    197:
1.5     ! andrew    198:         if ( !ref $entry ) {
        !           199:             $entry = Text::Todo::Entry->new($entry);
        !           200:         }
        !           201:         elsif ( ref $entry ne 'Text::Todo::Entry' ) {
        !           202:             croak(
        !           203:                 'entry is a ' . ref($entry) . ' not a Text::Todo::Entry!' );
        !           204:         }
        !           205:
        !           206:         push @{ $list_of{$ident} }, $entry;
        !           207:
        !           208:         return $entry;
        !           209:     }
        !           210:
        !           211:     sub del {
        !           212:         my ( $self, $src ) = @_;
        !           213:         my $ident = ident($self);
        !           214:
        !           215:         my $id  = $self->_find_entry_id($src);
        !           216:
        !           217:         my @list = $self->list;
        !           218:         my $entry = splice( @list, $id, 1 );
        !           219:         $list_of{$ident} = \@list;
        !           220:
        !           221:         return $entry;
        !           222:     }
        !           223:
        !           224:     sub move {
        !           225:         my ( $self, $entry, $dst ) = @_;
        !           226:         my $ident = ident($self);
        !           227:
        !           228:         my $src  = $self->_find_entry_id($entry);
        !           229:         my @list = $self->list;
        !           230:
        !           231:         splice( @list, $dst, 0, splice( @list, $src, 1 ) );
        !           232:
        !           233:         $list_of{$ident} = \@list;
        !           234:
        !           235:         return 1;
        !           236:     }
        !           237:
        !           238:     sub listproj {
        !           239:         my ( $self, $entry, $dst ) = @_;
        !           240:         my $ident = ident($self);
        !           241:
        !           242:         my %available_projects;
        !           243:         foreach my $e ($self->list) {
        !           244:             foreach my $p ( $e->projects ) {
        !           245:                 $available_projects{$p} = 1;
        !           246:             }
        !           247:         }
        !           248:
        !           249:         my @projects = sort keys %available_projects;
        !           250:
        !           251:         return wantarray ? @projects : \@projects;
        !           252:     }
        !           253:
        !           254:     sub archive  { carp "unsupported\n", return }
        !           255:
        !           256:     sub addto    { carp "unsupported\n", return }
        !           257:     sub listfile { carp "unsupported\n", return }
        !           258:
        !           259:     sub _find_entry_id {
        !           260:         my ( $self, $entry ) = @_;
        !           261:         my $ident = ident($self);
        !           262:
1.3       andrew    263:         if ( ref $entry ) {
                    264:             if ( ref $entry ne 'Text::Todo::Entry' ) {
                    265:                 croak(    'entry is a '
                    266:                         . ref($entry)
                    267:                         . ' not a Text::Todo::Entry!' );
                    268:             }
1.5     ! andrew    269:
        !           270:             my @list = $self->list;
        !           271:             foreach my $id ( 0 .. $#list ) {
        !           272:                 if ( $list[$id] eq $entry ) {
        !           273:                     return $id;
        !           274:                 }
        !           275:             }
1.3       andrew    276:         }
1.5     ! andrew    277:         elsif ( $entry =~ /^\d+$/xms ) {
        !           278:             return $entry;
1.3       andrew    279:         }
                    280:
1.5     ! andrew    281:         croak "Invalid entry [$entry]!";
1.3       andrew    282:     }
1.2       andrew    283: }
1.1       andrew    284:
1.2       andrew    285: 1;    # Magic true value required at end of module
1.1       andrew    286: __END__
                    287:
                    288: =head1 NAME
                    289:
1.4       andrew    290: Text::Todo - Perl interface to todo_txt files
1.1       andrew    291:
                    292:
                    293: =head1 SYNOPSIS
                    294:
                    295:     use Text::Todo;
                    296:
                    297: =head1 DESCRIPTION
                    298:
1.4       andrew    299: For more information see L<http://todotxt.com>
1.1       andrew    300:
                    301: =head1 INTERFACE
                    302:
1.2       andrew    303: =head2 new
                    304:
                    305: =head2 load
                    306:
                    307: =head2 save
                    308:
1.3       andrew    309: =head2 file
                    310:
1.2       andrew    311: =head2 list
1.3       andrew    312:
                    313: =head2 add
1.1       andrew    314:
                    315: =head1 DIAGNOSTICS
                    316:
                    317: =for author to fill in:
                    318:     List every single error and warning message that the module can
                    319:     generate (even the ones that will "never happen"), with a full
                    320:     explanation of each problem, one or more likely causes, and any
                    321:     suggested remedies.
                    322:
                    323: =over
                    324:
                    325: =item C<< Error message here, perhaps with %s placeholders >>
                    326:
                    327: [Description of error here]
                    328:
                    329: =item C<< Another error message here >>
                    330:
                    331: [Description of error here]
                    332:
                    333: [Et cetera, et cetera]
                    334:
                    335: =back
                    336:
                    337:
                    338: =head1 CONFIGURATION AND ENVIRONMENT
                    339:
                    340: Text::Todo requires no configuration files or environment variables.
                    341:
1.4       andrew    342: Someday it should be able to read and use the todo.sh config file.
                    343:
1.1       andrew    344:
                    345: =head1 DEPENDENCIES
                    346:
                    347: =for author to fill in:
                    348:     A list of all the other modules that this module relies upon,
                    349:     including any restrictions on versions, and an indication whether
                    350:     the module is part of the standard Perl distribution, part of the
                    351:     module's distribution, or must be installed separately. ]
                    352:
                    353: None.
                    354:
                    355:
                    356: =head1 INCOMPATIBILITIES
                    357:
                    358: None reported.
                    359:
                    360:
                    361: =head1 BUGS AND LIMITATIONS
                    362:
                    363: No bugs have been reported.
                    364:
                    365: Please report any bugs or feature requests to
                    366: C<bug-text-todo@rt.cpan.org>, or through the web interface at
                    367: L<http://rt.cpan.org>.
                    368:
                    369:
                    370: =head1 AUTHOR
                    371:
                    372: Andrew Fresh  C<< <andrew@cpan.org> >>
                    373:
                    374:
                    375: =head1 LICENSE AND COPYRIGHT
                    376:
                    377: Copyright (c) 2009, Andrew Fresh C<< <andrew@cpan.org> >>. All rights reserved.
                    378:
                    379: This module is free software; you can redistribute it and/or
                    380: modify it under the same terms as Perl itself. See L<perlartistic>.
                    381:
                    382:
                    383: =head1 DISCLAIMER OF WARRANTY
                    384:
                    385: BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
                    386: FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
                    387: OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
                    388: PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
                    389: EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
                    390: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
                    391: ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
                    392: YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
                    393: NECESSARY SERVICING, REPAIR, OR CORRECTION.
                    394:
                    395: IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
                    396: WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
                    397: REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
                    398: LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
                    399: OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
                    400: THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
                    401: RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
                    402: FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
                    403: SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
                    404: SUCH DAMAGES.

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>