File Coverage

lib/Dancer/Plugin/Chain.pm
Criterion Covered Total %
statement 14 16 87.5
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 20 22 90.9


line stmt bran cond sub pod time code
1             package Dancer::Plugin::Chain;
2             BEGIN {
3 1     1   330605 $Dancer::Plugin::Chain::AUTHORITY = 'cpan:YANICK';
4             }
5             # ABSTRACT: Chained actions for Dancer
6             $Dancer::Plugin::Chain::VERSION = '0.1.0';
7 1     1   9 use strict;
  1         2  
  1         26  
8 1     1   6 use warnings;
  1         1  
  1         25  
9              
10 1     1   6 use Dancer ':syntax';
  1         11  
  1         7  
11 1     1   1251 use Dancer::Plugin;
  1         1368  
  1         142  
12              
13             register chain => sub {
14             my $link = Dancer::Plugin::Chain::Link->new( args => [ @_ ] );
15            
16             return wantarray ? $link->as_route : $link;
17             };
18              
19             register_plugin;
20              
21             package
22             Dancer::Plugin::Chain::Link;
23              
24 1     1   175293 use Moose;
  0            
  0            
25              
26             has "path_segments" => (
27             traits => [ qw/ Array /],
28             isa => 'ArrayRef',
29             is => 'ro',
30             default => sub { [] },
31             handles => {
32             add_to_path => 'push',
33             all_path_segments => 'elements'
34             },
35             );
36              
37             sub path {
38             my $self = shift;
39             return join '', $self->all_path_segments;
40             }
41              
42             has code_blocks => (
43             traits => [ qw/ Array /],
44             isa => 'ArrayRef',
45             is => 'ro',
46             default => sub { [] },
47             handles => {
48             add_to_code => 'push',
49             all_code_blocks => 'elements'
50             },
51             );
52              
53             sub code {
54             my $self = shift;
55              
56             my @code = $self->all_code_blocks;
57             return sub {
58             my $result;
59             $result = $_->(@_) for @code;
60             return $result;
61             }
62             }
63              
64             sub BUILD {
65             my $self = shift;
66             my @args = @{ $_[0]{args} };
67              
68             my $code;
69             $code = pop @args if ref $args[-1] eq 'CODE';
70              
71             for my $segment ( @args ) {
72             if ( ref $segment eq __PACKAGE__ ) {
73             $self->add_to_path( $segment->all_path_segments );
74             $self->add_to_code( $segment->all_code_blocks );
75             }
76             elsif( ref $segment eq 'CODE' ) {
77             $self->add_to_code($segment);
78             }
79             else {
80             $self->add_to_path( $segment );
81             }
82             }
83              
84             $self->add_to_code($code) if $code;
85             }
86              
87             sub as_route {
88             my $self = shift;
89              
90             return ( $self->path, $self->code );
91             }
92              
93             __PACKAGE__->meta->make_immutable;
94              
95             1;
96              
97             __END__
98              
99             =pod
100              
101             =head1 NAME
102              
103             Dancer::Plugin::Chain - Chained actions for Dancer
104              
105             =head1 VERSION
106              
107             version 0.1.0
108              
109             =head1 SYNOPSIS
110              
111             use Dancer;
112             use Dancer::Plugin::Chain;
113              
114             my $country = chain '/country/:country' => sub {
115             # silly example. Typically much more work would
116             # go on in here
117             var 'site' => param('country');
118             };
119              
120             my $event = chain '/event/:event' => sub {
121             var 'event' => param('event');
122             };
123              
124             # will match /country/usa/event/yapc
125             get chain $country, $event, '/schedule' => sub {
126             return sprintf "schedule of %s in %s\n", map { var $_ }
127             qw/ event site /;
128             };
129              
130             my $continent = chain '/continent/:continent' => sub {
131             var 'site' => param('continent');
132             };
133              
134             my $continent_event = chain $continent, $event;
135              
136             # will match /continent/europe/event/yapc
137             get chain $continent_event, '/schedule' => sub {
138             return sprintf "schedule of %s in %s\n", map { var $_ } qw/ event site /;
139             };
140              
141             # will match /continent/asia/country/japan/event/yapc
142             # and will do special munging in-between!
143              
144             get chain $continent,
145             sub { var temp => var 'site' },
146             $country,
147             sub {
148             var 'site' => join ', ', map { var $_ } qw/ site temp /
149             },
150             $event,
151             '/schedule'
152             => sub {
153             return sprintf "schedule of %s in %s\n", map { var $_ }
154             qw/ event site /;
155             };
156              
157             =head1 DESCRIPTION
158              
159             Implementation of Catalyst-like chained routes.
160              
161             The plugin exports a single keyword, C<chain>, which creates the chained
162             routes.
163              
164             =head2 KNOWN CAVEATS
165              
166             The plugin does not support C<prefix> yet, and only support string-based urls
167             (so no regexes).
168              
169             =head1 EXPORTED FUNCTIONS
170              
171             =head2 chain @chain_items, $coderef
172              
173             Create a chain out of the items provided, and assign it the final action coderef.
174              
175             Each chain item can be
176             a string representing a path segment, a previously defined chain or an
177             anonymous function. The chain's final path and action will be the aggregate of
178             its parts.
179              
180             For example, the final route declaration of the SYNOPSIS,
181              
182             get chain $continent,
183             sub { var temp => var 'site' },
184             $country,
185             sub {
186             var 'site' => join ', ', map { var $_ } qw/ site temp /
187             },
188             $event,
189             '/schedule'
190             => sub {
191             return sprintf "schedule of %s in %s\n", map { var $_ }
192             qw/ event site /;
193             };
194              
195             would be is equivalent to
196              
197             get '/continent/:continent/country/:country/event/:event/schedule' => sub {
198             var 'site' => param('continent');
199             var temp => var 'site';
200             var 'site' => param('country');
201             var 'site' => join ', ', map { var $_ } qw/ site temp /
202             var 'event' => param('event');
203              
204             return sprintf "schedule of %s in %s\n", map { var $_ }
205             qw/ event site /;
206             }
207              
208             In scalar context, C<chain> returns its underlying object.
209             In list context, it returns a route / action pair of values (). That's how it
210             can work transparently with C<get>, C<post> and friends.
211              
212             # returns the object, that can be used to forge longer chains.
213             my $foo_chain = chain '/foo', sub { ... };
214              
215             # returns the pair that makes 'get' happy
216             get chain $foo_chain;
217              
218             =head1 SEE ALSO
219              
220             =over
221              
222             =item *
223              
224             Original blog entry: L<http://techblog.babyl.ca/entry/dancer-in-chains>
225              
226             =item *
227              
228             L<Dancer-Plugin-Dispatcher>
229              
230             =back
231              
232             =head1 AUTHOR
233              
234             Yanick Champoux <yanick@babyl.dyndns.org>
235              
236             =head1 COPYRIGHT AND LICENSE
237              
238             This software is copyright (c) 2014 by Yanick Champoux.
239              
240             This is free software; you can redistribute it and/or modify it under
241             the same terms as the Perl 5 programming language system itself.
242              
243             =cut