=================================================================== RCS file: /cvs/todotxt/Text-Todo-REST-API/lib/Text/Todo/REST/API.pm,v retrieving revision 1.8 retrieving revision 1.20 diff -u -r1.8 -r1.20 --- todotxt/Text-Todo-REST-API/lib/Text/Todo/REST/API.pm 2010/01/19 03:18:34 1.8 +++ todotxt/Text-Todo-REST-API/lib/Text/Todo/REST/API.pm 2010/04/30 00:04:29 1.20 @@ -1,32 +1,23 @@ package Text::Todo::REST::API; -# $AFresh1: API.pm,v 1.7 2010/01/18 13:47:53 andrew Exp $ +# $AFresh1: API.pm,v 1.19 2010/02/16 06:12:26 andrew Exp $ use warnings; use strict; use Carp; -use Data::Dumper; use Text::Todo; +use Text::Todo::REST::API::Response; -use Class::Std::Utils; -use Digest::MD5 qw/ md5_hex /; - use Module::Pluggable instantiate => 'new', - search_path => __PACKAGE__ . '::Representations', - sub_name => 'representations'; + search_path => 'Text::Todo::REST::API::Actions', + sub_name => 'actions'; +use Class::Std::Utils; + use version; our $VERSION = qv('0.0.1'); -&RegisterActionHandler( - 'GET', - [ list => 'get_list' ], - [ entry => 'get_entry' ], - [ tags => 'get_tags' ], - [ files => 'get_files' ], -); - { my @attr_refs = \( my %todo_of, @@ -35,10 +26,6 @@ my %file_regex_of, my %format_of, - my %user_of, - my %list_of, - my %action_of, - my %args_of, my %action_handlers, ); @@ -53,11 +40,6 @@ if ( $options->{format} ) { $format_of{$ident} = $options->{format}; } - elsif ($options->{path_info} - && $options->{path_info} =~ s/\.(\w+)$//xms ) - { - $format_of{$ident} = $1; - } $suffix_of{$ident} = $options->{suffix} || '.txt'; @@ -69,18 +51,6 @@ $ }ixms; - $options->{path_info} ||= q{}; - $options->{path_info} =~ s{^/}{}xms; - ( $list_of{$ident}, $action_of{$ident}, @{ $args_of{$ident} }, ) - = split '/', $options->{path_info}; - - if ( $list_of{$ident} ) { - $action_of{$ident} ||= 'list'; - } - else { - $action_of{$ident} = 'files'; - } - eval { $todo_of{$ident} = Text::Todo->new( { todo_dir => $options->{todo_dir}, @@ -92,152 +62,145 @@ $self->fail( 'Unable to create Text::Todo object' . $@ ); } - $todo_of{$ident}->load('todo_file') - or $self->fail('Unable to load todo_file in Text::Todo object'); - return $self; } - sub RegisterActionHandler { - my ( $handler, @types ) = @_; + sub _parse_options { + my ( $self, $method, @options) = @_; - foreach my $type (@types) { - $action_handlers{$handler}{ $type->[0] } = $type->[1]; - } + my %options = ( + method => lc $method, + list => '', + action => 'files', + args => {}, - return 1; - } + suffix => $self->_suffix, + file_regex => $self->_file_regex, + format => $self->_format, + ); - sub content_type {return} + if (@options) { + if ( !ref $options[0] ) { + $options{path} = shift @options; + } - sub _handle_representation { - my ( $self, $type, @args ) = @_; + if ( ref $options[0] eq 'HASH' ) { + my $opts = shift @options; + foreach my $o ( keys %{$opts} ) { + $options{$o} = $opts->{$o}; + } + } + } - my $method = join q{_}, $type, $self->_action; + if ( exists $options{path} ) { + my %opts = $self->_split_path( $options{path} ); + delete $options{path}; - foreach my $class ( $self->representations ) { - if ( $class->can_format( $self->_format ) - && $class->can( $method ) ) - { - return $class->$method($self->_format, @args); + foreach my $o ( keys %opts ) { + if ( defined $opts{$o} ) { + $options{$o} = $opts{$o}; + } } } - return $self->fail( - 'Unable to ' . $type . ' [' . $self->_action . ']' ); - } + if ( $options{action} eq 'entry' && @{ $options{extra} } ) { + $options{action} .= q{_} . lc shift @{ $options{extra} }; + } + + push @{ $options{extra} }, @options; - sub Dump { - my ( $self, @args ) = @_; - return $self->_handle_representation( 'dump', @args ); - } + my $list = $self->_todo->file( $options{list} ); - sub _handle_action { - my ( $self, $method, $params ) = @_; - - if ( exists $action_handlers{$method}{ $self->_action } ) { - my $a = $action_handlers{$method}{ $self->_action }; - return $self->$a( $self->_args, $params ); + my $suffix = $self->_suffix || q{}; + if ( $list !~ / \Q$suffix\E $/ixms ) { + $list .= $suffix; } - return $self->fail( - 'Unable to handle ' . $method . ' [' . $self->_action . ']' ); - } + $options{list} = $list; - sub GET { - my ( $self, @args ) = @_; - return $self->_handle_action( 'GET', @args ); - } - - sub get_entry { - my ( $self, $key ) = @_; - - if ( !$key ) { - return $self->fail("get_entry requires arguments"); + if ( $options{format} ) { + $format_of{ ident $self } = $options{format}; + delete $options{format}; } - elsif ( ref $key eq 'ARRAY' ) { - my @entries; - foreach ( @{$key} ) { - push @entries, $self->get_entry($_); + + my @method; + foreach my $o qw( method action ) { + if ( $options{$o} ) { + push @method, $options{$o}; } - return @entries; } + $method = join q{_}, @method; - my @list = $self->get_list; + return $method, %options; + } - my $entry; - if ( $key =~ /^[[:xdigit:]]{32}$/xms ) { - my $search = lc $key; + sub _handle_action { + my ( $self, @args ) = @_; - ENTRY: foreach my $e (@list) { - if ( $search eq $e->{md5} ) { - $entry = $e; - last ENTRY; - } + my ( $method, %options ) = $self->_parse_options(@args); + + my $todo = $self->_todo; + $todo->load( $options{list} ); + + foreach my $class ( $self->actions ) { + if ( $class->can($method) ) { + return Text::Todo::REST::API::Response->new( + { type => $options{action}, + format => $self->_format, + data => $class->$method( $todo, \%options ), + } + ); } } - elsif ( $key =~ /^\d+$/xms ) { - $entry = $list[ $key - 1 ]; - } - if ( !$entry ) { - return $self->fail("Unable to find entry!"); - } - - return $entry; + return $self->fail( 'Unable to handle [' . $method . ']' ); } - sub get_list { - my ($self) = @_; + sub _split_path { + my ( $self, $path ) = @_; - my $line = 1; - return map ( { - line => $line++, - md5 => md5_hex( $_->text ), - text => $_->text, - }, - $self->_todo->list ); - } + my %options = ( + action => undef, + list => undef, + entry => undef, + extra => [], + ); - sub get_files { - my ($self) = @_; - my $dir = $self->_todo->file('todo_dir'); + $path = defined $path ? $path : q{}; + $path =~ s{^/}{}xms; - if ( !$dir ) { - return $self->fail('Unable to find todo_dir'); + if ( $path =~ s/\.(\w+)$//xms ) { + $options{format} = $1; } - my $file_regex = $self->_file_regex; + ( $options{list}, $options{action}, + @{ $options{extra} } ) = split '/', $path; - opendir my $dh, $dir or croak "Couldn't opendir: $!"; - my @files = grep {m/$file_regex/xms} readdir $dh; - closedir $dh; + if (!defined $options{action}) { + if ( $options{list} ) { + $options{action} = 'list'; + } + else { + $options{action} = 'files'; + } + } - return @files; - } + $options{action} = lc( $options{action} ); - sub get_tags { - my ( $self, $tag ) = @_; - my $ident = ident($self); + if ($options{action} eq 'entry' + || $options{action} eq 'tags' + ) { + $options{ $options{action} } = shift @{ $options{extra} }; + } - return $todo_of{$ident}->listtag($tag); + return %options; } - sub POST { - my ( $self, @args ) = @_; - return $self->_handle_action( 'POST', @args ); - } + sub GET { return shift->_handle_action( 'GET', @_ ) } + sub POST { return shift->_handle_action( 'POST', @_ ) } + sub PUT { return shift->_handle_action( 'PUT', @_ ) } + sub DELETE { return shift->_handle_action( 'DELETE', @_ ) } - sub PUT { - my ( $self, @args ) = @_; - return $self->_handle_action( 'PUT', @args ); - } - - sub DELETE { - my ( $self, @args ) = @_; - return $self->_handle_action( 'DELETE', @args ); - } - sub fail { my ( $self, @message ) = @_; croak(@message); @@ -246,11 +209,7 @@ sub _todo { my ($self) = @_; return $todo_of{ ident $self }; } sub _suffix { my ($self) = @_; return $suffix_of{ ident $self}; } sub _file_regex { my ($self) = @_; return $file_regex_of{ ident $self}; } - sub _user { my ($self) = @_; return $user_of{ ident $self}; } - sub _list { my ($self) = @_; return $list_of{ ident $self}; } - sub _action { my ($self) = @_; return $action_of{ ident $self}; } sub _format { my ($self) = @_; return $format_of{ ident $self}; } - sub _args { my ($self) = @_; return $args_of{ ident $self}; } sub DESTROY { my ($self) = @_;