version 1.7, 2010/01/18 13:47:53 |
version 1.10, 2010/01/24 04:17:39 |
|
|
package Text::Todo::REST::API; |
package Text::Todo::REST::API; |
|
|
# $AFresh1: API.pm,v 1.6 2010/01/18 03:51:40 andrew Exp $ |
# $AFresh1: API.pm,v 1.9 2010/01/23 07:15:40 andrew Exp $ |
|
|
use warnings; |
use warnings; |
use strict; |
use strict; |
use Carp; |
use Carp; |
|
|
use Data::Dumper; |
|
use Text::Todo; |
use Text::Todo; |
|
use Text::Todo::REST::API::Response; |
|
|
use Class::Std::Utils; |
use Class::Std::Utils; |
use Module::Pluggable |
|
require => 1, |
|
search_path => __PACKAGE__ . '::Representations', |
|
sub_name => 'representations'; |
|
use Digest::MD5 qw/ md5_hex /; |
use Digest::MD5 qw/ md5_hex /; |
|
|
use version; our $VERSION = qv('0.0.1'); |
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 @attr_refs = \( |
my %todo_of, |
my %todo_of, |
|
|
my %suffix_of, |
my %suffix_of, |
my %file_regex_of, |
my %file_regex_of, |
|
|
my %user_of, |
my %format_of, |
my %list_of, |
|
my %action_of, |
|
my %args_of, |
|
|
|
my %action_handlers, |
my %action_handlers, |
); |
); |
|
|
sub new { |
sub new { |
my ( $class, $options ) = @_; |
my ( $class, $options ) = @_; |
|
|
my $format = $options->{default_format}; |
|
if ( $options->{format} ) { |
|
$format = $options->{format}; |
|
} |
|
elsif ($options->{path_info} |
|
&& $options->{path_info} =~ s/\.(\w+)$//xms ) |
|
{ |
|
$format = $1; |
|
} |
|
|
|
my $self = bless anon_scalar(), $class; |
my $self = bless anon_scalar(), $class; |
my $ident = ident($self); |
my $ident = ident($self); |
|
|
if ( ref $self eq __PACKAGE__ && $format ) { |
$format_of{$ident} = $options->{default_format}; |
my $found_handler = 0; |
if ( $options->{format} ) { |
REP: foreach my $rep ( $self->representations ) { |
$format_of{$ident} = $options->{format}; |
if ( $rep->_handles($format) ) { |
|
$self = $rep->new($options); |
|
$found_handler = 1; |
|
last REP; |
|
} |
|
} |
|
if ( !$found_handler ) { |
|
croak("Unable to find handler for [$format]\n"); |
|
} |
|
} |
} |
|
|
$suffix_of{$ident} = $options->{suffix} || '.txt'; |
$suffix_of{$ident} = $options->{suffix} || '.txt'; |
|
|
$file_regex_of{$ident} = $options->{file_regex} || qr{ |
$file_regex_of{$ident} = $options->{file_regex} || qr{ |
.* |
.* |
|
|
$ |
$ |
}ixms; |
}ixms; |
|
|
$options->{path_info} ||= q{}; |
eval { |
$options->{path_info} =~ s{^/}{}xms; |
$todo_of{$ident} = Text::Todo->new( |
( $list_of{$ident}, $action_of{$ident}, @{ $args_of{$ident} }, |
{ todo_dir => $options->{todo_dir}, |
) = split '/', $options->{path_info}; |
todo_file => $options->{todo_file}, |
|
} |
|
); |
|
}; |
|
if ($@) { |
|
$self->fail( 'Unable to create Text::Todo object' . $@ ); |
|
} |
|
|
if ( $list_of{$ident} ) { |
return $self; |
$action_of{$ident} ||= 'list'; |
} |
|
|
|
sub _parse_options { |
|
my ( $self, $method, @args ) = @_; |
|
|
|
my %options = ( |
|
method => lc $method, |
|
list => '', |
|
action => 'files', |
|
args => [], |
|
); |
|
|
|
if (@args) { |
|
if ( !ref $args[0] ) { |
|
$options{path} = shift @args; |
|
} |
|
|
|
if ( ref $args[0] eq 'HASH' ) { |
|
my $opts = shift @args; |
|
foreach my $o ( keys %{$opts} ) { |
|
$options{$o} = $opts->{$o}; |
|
} |
|
} |
} |
} |
else { |
|
$action_of{$ident} = 'files'; |
if ( exists $options{path} ) { |
|
my %opts = $self->_split_path( $options{path} ); |
|
delete $options{path}; |
|
|
|
foreach my $o ( keys %opts ) { |
|
if ( defined $opts{$o} ) { |
|
$options{$o} = $opts{$o}; |
|
} |
|
} |
} |
} |
|
|
eval { $todo_of{$ident} = Text::Todo->new( |
if ( $options{action} eq 'entry' && @{ $options{args} } ) { |
{ todo_dir => $options->{todo_dir}, |
$options{entry} = shift @{ $options{args} }; |
todo_file => $options->{todo_file}, |
if ( @{ $options{args} } ) { |
|
$options{action} = lc shift @{ $options{args} }; |
} |
} |
) }; |
|
if ($@) { |
|
$self->fail('Unable to create Text::Todo object' . $@); |
|
} |
} |
|
|
$todo_of{$ident}->load('todo_file') |
push @{ $options{args} }, @args; |
or $self->fail('Unable to load todo_file in Text::Todo object'); |
|
|
|
return $self; |
$options{list} |
} |
= defined $options{list} ? $options{list} : 'todo_file'; |
|
|
sub RegisterActionHandler { |
if ( $options{format} ) { |
my ( $handler, @types ) = @_; |
$format_of{ ident $self } = $options{format}; |
|
delete $options{format}; |
|
} |
|
|
foreach my $type (@types) { |
my @method; |
$action_handlers{$handler}{ $type->[0] } = $type->[1]; |
foreach my $o qw( method action ) { |
|
if ( $options{$o} ) { |
|
push @method, $options{$o}; |
|
} |
} |
} |
|
$method = join q{_}, @method; |
|
|
return 1; |
return $method, %options; |
} |
} |
|
|
sub content_type {return} |
sub _handle_action { |
|
my ( $self, @args ) = @_; |
|
|
sub Dump { |
my ( $method, %options ) = $self->_parse_options(@args); |
my ($self) = @_; |
|
return $self->fail( 'Unable to Dump [' . $self->_action . ']' ); |
|
} |
|
|
|
sub Load { |
my $todo = $self->_todo; |
my ($self) = @_; |
$todo->load( $options{list} ); |
return $self->fail( 'Unable to Load [' . $self->_action . ']' ); |
|
|
foreach my $class ($self) { |
|
if ( $class->can($method) ) { |
|
my @data = $class->$method( $todo, \%options ); |
|
|
|
return Text::Todo::REST::API::Response->new({ |
|
type => $options{action}, |
|
format => $self->_format, |
|
data => \@data, |
|
}); |
|
} |
|
} |
|
|
|
return $self->fail( 'Unable to handle [' . $method . ']' ); |
} |
} |
|
|
sub _handle_action { |
sub _split_path { |
my ( $self, $method, $params ) = @_; |
my ( $self, $path ) = @_; |
|
|
if ( exists $action_handlers{$method}{ $self->_action } ) { |
my %options = ( |
my $a = $action_handlers{$method}{ $self->_action }; |
list => undef, |
return $self->$a( $self->_args, $params ); |
action => undef, |
|
args => [], |
|
); |
|
|
|
$path = defined $path ? $path : q{}; |
|
$path =~ s{^/}{}xms; |
|
|
|
if ( $path =~ s/\.(\w+)$//xms ) { |
|
$options{format} = $1; |
} |
} |
|
|
return $self->fail( |
( $options{list}, $options{action}, @{ $options{args} } ) = split '/', |
'Unable to handle ' . $method . ' [' . $self->_action . ']' ); |
$path; |
|
|
|
if ( $options{list} ) { |
|
$options{action} ||= 'list'; |
|
|
|
my $suffix = $self->_suffix; |
|
|
|
if ( ( lc $options{list} ) eq 'files' ) { |
|
$options{action} = lc $options{list}; |
|
$options{list} = q{}; |
|
} |
|
elsif ( $self->_todo->file( $options{list} ) ) { |
|
$options{list} = $self->_todo->file( $options{list} ); |
|
} |
|
} |
|
|
|
if ( @{ $options{args} } && ( lc $options{args}[0] ) eq 'entry' ) { |
|
$options{action} = lc shift @{ $options{args} }; |
|
} |
|
|
|
return %options; |
} |
} |
|
|
sub GET { |
sub GET { |
|
|
} |
} |
|
|
sub get_entry { |
sub get_entry { |
my ( $self, $key ) = @_; |
my ( $self, $todo, $key ) = @_; |
|
|
if ( !$key ) { |
if ( !$key ) { |
return $self->fail("get_entry requires arguments"); |
return $self->fail("get_entry requires arguments"); |
|
|
} |
} |
return @entries; |
return @entries; |
} |
} |
|
elsif ( ref $key eq 'HASH' ) { |
|
if ( exists $key->{entry} ) { |
|
$key = $key->{entry}; |
|
} |
|
else { |
|
return $self->fail('get_entry requires key [entry]'); |
|
} |
|
} |
|
|
my @list = $self->get_list; |
my @list = $self->get_list($todo); |
|
|
my $entry; |
my $entry; |
if ( $key =~ /^[[:xdigit:]]{32}$/xms ) { |
if ( $key =~ /^[[:xdigit:]]{32}$/xms ) { |
|
|
} |
} |
|
|
sub get_list { |
sub get_list { |
my ($self) = @_; |
my ( $self, $todo ) = @_; |
|
|
my $line = 1; |
my $line = 1; |
return map ( { |
return map ( { |
|
|
md5 => md5_hex( $_->text ), |
md5 => md5_hex( $_->text ), |
text => $_->text, |
text => $_->text, |
}, |
}, |
$self->_todo->list ); |
$todo->list ); |
} |
} |
|
|
sub get_files { |
sub get_files { |
my ($self) = @_; |
my ( $self, $todo ) = @_; |
my $dir = $self->_todo->file('todo_dir'); |
my $dir = $todo->file('todo_dir'); |
|
|
if ( !$dir ) { |
if ( !$dir ) { |
return $self->fail('Unable to find todo_dir'); |
return $self->fail('Unable to find todo_dir'); |
|
|
} |
} |
|
|
sub get_tags { |
sub get_tags { |
my ( $self, $tag ) = @_; |
my ( $self, $todo, $tag ) = @_; |
my $ident = ident($self); |
return $todo->listtag($tag); |
|
|
return $todo_of{$ident}->listtag($tag); |
|
} |
} |
|
|
sub POST { |
sub POST { |
|
|
sub _todo { my ($self) = @_; return $todo_of{ ident $self }; } |
sub _todo { my ($self) = @_; return $todo_of{ ident $self }; } |
sub _suffix { my ($self) = @_; return $suffix_of{ ident $self}; } |
sub _suffix { my ($self) = @_; return $suffix_of{ ident $self}; } |
sub _file_regex { my ($self) = @_; return $file_regex_of{ ident $self}; } |
sub _file_regex { my ($self) = @_; return $file_regex_of{ ident $self}; } |
sub _user { my ($self) = @_; return $user_of{ ident $self}; } |
sub _format { my ($self) = @_; return $format_of{ ident $self}; } |
sub _list { my ($self) = @_; return $list_of{ ident $self}; } |
|
sub _action { my ($self) = @_; return $action_of{ ident $self}; } |
|
sub _args { my ($self) = @_; return $args_of{ ident $self}; } |
|
|
|
sub DESTROY { |
sub DESTROY { |
my ($self) = @_; |
my ($self) = @_; |