File Coverage

blib/lib/HTTP/Engine/Middleware.pm
Criterion Covered Total %
statement 148 153 96.7
branch 22 24 91.6
condition 5 6 83.3
subroutine 32 35 91.4
pod 0 5 0.0
total 207 223 92.8


line stmt bran cond sub pod time code
1             package HTTP::Engine::Middleware;
2 20     20   615296 use 5.00800;
  20         55  
3 20     20   1176 use Any::Moose;
  20         63138  
  20         95  
4             use Any::Moose (
5 20         74 '::Util' => [qw/apply_all_roles/],
6 20     20   7463 );
  20         30  
7             our $VERSION = '0.11_01';
8              
9 20     20   3814 use Carp ();
  20         27  
  20         2340  
10              
11             has 'middlewares' => (
12             is => 'ro',
13             isa => 'ArrayRef',
14             default => sub { +[] },
15             );
16              
17             has '_instance_of' => (
18             is => 'rw',
19             isa => 'HashRef',
20             default => sub { +{} },
21             );
22              
23             has '_instance_ary_ex' => (
24             is => 'rw',
25             isa => 'ArrayRef',
26             default => sub { +[] },
27             );
28              
29             has 'method_class' => (
30             is => 'ro',
31             isa => 'Str',
32             );
33              
34             has 'diecatch' => (
35             is => 'rw',
36             isa => 'Bool',
37             );
38              
39             sub init_class {
40 20     20 0 32 my $klass = shift;
41 20         84 my $meta = any_moose('::Meta::Class')->initialize($klass);
42 20 50       1488 $meta->superclasses(any_moose('::Object'))
43             unless $meta->superclasses;
44              
45 20     20   87 no strict 'refs';
  20         30  
  20         556  
46 20     20   70 no warnings 'redefine';
  20         25  
  20         3030  
47 20     0   1362 *{ $klass . '::meta' } = sub { $meta };
  20         108  
  0         0  
48             }
49              
50             sub import {
51 40     40   2258 my($class, ) = @_;
52 40         77 my $caller = caller;
53              
54 40 100       1772 return unless $caller =~ /(?:\:)?Middleware\:\:.+/;
55              
56 20         116 strict->import;
57 20         196 warnings->import;
58              
59 20         55 init_class($caller);
60              
61 20 100       66 if (Any::Moose::is_moose_loaded()) {
62 1         231 Moose->import({ into_level => 1 });
63             } else {
64 19         4599 Mouse->export_to_level( 1 );
65             }
66              
67 20     20   84 no strict 'refs';
  20         29  
  20         674  
68 20         135 *{"$caller\::__MIDDLEWARE__"} = sub {
69 20     20   79 use strict;
  20         23  
  20         3634  
70 20     20   49 my $caller = caller(0);
71 20         56 __MIDDLEWARE__($caller);
72 20         8717 };
73              
74 20     16   68 *{"$caller\::before_handle"} = sub (&) { goto \&before_handle };
  20         78  
  16         356  
75 20     13   138 *{"$caller\::after_handle"} = sub (&) { goto \&after_handle };
  20         91  
  13         40  
76 20     7   56 *{"$caller\::middleware_method"} = sub { goto \&middleware_method };
  20         1299  
  7         29  
77             }
78              
79             sub __MIDDLEWARE__ {
80 20     20   31 my ( $caller, ) = @_;
81              
82 20         70 Any::Moose::unimport;
83 20         2887 apply_all_roles( $caller, 'HTTP::Engine::Middleware::Role' );
84              
85 20         26860 $caller->meta->make_immutable( inline_destructor => 1 );
86 20         1575 "MIDDLEWARE";
87             }
88              
89             BEGIN {
90 20     20   305 no strict 'refs';
  20         23  
  20         1370  
91 20     20   49 for my $meth (
92             qw(before_handle after_handle middleware_method)
93             )
94             {
95 60         5659 *{__PACKAGE__ . "::$meth"} = sub {
96 0     0   0 Carp::croak("Can't call ${meth} function outside Middleware's load phase");
97 60         141 };
98             }
99             };
100              
101             # this method's return value is indefinite.
102             sub install {
103 59     59 0 405 my($self, @middlewares) = @_;
104              
105 59         173 my $args = $self->_build_args(@middlewares);
106 59         178 $self->_create_middleware_instance($args);
107             }
108              
109             # this module accepts
110             # $mw->install(qw/HTTP::Engine::Middleware::Foo/);
111             # and
112             # $mw->install('HTTP::Engine::Middleware::Foo' => { arg1 => 'foo'});
113             sub _build_args {
114 59     59   85 my $self = shift;
115              
116             # basis of Data::OptList
117 59         72 my @middlewares;
118 59         90 my $max = scalar(@_);
119 59         222 for (my $i = 0; $i < $max ; $i++) {
120 62 100 100     378 if ($i + 1 < $max && ref($_[$i + 1])) {
121 50         247 push @middlewares, [ $_[$i++] => $_[$i] ];
122             } else {
123 12         47 push @middlewares, [ $_[$i] => {} ];
124             }
125             }
126              
127 59         125 return \@middlewares;
128             }
129              
130             # load & create middleware instance
131             my %IS_INITIALIZED;
132             sub _create_middleware_instance {
133 59     59   89 my ($self, $args) = @_;
134              
135 59         76 my %instances;
136 59         132 for my $stuff (@$args) {
137 62         118 my $klass = $stuff->[0];
138 62         87 my $config = $stuff->[1];
139              
140 62 100       208 unless ($IS_INITIALIZED{$klass}++) {
141 20         54 $self->_init_middleware_class($klass);
142             }
143              
144 62         455 my $instance = $klass->new(
145             %$config,
146             before_handles => [$klass->_before_handles()],
147             after_handles => [$klass->_after_handles() ],
148             );
149              
150 62         244 push @{ $self->_instance_ary_ex }, $instance;
  62         224  
151 62         90 push @{ $self->middlewares }, $klass;
  62         211  
152 62         79 push @{ $self->_instance_of->{$klass} }, $instance;
  62         615  
153             }
154             }
155              
156             # load one middleware 'class'
157             sub _init_middleware_class {
158 20     20   35 my ($self, $klass,) = @_;
159              
160 20         29 my @before_handles;
161             my @after_handles;
162              
163 20     20   85 no warnings 'redefine';
  20         22  
  20         2742  
164              
165 20     16   111 local *before_handle = sub { push @before_handles, @_ };
  16         43  
166 20     13   90 local *after_handle = sub { push @after_handles, @_ };
  13         26  
167             local *middleware_method = sub {
168 7     7   12 my($method, $code) = @_;
169 7         23 my $method_class = $self->method_class;
170 7 100       35 if ($method =~ /^(.+)\:\:([^\:]+)$/) {
171 3         14 ($method_class, $method) = ($1, $2);
172             }
173 7 100       21 return unless $method_class;
174              
175 20     20   85 no strict 'refs';
  20         33  
  20         1443  
176 6         7 *{"$klass\::$method"} = $code;
  6         28  
177 6         8 *{"$method_class\::$method"} = $code;
  6         34  
178 20         88 };
179              
180 20         88 Any::Moose::load_class($klass);
181              
182 20     20   74 no strict 'refs';
  20         22  
  20         6677  
183 20     62   153 *{"${klass}::_before_handles"} = sub () { @before_handles };
  20         103  
  62         250  
184 20     62   281 *{"${klass}::_after_handles"} = sub () { @after_handles };
  20         273  
  62         631  
185             }
186              
187             sub is_class_loaded {
188 0     0 0 0 my $class = shift;
189 0         0 return Any::Moose::is_class_loaded($class);
190             }
191              
192             sub instance_of {
193 1     1 0 7 my($self, $name) = @_;
194 1         11 my $stuff = $self->_instance_of->{$name};
195 1 50       8 return wantarray ? @{$stuff} : $stuff->[0];
  0         0  
196             }
197              
198             sub handler {
199 58     58 0 46892 my($self, $handle) = @_;
200              
201             sub {
202 64     64   13824 my $req = shift;
203              
204 64         85 my $res;
205             my @run_middlewares;
206             LOOP:
207 64         89 for my $instance (@{ $self->_instance_ary_ex }) {
  64         262  
208 65         73 for my $code (@{ $instance->before_handles }) {
  65         218  
209 58         298 my $ret = $code->($self, $instance, $req);
210 45 100       2252 if ($ret->isa('HTTP::Engine::Response')) {
211 19         24 $res = $ret;
212 19         54 last LOOP;
213             }
214 26         55 $req = $ret;
215             }
216 33         65 push @run_middlewares, $instance;
217             }
218 51         61 my $msg;
219 51 100       129 unless ($res) {
220 32         144 $self->diecatch(0);
221 32         36 local $@;
222 32         43 eval { $res = $handle->($req) };
  32         94  
223 32 100 66     5694 $msg = $@ if !$self->diecatch && $@;
224             }
225 51 100       117 die $msg if $msg;
226 50         92 for my $instance (reverse @run_middlewares) {
227 33         95 for my $code (reverse @{ $instance->after_handles }) {
  33         110  
228 14         44 $res = $code->($self, $instance, $req, $res);
229             }
230             }
231              
232 50         148 $res;
233 58         940 };
234             }
235              
236             1;
237             __END__
238              
239             =for stopwords Daisuke Maki dann hidek marcus nyarla API middlewares
240              
241             =encoding utf8
242              
243             =head1 NAME
244              
245             HTTP::Engine::Middleware - middlewares distribution
246              
247             =head1 WARNING! WARNING!
248              
249             THIS MODULE IS IN ITS ALPHA QUALITY. THE API MAY CHANGE IN THE FUTURE
250              
251             =head1 SYNOPSIS
252              
253             simply
254              
255             my $mw = HTTP::Engine::Middleware->new;
256             $mw->install(qw/ HTTP::Engine::Middleware::DebugScreen HTTP::Engine::Middleware::ReverseProxy /);
257             HTTP::Engine->new(
258             interface => {
259             module => 'YourFavoriteInterfaceHere',
260             request_handler => $mw->handler( \&handler ),
261             }
262             )->run();
263              
264             method injection middleware
265              
266             my $mw = HTTP::Engine::Middleware->new({ method_class => 'HTTP::Engine::Request' });
267             $mw->install(qw/ HTTP::Engine::Middleware::DebugScreen HTTP::Engine::Middleware::ReverseProxy /);
268             HTTP::Engine->new(
269             interface => {
270             module => 'YourFavoriteInterfaceHere',
271             request_handler => $mw->handler(sub {
272             my $req = shift;
273             HTTP::Engine::Response->new( body => $req->mobile_attribute );
274             })
275             }
276             )->run();
277              
278             =head1 DESCRIPTION
279              
280             HTTP::Engine::Middleware is official middlewares distribution of HTTP::Engine.
281              
282             =head1 WISHLIST
283              
284             Authentication
285              
286             OpenID
287              
288             mod_rewrite ( someone write :p )
289              
290             and more ideas
291              
292             =head1 AUTHOR
293              
294             Kazuhiro Osawa E<lt>ko@yappo.ne.jpE<gt>
295              
296             Daisuke Maki
297              
298             Tokuhiro Matsuno E<lt>tokuhirom@gmail.comE<gt>
299              
300             nyarla
301              
302             marcus
303              
304             hidek
305              
306             walf443
307              
308             Takatoshi Kitano E<lt>techmemo@gmail.com<gt>
309              
310             =head1 SEE ALSO
311              
312             L<HTTP::Engine>
313              
314             =head1 REPOSITORY
315              
316             svn co http://svn.coderepos.org/share/lang/perl/HTTP-Engine-Middleware/trunk HTTP-Engine-Middleware
317              
318             HTTP::Engine::Middleware's Subversion repository is hosted at L<http://coderepos.org/share/>.
319             patches and collaborators are welcome.
320              
321             =head1 LICENSE
322              
323             This library is free software; you can redistribute it and/or modify
324             it under the same terms as Perl itself.
325              
326             =cut