· 8 years ago · Apr 09, 2018, 02:50 AM
1diff -ruN router-anymoose/.shipit router-caf/.shipit
2--- router-anymoose/.shipit 2010-02-23 16:13:48.000000000 +0900
3+++ router-caf/.shipit 2010-02-23 16:15:20.000000000 +0900
4@@ -1,7 +1,3 @@
5-steps = FindVersion, ChangeVersion, CheckChangeLog, Manifest, DistTest, Commit, Tag, MakeDist, UploadCPAN, Twitter, DistClean
6-
7+steps = FindVersion, ChangeVersion, CheckChangeLog, Manifest, DistTest, Commit, Tag, MakeDist, UploadCPAN, DistClean
8 git.tagpattern = %v
9 git.push_to = origin
10-
11-twitter.config = ~/.pit/twitter.yaml
12-
13diff -ruN router-anymoose/Changes router-caf/Changes
14--- router-anymoose/Changes 2010-02-23 16:13:48.000000000 +0900
15+++ router-caf/Changes 2010-02-23 16:15:20.000000000 +0900
16@@ -1,12 +1,4 @@
17 Revision history for Perl extension HTTP::Router
18
19-0.03
20- - fixed dependencies
21-
22-0.02
23- - switched from Mouse to Any::Moose
24- - moved DSL route definitions to HTTP::Router::Declare
25- - added Test::HTTP::Router
26-
27 0.01
28 - original version
29diff -ruN router-anymoose/Makefile.PL router-caf/Makefile.PL
30--- router-anymoose/Makefile.PL 2010-02-23 16:13:48.000000000 +0900
31+++ router-caf/Makefile.PL 2010-02-23 16:15:20.000000000 +0900
32@@ -2,22 +2,13 @@
33 name 'HTTP-Router';
34 all_from 'lib/HTTP/Router.pm';
35
36-requires 'Any::Moose' => '0.10';
37-requires_any_moose prefer => 'Mouse', mouse => '0.21';
38-requires_any_moose 'X::AttributeHelpers', prefer => 'Mouse', mouse => '0.03';
39-
40-# Core
41-requires 'Hash::AsObject';
42+requires 'Class::Accessor::Fast';
43+requires 'Hash::Merge';
44 requires 'List::MoreUtils';
45-requires 'URI::Template::Restrict' => '0.03';
46 requires 'Scalar::Util' => '1.14';
47-# Declare
48-requires 'Carp';
49-requires 'Storable';
50-requires 'Devel::Caller::Perl';
51-requires 'String::CamelCase';
52 requires 'Lingua::EN::Inflect::Number';
53-# Debug
54+requires 'String::CamelCase';
55+requires 'URI::Template::Restrict' => '0.03';
56 requires 'Text::SimpleTable';
57
58 tests 't/*.t t/*/*.t';
59diff -ruN router-anymoose/lib/HTTP/Router/Debug.pm router-caf/lib/HTTP/Router/Debug.pm
60--- router-anymoose/lib/HTTP/Router/Debug.pm 2010-02-23 16:13:48.000000000 +0900
61+++ router-caf/lib/HTTP/Router/Debug.pm 2010-02-23 16:15:20.000000000 +0900
62@@ -1,13 +1,25 @@
63 package HTTP::Router::Debug;
64
65-use Any::Moose '::Role';
66+use strict;
67+use warnings;
68 use Text::SimpleTable;
69-use HTTP::Router;
70
71-requires 'routes';
72+our @EXPORT = qw(routing_table draw_routing_table);
73
74-sub show_table {
75- my $table = $_[0]->routing_table->draw;
76+sub import {
77+ # TODO: into package
78+ my $into = 'HTTP::Router';
79+ eval "require $into; 1" or die $@;
80+
81+ no strict 'refs';
82+ no warnings 'redefine';
83+ for my $keyword (@EXPORT) {
84+ *{ $into . '::' . $keyword } = \&{ __PACKAGE__ . '::' . $keyword };
85+ }
86+}
87+
88+sub draw_routing_table {
89+ my $table = shift->routing_table->draw;
90 print "$table\n";
91 }
92
93@@ -36,10 +48,6 @@
94 return $table;
95 }
96
97-# apply self to HTTP::Router
98-__PACKAGE__->meta->apply(HTTP::Router->meta);
99-
100-no Any::Moose '::Role';
101 1;
102
103 =head1 NAME
104@@ -55,17 +63,13 @@
105
106 print $router->routing_table->draw;
107 # or
108- $router->show_table;
109+ $router->draw_routing_table;
110
111 =head1 METHODS
112
113 =head2 routing_table
114
115-Returns a Text::SimpleTable object for routing information.
116-
117-=head2 show_table
118-
119-Constructs and Prints a table for routing information.
120+=head2 draw_routing_table
121
122 =head1 AUTHOR
123
124diff -ruN router-anymoose/lib/HTTP/Router/Declare.pm router-caf/lib/HTTP/Router/Declare.pm
125--- router-anymoose/lib/HTTP/Router/Declare.pm 2010-02-23 16:13:48.000000000 +0900
126+++ router-caf/lib/HTTP/Router/Declare.pm 1970-01-01 09:00:00.000000000 +0900
127@@ -1,310 +0,0 @@
128-package HTTP::Router::Declare;
129-
130-use strict;
131-use warnings;
132-use Carp 'croak';
133-use Storable 'dclone';
134-use Devel::Caller::Perl 'called_args';
135-use String::CamelCase 'decamelize';
136-use Lingua::EN::Inflect::Number 'to_S';
137-use HTTP::Router;
138-use HTTP::Router::Route;
139-
140-sub import {
141- my $caller = caller;
142-
143- no strict 'refs';
144- no warnings 'redefine';
145-
146- *{ $caller . '::router' } = \&routing;
147- *{ $caller . '::routes' } = \&routing; # alias router
148-
149- # lexical bindings
150- *{ $caller . '::match' } = sub { goto &match };
151- *{ $caller . '::with' } = sub { goto &with };
152- *{ $caller . '::to' } = sub ($) { goto &to };
153- *{ $caller . '::then' } = sub (&) { goto &then };
154- # resource(s)
155- *{ $caller . '::resource' } = sub { goto &resource };
156- *{ $caller . '::resources' } = sub { goto &resources };
157-}
158-
159-sub _stub {
160- my $name = shift;
161- return sub { croak "Can't call $name() outside routing block" };
162-}
163-
164-{
165- my @Declarations = qw(match with to then resource resources);
166- for my $keyword (@Declarations) {
167- no strict 'refs';
168- *$keyword = _stub $keyword;
169- }
170-}
171-
172-sub routing (&) {
173- my $block = shift;
174- my $router = HTTP::Router->new;
175-
176- if ($block) {
177- no warnings 'redefine';
178-
179- local *match = create_match($router);
180- local *with = create_with($router);
181- local *to = sub { params => $_[0] };
182- local *then = sub { $_[0] };
183-
184- local *resource = create_resource($router);
185- local *resources = create_resources($router);
186-
187- my $root = HTTP::Router::Route->new;
188- $block->($root);
189- }
190-
191- return $router;
192-}
193-
194-sub _map {
195- my ($router, $block, %args) = @_;
196-
197- my $route = dclone called_args(1)->[0];
198- $route->append_path($args{path}) if exists $args{path};
199- $route->add_conditions(%{ $args{conditions} }) if exists $args{conditions};
200- $route->add_params(%{ $args{params} }) if exists $args{params};
201-
202- return defined $block ? $block->($route) : $router->add_route($route);
203-}
204-
205-sub create_match {
206- my $router = shift;
207- return sub {
208- my $block = ref $_[-1] eq 'CODE' ? pop : undef;
209- my %args = ();
210- $args{path} = shift unless ref $_[0];
211- $args{conditions} = shift if ref $_[0] eq 'HASH';
212- _map $router, $block, %args, @_;
213- };
214-}
215-
216-sub create_with {
217- my $router = shift;
218- return sub {
219- my $block = ref $_[-1] eq 'CODE' ? pop : undef;
220- _map $router, $block, params => @_;
221- };
222-}
223-
224-{
225- my $Resource = {
226- collection => {},
227- member => {
228- create => { method => 'POST', suffix => '', action => 'create' },
229- show => { method => 'GET', suffix => '', action => 'show' },
230- update => { method => 'PUT', suffix => '', action => 'update' },
231- destroy => { method => 'DELETE', suffix => '', action => 'destroy' },
232- new => { method => 'GET', suffix => '/new', action => 'post' },
233- edit => { method => 'GET', suffix => '/edit', action => 'edit' },
234- delete => { method => 'GET', suffix => '/delete', action => 'delete' },
235- },
236- };
237- sub _resource_collection { $Resource->{collection} }
238- sub _resource_member { $Resource->{member} }
239-
240- my $Resources = {
241- collection => {
242- index => { method => 'GET', suffix => '', action => 'index' },
243- create => { method => 'POST', suffix => '', action => 'create' },
244- new => { method => 'GET', suffix => '/new', action => 'post' },
245- },
246- member => {
247- show => { method => 'GET', suffix => '', action => 'show' },
248- update => { method => 'PUT', suffix => '', action => 'update' },
249- destroy => { method => 'DELETE', suffix => '', action => 'destroy' },
250- edit => { method => 'GET', suffix => '/edit', action => 'edit' },
251- delete => { method => 'GET', suffix => '/delete', action => 'delete' },
252- },
253- };
254- sub _resources_collection { $Resources->{collection} }
255- sub _resources_member { $Resources->{member} }
256-}
257-
258-sub _map_resources {
259- my ($router, $args) = @_;
260-
261- for my $symbol (qw'collection member') {
262- while (my ($key, $config) = each %{ $args->{$symbol} }) {
263- $config = { method => $config } unless ref $config;
264-
265- my $action = exists $config->{action} ? $config->{action} : $key;
266- my $suffix = exists $config->{suffix} ? $config->{suffix} : "/$action";
267- my $prefix = $args->{"${symbol}_prefix"};
268-
269- my $path = $prefix . $suffix;
270- my $conditions = { method => $config->{method} };
271- my $params = { controller => $args->{controller}, action => $action };
272-
273- my $formatted_route = HTTP::Router::Route->new(
274- path => "${path}.{format}",
275- conditions => $conditions,
276- params => $params,
277- );
278- $router->add_route($formatted_route);
279-
280- my $route = HTTP::Router::Route->new(
281- path => $path,
282- conditions => $conditions,
283- params => $params,
284- );
285- $router->add_route($route);
286- }
287- }
288-}
289-
290-sub _create_resources {
291- my ($router, $name, $block, $args) = @_;
292-
293- my %only = map { $_ => 1 } @{ $args->{only} || [] };
294- my %except = map { $_ => 1 } @{ $args->{except} || [] };
295-
296- for my $symbol (qw'collection member') {
297- my $extra = delete $args->{$symbol}; # save extra maps
298-
299- no strict 'refs';
300- my $default = exists $args->{singleton} ? &{"_resource_$symbol"}() : &{"_resources_$symbol"}();
301-
302- if (exists $args->{only}) {
303- $args->{$symbol} = {
304- map { $_ => $default->{$_} } grep { $only{$_} } keys %$default
305- };
306- }
307- elsif (exists $args->{except}) {
308- $args->{$symbol} = {
309- map { $_ => $default->{$_} } grep { !$except{$_} } keys %$default
310- };
311- }
312- else {
313- $args->{$symbol} = $default;
314- }
315-
316- $args->{$symbol} = { %{ $args->{$symbol} }, %$extra } if defined $extra;
317- }
318-
319- my $decamelized = decamelize $name;
320- my $singular = to_S $decamelized;
321-
322- $args->{collection_prefix} = called_args(1)->[0]->path .
323- (exists $args->{path_prefix} ? $args->{path_prefix} : "/$decamelized");
324- $args->{member_prefix} = $args->{collection_prefix} .
325- (exists $args->{singleton} ? '' : "/{${singular}_id}");
326-
327- $args->{controller} ||= $name;
328-
329- _map_resources($router, $args);
330-
331- if (defined $block) {
332- my $route = HTTP::Router::Route->new(path => $args->{member_prefix});
333- $block->($route);
334- }
335-}
336-
337-sub create_resource {
338- my $router = shift;
339- return sub {
340- my $block = ref $_[-1] eq 'CODE' ? pop : undef;
341- my $name = shift;
342- my $args = shift || {};
343- $args->{singleton} = 1;
344- _create_resources $router, $name, $block, $args;
345- };
346-}
347-
348-sub create_resources {
349- my $router = shift;
350- return sub {
351- my $block = ref $_[-1] eq 'CODE' ? pop : undef;
352- my $name = shift;
353- my $args = shift || {};
354- _create_resources $router, $name, $block, $args;
355- };
356-}
357-
358-1;
359-
360-=head1 NAME
361-
362-HTTP::Router::Declare
363-
364-=head1 SYNOPSIS
365-
366- use HTTP::Router::Declare;
367-
368- my $router = router {
369- # path and params
370- match '/' => to { controller => 'Root', action => 'index' };
371-
372- # path, conditions, and params
373- match '/home', { method => 'GET' }
374- => to { controller => 'Home', action => 'show' };
375- match '/date/{year}', { year => qr/^\d{4}$/ }
376- => to { controller => 'Date', action => 'by_year' };
377-
378- # path, params, and nesting
379- match '/account' => to { controller => 'Account' } => then {
380- match '/login' => to { action => 'login' };
381- match '/logout' => to { action => 'logout' };
382- };
383-
384- # path nesting
385- match '/account' => then {
386- match '/signup' => to { controller => 'Users', action => 'register' };
387- match '/logout' => to { controller => 'Account', action => 'logout' };
388- };
389-
390- # conditions nesting
391- match { method => 'GET' } => then {
392- match '/search' => to { controller => 'Items', action => 'search' };
393- match '/tags' => to { controller => 'Tags', action => 'index' };
394- };
395-
396- # params nesting
397- with { controller => 'Account' } => then {
398- match '/login' => to { action => 'login' };
399- match '/logout' => to { action => 'logout' };
400- match '/signup' => to { action => 'signup' };
401- };
402-
403- # match only
404- match '/{controller}/{action}/{id}.{format}';
405- match '/{controller}/{action}/{id}';
406- };
407-
408-=head1 METHODS
409-
410-=head2 router $block
411-
412-=head2 match $path?, $conditions?
413-
414-=head2 to $params
415-
416-=head2 with $params
417-
418-=head2 then $block
419-
420-=head2 resources $name
421-
422-=head2 resource $name
423-
424-=head1 AUTHOR
425-
426-NAKAGAWA Masaki E<lt>masaki@cpan.orgE<gt>
427-
428-=head1 LICENSE
429-
430-This library is free software; you can redistribute it and/or modify
431-it under the same terms as Perl itself.
432-
433-=head1 SEE ALSO
434-
435-L<HTTP::Router>, L<HTTP::Router::Route>
436-
437-=cut
438diff -ruN router-anymoose/lib/HTTP/Router/Mapper.pm router-caf/lib/HTTP/Router/Mapper.pm
439--- router-anymoose/lib/HTTP/Router/Mapper.pm 1970-01-01 09:00:00.000000000 +0900
440+++ router-caf/lib/HTTP/Router/Mapper.pm 2010-02-23 16:15:20.000000000 +0900
441@@ -0,0 +1,182 @@
442+package HTTP::Router::Mapper;
443+
444+use strict;
445+use warnings;
446+use base 'Class::Accessor::Fast';
447+use Carp 'croak';
448+use Hash::Merge 'merge';
449+use HTTP::Router::Route;
450+
451+__PACKAGE__->mk_ro_accessors('router');
452+__PACKAGE__->mk_accessors(qw'path params conditions route');
453+
454+sub new {
455+ my ($class, $args) = @_;
456+
457+ $args->{path} ||= '';
458+ $args->{params} ||= {};
459+ $args->{conditions} ||= {};
460+
461+ return bless $args, $class;
462+}
463+
464+sub _clone_mapper {
465+ my ($self, %params) = @_;
466+
467+ for my $key (qw'path params conditions') {
468+ $params{$key} = $self->$key unless exists $params{$key};
469+ }
470+
471+ my $class = ref $self || $self;
472+ return $class->new({ %params, router => $self->router });
473+}
474+
475+sub _freeze_route {
476+ my $self = shift;
477+
478+ my $route = HTTP::Router::Route->new({
479+ path => $self->path,
480+ params => $self->params,
481+ conditions => $self->conditions,
482+ });
483+
484+ $self->router->add_route($route);
485+ $self->route($route);
486+
487+ return $self;
488+}
489+
490+sub match {
491+ my $self = shift;
492+ croak 'route has already been committed' if $self->route;
493+
494+ # TODO: parameterize
495+ my $block = ref $_[-1] eq 'CODE' ? pop : undef;
496+ my ($path, $conditions) = @_;
497+ croak '$path or $conditions is required' unless $path or $conditions;
498+
499+ my %extra = (
500+ $path ? (path => $self->path . $path) : (),
501+ $conditions ? (conditions => merge($conditions, $self->conditions)) : (),
502+ );
503+ my $mapper = $self->_clone_mapper(%extra);
504+
505+ if ($block) {
506+ local $_ = $mapper;
507+ $block->($_);
508+ }
509+
510+ return $mapper;
511+}
512+
513+sub to {
514+ my $self = shift;
515+ croak 'route has already been committed' if $self->route;
516+
517+ # TODO: parameterize
518+ my $block = ref $_[-1] eq 'CODE' ? pop : undef;
519+ my $params = shift;
520+ croak '$params is required' unless $params;
521+
522+ $self->params(merge($params, $self->params));
523+
524+ if ($block) {
525+ local $_ = $self;
526+ $block->($_);
527+ }
528+ else {
529+ $self->_freeze_route;
530+ }
531+
532+ return $self;
533+}
534+
535+sub with {
536+ my $self = shift;
537+ croak 'route has already been committed' if $self->route;
538+
539+ # TODO: parameterize
540+ my $block = ref $_[-1] eq 'CODE' ? pop : undef;
541+ my $params = shift;
542+ croak '$params and $block are required' unless $params and $block;
543+
544+ local $_ = $self->_clone_mapper(params => merge($params, $self->params));
545+ $block->($_);
546+
547+ return $_;
548+}
549+
550+sub register {
551+ my $self = shift;
552+ croak 'route has already been committed' if $self->route;
553+ return $self->_freeze_route;
554+}
555+
556+# TODO: not implemented yet
557+#sub namespace {}
558+#sub name {}
559+
560+1;
561+
562+=for stopwords params
563+
564+=head1 NAME
565+
566+HTTP::Router::Mapper
567+
568+=head1 SYNOPSIS
569+
570+ my $router = HTTP::Router->define(sub {
571+ $_->match('/index.{format}')
572+ ->to({ controller => 'Root', action => 'index' });
573+
574+ $_->match('/archives/{year}', { year => qr/\d{4}/ })
575+ ->to({ controller => 'Archive', action => 'by_month' });
576+
577+ $_->match('/account/login', { method => ['GET', 'POST'] })
578+ ->to({ controller => 'Account', action => 'login' });
579+
580+ $_->with({ controller => 'Account' }, sub {
581+ $_->match('/account/signup')->to({ action => 'signup' });
582+ $_->match('/account/logout')->to({ action => 'logout' });
583+ });
584+
585+ $_->match('/')->register;
586+ });
587+
588+=head1 METHODS
589+
590+=head2 match($path?, $conditions?, $block?)
591+
592+=head2 to($params, $block?)
593+
594+=head2 with($params, $block)
595+
596+=head2 register
597+
598+=head1 PROPERTIES
599+
600+=head2 path
601+
602+=head2 conditions
603+
604+=head2 params
605+
606+=head2 route
607+
608+=head2 router
609+
610+=head1 AUTHOR
611+
612+NAKAGAWA Masaki E<lt>masaki@cpan.orgE<gt>
613+
614+=head1 LICENSE
615+
616+This library is free software; you can redistribute it and/or modify
617+it under the same terms as Perl itself.
618+
619+=head1 SEE ALSO
620+
621+L<HTTP::Router>, L<HTTP::Router::Route>
622+
623+=cut
624diff -ruN router-anymoose/lib/HTTP/Router/Match.pm router-caf/lib/HTTP/Router/Match.pm
625--- router-anymoose/lib/HTTP/Router/Match.pm 2010-02-23 16:13:48.000000000 +0900
626+++ router-caf/lib/HTTP/Router/Match.pm 2010-02-23 16:15:20.000000000 +0900
627@@ -1,55 +1,33 @@
628 package HTTP::Router::Match;
629
630-use Any::Moose;
631+use strict;
632+use warnings;
633+use base 'Class::Accessor::Fast';
634
635-has 'params' => (
636- is => 'ro',
637- isa => 'HashRef',
638- lazy => 1,
639- default => sub { +{} },
640-);
641-
642-has 'captures' => (
643- is => 'ro',
644- isa => 'HashRef',
645- lazy => 1,
646- default => sub { +{} },
647-);
648-
649-has 'route' => (
650- is => 'ro',
651- isa => 'HTTP::Router::Route',
652- handles => ['uri_for'],
653- required => 1,
654-);
655+__PACKAGE__->mk_ro_accessors(qw'params captures route');
656
657-no Any::Moose;
658-__PACKAGE__->meta->make_immutable;
659+sub uri_for { shift->route->uri_for(@_) }
660+
661+1;
662+
663+=for stopwords params
664
665 =head1 NAME
666
667-HTTP::Router::Match - Matched Object Representation for HTTP::Router
668+HTTP::Router::Match
669
670 =head1 METHODS
671
672-=head2 uri_for($args?)
673-
674-Returns a route path which is processed with parameters.
675+=head2 uri_for($captures?)
676
677 =head1 PROPERTIES
678
679 =head2 params
680
681-Route parameters which was matched.
682-
683 =head2 captures
684
685-Captured variable parameters which was matched.
686-
687 =head2 route
688
689-L<HTTP::Router::Route> object which was matched.
690-
691 =head1 AUTHOR
692
693 NAKAGAWA Masaki E<lt>masaki@cpan.orgE<gt>
694diff -ruN router-anymoose/lib/HTTP/Router/Resources.pm router-caf/lib/HTTP/Router/Resources.pm
695--- router-anymoose/lib/HTTP/Router/Resources.pm 1970-01-01 09:00:00.000000000 +0900
696+++ router-caf/lib/HTTP/Router/Resources.pm 2010-02-23 16:15:20.000000000 +0900
697@@ -0,0 +1,164 @@
698+package HTTP::Router::Resources;
699+
700+use strict;
701+use warnings;
702+use Carp 'croak';
703+use Hash::Merge 'merge';
704+use Lingua::EN::Inflect::Number qw(to_S to_PL);
705+use String::CamelCase qw(camelize decamelize);
706+
707+our @EXPORT = qw(resources resource);
708+
709+sub import {
710+ # TODO: into package
711+ my $into = 'HTTP::Router::Mapper';
712+ eval "require $into; 1" or croak $@;
713+
714+ no strict 'refs';
715+ no warnings 'redefine';
716+ for my $keyword (@EXPORT) {
717+ *{ $into . '::' . $keyword } = \&{ __PACKAGE__ . '::' . $keyword };
718+ }
719+}
720+
721+sub resources {
722+ my $self = shift;
723+
724+ my $block = ref $_[-1] eq 'CODE' ? pop : undef;
725+ my ($name, $args) = @_;
726+
727+ my $path = decamelize $name;
728+ my $params = { controller => $args->{controller} || camelize(to_PL($path)) };
729+ my $singular = $args->{singular} || to_S($path);
730+ my $id = "${singular}_id";
731+
732+ my $collections = merge($args->{collection} || {}, {
733+ index => { method => 'GET', path => '' },
734+ create => { method => 'POST', path => '' },
735+ post => { method => 'GET', path => '/new' },
736+ });
737+
738+ my $members = merge($args->{member} || {}, {
739+ show => { method => 'GET', path => '' },
740+ update => { method => 'PUT', path => '' },
741+ destroy => { method => 'DELETE', path => '' },
742+ edit => { method => 'GET', path => '/edit' },
743+ });
744+
745+ $self->match("/${path}")->to($params, sub {
746+ # collections
747+ while (my ($action, $args) = each %$collections) {
748+ my $path = ref $args eq 'HASH' ? $args->{path} : "/${action}";
749+ my $method = ref $args eq 'HASH' ? $args->{method} : $args;
750+ my $conditions = { method => $method };
751+ my $params = { action => $action };
752+ $_[0]->match("${path}.{format}", $conditions)->to($params);
753+ $_[0]->match("${path}", $conditions)->to($params);
754+ }
755+
756+ # members
757+ $_[0]->match("/{$id}", sub {
758+ while (my ($action, $args) = each %$members) {
759+ my $path = ref $args ? $args->{path} : "/${action}";
760+ my $method = ref $args ? $args->{method} : $args;
761+ my $conditions = { method => $method };
762+ my $params = { action => $action };
763+ $_[0]->match("${path}.{format}", $conditions)->to($params);
764+ $_[0]->match("${path}", $conditions)->to($params);
765+ }
766+ });
767+ });
768+
769+ if ($block) {
770+ local $_ = $self->_clone_mapper(path => $self->path . "/${path}/{$id}");
771+ $block->($_);
772+ }
773+
774+ $self;
775+}
776+
777+sub resource {
778+ my $self = shift;
779+
780+ my $block = ref $_[-1] eq 'CODE' ? pop : undef;
781+ my ($name, $args) = @_;
782+
783+ my $path = decamelize $name;
784+ my $params = { controller => $args->{controller} || camelize($path) };
785+
786+ my $members = merge($args->{member} || {}, {
787+ create => { method => 'POST', path => '' },
788+ show => { method => 'GET', path => '' },
789+ update => { method => 'PUT', path => '' },
790+ destroy => { method => 'DELETE', path => '' },
791+ post => { method => 'GET', path => '/new' },
792+ edit => { method => 'GET', path => '/edit' },
793+ });
794+
795+ $self->match("/${path}")->to($params, sub {
796+ # members
797+ while (my ($action, $args) = each %$members) {
798+ my $path = ref $args eq 'HASH' ? $args->{path} : "/${action}";
799+ my $method = ref $args eq 'HASH' ? $args->{method} : $args;
800+ my $conditions = { method => $method };
801+ my $params = { action => $action };
802+ $_[0]->match("${path}.{format}", $conditions)->to($params);
803+ $_[0]->match("${path}", $conditions)->to($params);
804+ }
805+ });
806+
807+ if ($block) {
808+ local $_ = $self->_clone_mapper(path => $self->path . "/${path}");
809+ $block->($_);
810+ }
811+
812+ $self;
813+}
814+
815+1;
816+
817+=head1 NAME
818+
819+HTTP::Router::Resources
820+
821+=head1 SYNOPSIS
822+
823+ use HTTP::Router;
824+ use HTTP::Router::Resources;
825+
826+ my $router = HTTP::Router->define(sub {
827+ $_->resources('users');
828+
829+ $_->resource('account');
830+
831+ $_->resources('members', sub {
832+ $_->resources('articles');
833+ });
834+
835+ $_->resources('members', {
836+ controller => 'Users',
837+ collection => { login => 'GET' },
838+ member => { settings => 'GET' },
839+ });
840+ });
841+
842+=head1 METHODS
843+
844+=head2 resources($name, $args?, $block?)
845+
846+=head2 resource($name, $args?, $block?)
847+
848+=head1 AUTHOR
849+
850+NAKAGAWA Masaki E<lt>masaki@cpan.orgE<gt>
851+
852+=head1 LICENSE
853+
854+This library is free software; you can redistribute it and/or modify
855+it under the same terms as Perl itself.
856+
857+=head1 SEE ALSO
858+
859+L<HTTP::Router::Mapper>, L<HTTP::Router>
860+
861+=cut
862diff -ruN router-anymoose/lib/HTTP/Router/Route.pm router-caf/lib/HTTP/Router/Route.pm
863--- router-anymoose/lib/HTTP/Router/Route.pm 2010-02-23 16:13:48.000000000 +0900
864+++ router-caf/lib/HTTP/Router/Route.pm 2010-02-23 16:15:20.000000000 +0900
865@@ -1,100 +1,83 @@
866 package HTTP::Router: