File Coverage

blib/lib/Plack/Middleware/Return/MultiLevel.pm
Criterion Covered Total %
statement 25 27 92.5
branch 4 4 100.0
condition 1 2 50.0
subroutine 9 10 90.0
pod 3 3 100.0
total 42 46 91.3


line stmt bran cond sub pod time code
1 2     2   226616 use strict;
  2         3  
  2         73  
2 2     2   11 use warnings;
  2         5  
  2         122  
3              
4             package Plack::Middleware::Return::MultiLevel;
5              
6             our $VERSION = "0.002";
7 2     2   10 use base 'Plack::Middleware';
  2         9  
  2         1668  
8 2     2   35636 use Plack::Util::Accessor 'level_name';
  2         5  
  2         10  
9 2     2   2331 use Return::MultiLevel;
  2         9933  
  2         1039  
10              
11             sub PSGI_KEY () { 'Plack.Middleware.Return.MultiLevel.return_to' }
12              
13             sub DEFAULT_LEVEL_NAME() { 'default' }
14              
15             sub _return_level {
16 3     3   5 my ($env, $level_name, @returning) = @_;
17 3   50     14 my $returns_to = $env->{+PSGI_KEY}->{$level_name} ||
18             die "'$level_name' not found, cannot return to it!";
19              
20 3         8 $returns_to->(@returning);
21             }
22              
23             sub prepare_app {
24 3 100   3 1 846 $_[0]->level_name(DEFAULT_LEVEL_NAME)
25             unless(defined $_[0]->level_name);
26             }
27              
28             sub call {
29 7     7 1 82977 my ($self, $env) = @_;
30             return Return::MultiLevel::with_return {
31 7     7   621 my ($return_to) = @_;
32 7 100       64 my $new_env = +{
33             %$env,
34 7         43 +PSGI_KEY, +{ %{$env->{+PSGI_KEY}||{}}, $self->level_name => $return_to },
35             }; # make a shallow copy
36              
37 7         155 $self->app->($new_env);
38 7         55 };
39             }
40              
41             sub return {
42 0     0 1   my ($self, $env, @returning) = @_;
43 0           return _return_level($env, $self->level_name, @returning);
44             }
45              
46             =head1 TITLE
47            
48             Plack::Middleware::Return::MultiLevel - Escape a PSGI app from anywhere in the stack
49            
50             =head1 SYNOPSIS
51              
52             use Plack::Builder;
53             use Plack::Middleware::Return::MultiLevel::Utils
54             qw/return_to_level return_to_default_level/;
55              
56             my $app = builder {
57             enable "Return::MultiLevel";
58              
59             mount "/default" => sub {
60             my $env = shift;
61             return_to_default_level($env, [200, ['Content-Type', 'text/plain'], ['default']]);
62             };
63              
64             mount '/layers' => builder {
65             enable "Return::MultiLevel", level_name=>'one';
66             enable "Return::MultiLevel", level_name=>'two';
67              
68             mount '/one' => sub {
69             my $env = shift;
70             return_to_level($env, 'one', [200, ['Content-Type', 'text/plain'], ['one']]);
71             };
72              
73             mount '/two' => sub {
74             my $env = shift;
75             return_to_level($env, 'two', [200, ['Content-Type', 'text/plain'], ['two']]);
76             };
77              
78             };
79              
80             };
81              
82             =head1 DESCRIPTION
83              
84             Sometimes when in a PSGI application you want an easy way to escape out of the
85             current callstack. For example you might wish to immediately end processing and
86             return a 'NotFound' or 'ServerError' type response. In those cases you might
87             use the core middleware L, which allows you
88             to throw an exception object that matches a duck type (has methods C and
89             C or C). That middleware wraps everything in an eval and
90             looks for exception objects of that type, and converts them to a response.
91              
92             L is an alternative approach to solving
93             this problem. Instead of throwing an exception, it uses L
94             to set a 'callback' point that you can jump to at any time. If you don't like
95             using exceptions for control flow, or you have code that does a lot of exception
96             catching, you might prefer this approach.
97              
98             Unlike L you don't need to return an object
99             matching a ducktype, you can just return any standard, acceptable PSGI response.
100              
101             =head1 CONSTANTS
102              
103             This class defines the following constants
104              
105             =head2 PSGI_KEY
106              
107             PSGI environment key under which your return callback are stored.
108              
109             =head2 DEFAULT_LEVEL_NAME
110              
111             The default level name used if you choose not to explicitly name your return
112             level target.
113            
114             =head1 METHODS
115            
116             This class defines the following methods.
117              
118             =head2 prepare_app
119              
120             Sets instance defaults
121            
122             =head2 call
123            
124             Used by plack to call the middleware
125              
126             =head2 return
127              
128             my $mw = Plack::Middleware::Return::MultiLevel->new;
129              
130             #...
131              
132             $mw->return([200, ['Content-Type', 'text/plain'], ['returned']]);
133              
134             Returns to the callpoint set by L. You should pass this
135             args suitable for a PSGI response.
136              
137             Since the return callback is also stored in the C<$psgi_env>, you are more
138             likely to use methods from L
139             rather than storing the middleware object.
140              
141             =head1 AUTHOR
142            
143             John Napiorkowski L
144            
145             =head1 SEE ALSO
146              
147             L, L, L,
148             L
149            
150             =head1 COPYRIGHT & LICENSE
151            
152             Copyright 2014, John Napiorkowski L
153            
154             This library is free software; you can redistribute it and/or modify it under
155             the same terms as Perl itself.
156              
157             =cut
158              
159             1;