File Coverage

blib/lib/Dancer.pm
Criterion Covered Total %
statement 236 285 82.8
branch 56 78 71.7
condition 15 36 41.6
subroutine 90 107 84.1
pod 67 67 100.0
total 464 573 80.9


line stmt bran cond sub pod time code
1             package Dancer;
2             our $AUTHORITY = 'cpan:SUKRIA';
3             #ABSTRACT: lightweight yet powerful web application framework
4             $Dancer::VERSION = '1.3514_04'; # TRIAL
5             $Dancer::VERSION = '1.351404';
6 166     166   6074643 use strict;
  166         1182  
  166         4318  
7 166     166   749 use warnings;
  166         266  
  166         3758  
8 166     166   781 use Carp;
  166         295  
  166         10387  
9 166     166   954 use Cwd 'realpath';
  166         318  
  166         6457  
10              
11 166     166   59576 use Dancer::App;
  166         431  
  166         4171  
12 166     166   927 use Dancer::Config;
  166         290  
  166         5254  
13 166     166   757 use Dancer::Cookies;
  166         279  
  166         2772  
14 166     166   715 use Dancer::FileUtils;
  166         282  
  166         4700  
15 166     166   66713 use Dancer::GetOpt;
  166         420  
  166         5231  
16 166     166   65219 use Dancer::Error;
  166         449  
  166         4233  
17 166     166   64794 use Dancer::Hook;
  166         426  
  166         3553  
18 166     166   921 use Dancer::Logger;
  166         344  
  166         2636  
19 166     166   693 use Dancer::Renderer;
  166         300  
  166         2443  
20 166     166   680 use Dancer::Route;
  166         318  
  166         2450  
21 166     166   62327 use Dancer::Serializer::JSON;
  166         379  
  166         4361  
22 166     166   58672 use Dancer::Serializer::YAML;
  166         378  
  166         4112  
23 166     166   59138 use Dancer::Serializer::XML;
  166         379  
  166         4095  
24 166     166   58707 use Dancer::Serializer::Dumper;
  166         370  
  166         4056  
25 166     166   882 use Dancer::Session;
  166         314  
  166         2691  
26 166     166   673 use Dancer::SharedData;
  166         281  
  166         2680  
27 166     166   59581 use Dancer::Handler;
  166         427  
  166         4684  
28 166     166   967 use Dancer::MIME;
  166         336  
  166         3396  
29 166     166   752 use Dancer::Exception qw(:all);
  166         310  
  166         15356  
30              
31 166     166   1033 use Dancer::Continuation::Halted;
  166         353  
  166         3254  
32 166     166   71749 use Dancer::Continuation::Route::Forwarded;
  166         386  
  166         4372  
33 166     166   55915 use Dancer::Continuation::Route::Passed;
  166         384  
  166         4031  
34 166     166   57720 use Dancer::Continuation::Route::ErrorSent;
  166         379  
  166         4029  
35 166     166   61147 use Dancer::Continuation::Route::FileSent;
  166         386  
  166         3959  
36 166     166   58577 use Dancer::Continuation::Route::Templated;
  166         374  
  166         3832  
37              
38 166     166   886 use File::Spec;
  166         303  
  166         2627  
39 166     166   695 use Scalar::Util;
  166         274  
  166         4978  
40              
41 166     166   754 use base 'Exporter';
  166         297  
  166         485801  
42              
43             our @EXPORT = qw(
44             after
45             any
46             before
47             before_template
48             cookie
49             cookies
50             config
51             content_type
52             dance
53             dancer_version
54             debug
55             del
56             dirname
57             info
58             error
59             engine
60             false
61             forward
62             from_dumper
63             from_json
64             from_yaml
65             from_xml
66             get
67             halt
68             header
69             headers
70             hook
71             layout
72             load
73             load_app
74             logger
75             mime
76             options
77             param
78             param_array
79             params
80             pass
81             path
82             patch
83             post
84             prefix
85             push_header
86             put
87             redirect
88             render_with_layout
89             request
90             send_file
91             send_error
92             set
93             setting
94             set_cookie
95             session
96             splat
97             status
98             start
99             template
100             to_dumper
101             to_json
102             to_yaml
103             to_xml
104             true
105             upload
106             captures
107             uri_for
108             var
109             vars
110             warning
111             );
112              
113             # Dancer's syntax
114              
115             sub after {
116 0     0 1 0 Dancer::Deprecation->deprecated(reason => "use hooks!",
117             version => '1.3080',
118             fatal => 0);
119 0         0 Dancer::Hook->new('after', @_);
120             }
121             sub before {
122 0     0 1 0 Dancer::Deprecation->deprecated(reason => "use hooks!",
123             version => '1.3080',
124             fatal => 0);
125 0         0 Dancer::Hook->new('before', @_);
126             }
127             sub before_template {
128 0     0 1 0 Dancer::Deprecation->deprecated(reason => "use hooks!",
129             version => '1.3080',
130             fatal => 0);
131 0         0 Dancer::Hook->new('before_template', @_);
132             }
133              
134 24     24 1 765 sub any { Dancer::App->current->registry->any_add(@_) }
135 2     2 1 18 sub captures { Dancer::SharedData->request->params->{captures} }
136 4     4 1 935 sub cookie { Dancer::Cookies->cookie( @_ ) }
137 7     7 1 794 sub cookies { Dancer::Cookies->cookies }
138 17     17 1 1067 sub config { Dancer::Config::settings() }
139 22     22 1 98 sub content_type { Dancer::SharedData->response->content_type(@_) }
140 0     0 1 0 sub dance { goto &start }
141 0     0 1 0 sub dancer_version { Dancer->VERSION }
142 5     5 1 50 sub debug { goto &Dancer::Logger::debug }
143 2     2 1 24 sub del { Dancer::App->current->registry->universal_add('delete', @_) }
144 12     12 1 4293 sub dirname { Dancer::FileUtils::dirname(@_) }
145 32     32 1 121 sub engine { Dancer::Engine->engine(@_) }
146 6     6 1 29 sub error { goto &Dancer::Logger::error }
147 0     0 1 0 sub false { 0 }
148 15     15 1 68 sub forward { Dancer::SharedData->response->forward(@_);
149             # throw a special continuation exception
150 15         57 Dancer::Continuation::Route::Forwarded->new->throw;
151             }
152 0     0 1 0 sub from_dumper { Dancer::Serializer::Dumper::from_dumper(@_) }
153 13     13 1 48692 sub from_json { Dancer::Serializer::JSON::from_json(@_) }
154 0     0 1 0 sub from_xml { Dancer::Serializer::XML::from_xml(@_) }
155 8     8 1 21065 sub from_yaml { Dancer::Serializer::YAML::from_yaml(@_) }
156 640     640 1 29706 sub get { map { my $r = $_; Dancer::App->current->registry->universal_add($r, @_) } qw(head get) }
  1279         1731  
  1279         2920  
157 11     11 1 182 sub halt { Dancer::SharedData->response->halt(@_);
158             # throw a special continuation exception
159 11         81 Dancer::Continuation::Halted->new->throw;
160             }
161 8     8 1 191 sub header { goto &headers }
162 0     0 1 0 sub info { goto &Dancer::Logger::info }
163 9     9 1 215 sub push_header { Dancer::SharedData->response->push_header(@_); }
164 15     15 1 103 sub headers { Dancer::SharedData->response->headers(@_); }
165 115     115 1 33348 sub hook { Dancer::Hook->new(@_) }
166             sub layout {
167 0     0 1 0 Dancer::Deprecation->deprecated(reason => "use 'set layout => \"value\"'",
168             version => '1.3050',
169             fatal => 1);
170             }
171 1     1 1 848 sub load { require $_ for @_ }
172 9     9 1 3623 sub load_app { goto &_load_app } # goto doesn't add a call frame. So caller() will work as expected
173             sub logger {
174 0     0 1 0 Dancer::Deprecation->deprecated(reason => "use 'set logger => \"value\"'",
175             fatal => 1,version=>'1.3050');
176             }
177 8     8 1 2886 sub mime { Dancer::MIME->instance() }
178 1     1 1 10 sub options { Dancer::App->current->registry->universal_add('options', @_) }
179 255     255 1 7210 sub params { Dancer::SharedData->request->params(@_) }
180 13     13 1 63 sub param { params->{$_[0]} }
181             sub param_array {
182 6     6 1 22 my $value = param(shift);
183              
184 6 100       20 my @array = ref $value eq 'ARRAY' ? @$value
    100          
185             : defined $value ? ( $value )
186             : ()
187             ;
188              
189 6         19 return @array;
190             }
191 78     78 1 381 sub pass { Dancer::SharedData->response->pass(1);
192             # throw a special continuation exception
193 78         385 Dancer::Continuation::Route::Passed->new->throw;
194             }
195 0     0 1 0 sub patch { Dancer::App->current->registry->universal_add('patch', @_) }
196 225     225 1 3826 sub path { Dancer::FileUtils::path(@_) }
197 72     72 1 2799 sub post { Dancer::App->current->registry->universal_add('post', @_) }
198 32 100   32 1 1251 sub prefix { @_ == 0 ? Dancer::App->current->get_prefix :
199             Dancer::App->current->set_prefix(@_) }
200 20     20 1 165 sub put { Dancer::App->current->registry->universal_add('put', @_) }
201 19     19 1 141 sub redirect { goto &_redirect }
202 0     0 1 0 sub render_with_layout { Dancer::Template::Abstract->_render_with_layout(@_) }
203 221     221 1 876 sub request { Dancer::SharedData->request }
204 11   100 11 1 129 sub send_error { Dancer::Continuation::Route::ErrorSent->new(
205             return_value => Dancer::Error->new(
206             message => $_[0],
207             code => $_[1] || 500)->render()
208             )->throw }
209             #sub send_file { goto &_send_file }
210 9     9 1 30 sub send_file { Dancer::Continuation::Route::FileSent->new(
211             return_value => _send_file(@_)
212             )->throw
213             }
214 97     97 1 35524 sub set { goto &setting }
215 8     8 1 1106 sub set_cookie { Dancer::Cookies->set_cookie(@_) }
216 1375 100   1375 1 35761 sub setting { Dancer::App->applications ? Dancer::App->current->setting(@_) : Dancer::Config::setting(@_) }
217 18     18 1 96 sub session { goto &_session }
218 21 50   21 1 106 sub splat { @{ Dancer::SharedData->request->params->{splat} || [] } }
  21         56  
219 0     0 1 0 sub start { goto &_start }
220 9     9 1 57 sub status { Dancer::SharedData->response->status(@_) }
221 39     39 1 366 sub template { Dancer::Template::Abstract->template(@_) }
222 1     1 1 696 sub to_dumper { Dancer::Serializer::Dumper::to_dumper(@_) }
223 8     8 1 2709 sub to_json { Dancer::Serializer::JSON::to_json(@_) }
224 0     0 1 0 sub to_xml { Dancer::Serializer::XML::to_xml(@_) }
225 4     4 1 629 sub to_yaml { Dancer::Serializer::YAML::to_yaml(@_) }
226 21     21 1 2512 sub true { 1 }
227 3     3 1 442 sub upload { Dancer::SharedData->request->upload(@_) }
228 1     1 1 7 sub uri_for { Dancer::SharedData->request->uri_for(@_) }
229 19     19 1 1723 sub var { Dancer::SharedData->var(@_) }
230 28     28 1 120 sub vars { Dancer::SharedData->vars }
231 3     3 1 17 sub warning { goto &Dancer::Logger::warning }
232              
233             # When importing the package, strict and warnings pragma are loaded,
234             # and the appdir detection is performed.
235             {
236             my $as_script = 0;
237              
238             sub import {
239 306     306   34314 my ($class, @args) = @_;
240 306         1030 my ($package, $script) = caller;
241              
242 306         1866 strict->import;
243 306         3498 warnings->import;
244 306         2416 utf8->import;
245              
246 306         484 my @final_args;
247 306         498 my $syntax_only = 0;
248 306         696 foreach (@args) {
249 334 100       1505 if ( $_ eq ':moose' ) {
    100          
    100          
    50          
250 2         5 push @final_args, '!before', '!after';
251             }
252             elsif ( $_ eq ':tests' ) {
253 126         329 push @final_args, '!pass';
254             }
255             elsif ( $_ eq ':syntax' ) {
256 205         385 $syntax_only = 1;
257             }
258             elsif ($_ eq ':script') {
259 1         2 $as_script = 1;
260             } else {
261 0         0 push @final_args, $_;
262             }
263             }
264              
265 306         71884 $class->export_to_level(1, $class, @final_args);
266              
267             # if :syntax option exists, don't change settings
268 306 100       1273372 return if $syntax_only;
269              
270 101 100       406 $as_script = 1 if $ENV{PLACK_ENV};
271              
272 101 100       1471 Dancer::GetOpt->process_args unless $as_script;
273              
274 101         342 _init_script_dir($script);
275 101         518 Dancer::Config->load;
276             }
277              
278             }
279              
280             # private code
281              
282             # FIXME handle previous usage of load_app with multiple app names
283             sub _load_app {
284 9     9   28 my ($app_name, %options) = @_;
285 9         27 my $script = (caller)[1];
286 9         45 Dancer::Logger::core("loading application $app_name");
287              
288             # set the application
289 9         57 my $app = Dancer::App->set_running_app($app_name);
290              
291             # Application options
292 9 100       28 $app->set_app_prefix($options{prefix}) if $options{prefix};
293 9 100       25 $app->settings($options{settings}) if $options{settings};
294              
295             # load the application
296 9         27 _init_script_dir($script);
297 9         33 my ($res, $error) = Dancer::ModuleLoader->load($app_name);
298 9 100       41 $res or raise core => "unable to load application $app_name : $error";
299              
300             # restore the main application
301 7         21 Dancer::App->set_running_app('main');
302             }
303              
304             sub _init_script_dir {
305 196     196   486 my ($script) = @_;
306              
307 196         9849 my ($script_vol, $script_dirs, $script_name) =
308             File::Spec->splitpath(File::Spec->rel2abs($script));
309              
310             # normalize
311 196 100       5411 if ( -d ( my $fulldir = File::Spec->catdir( $script_dirs, $script_name ) ) ) {
312 85         285 $script_dirs = $fulldir;
313 85         181 $script_name = '';
314             }
315              
316 196         2378 my @script_dirs = File::Spec->splitdir($script_dirs);
317 196         426 my $script_path;
318 196 50       566 if ($script_vol) {
319 0         0 $script_path = Dancer::path($script_vol, $script_dirs);
320             } else {
321 196         533 $script_path = Dancer::path($script_dirs);
322             }
323              
324 196         397 my $LAYOUT_PRE_DANCER_1_2 = 1;
325              
326             # in bin/ or public/ or t/ we need to go one level up to find the appdir
327 196 50 33     2001 $LAYOUT_PRE_DANCER_1_2 = 0
      33        
328             if ($script_dirs[$#script_dirs - 1] eq 'bin')
329             or ($script_dirs[$#script_dirs - 1] eq 'public')
330             or ($script_dirs[$#script_dirs - 1] eq 't');
331              
332             my $appdir = $ENV{DANCER_APPDIR} || (
333 196   66     1254 $LAYOUT_PRE_DANCER_1_2
334             ? $script_path
335             : File::Spec->rel2abs(Dancer::path($script_path, '..'))
336             );
337 196         834 Dancer::setting(appdir => $appdir);
338              
339             # once the dancer_appdir have been defined, we export to env
340 196         1422 $ENV{DANCER_APPDIR} = $appdir;
341              
342 196         1315 Dancer::Logger::core("initializing appdir to: `$appdir'");
343              
344             Dancer::setting(confdir => $ENV{DANCER_CONFDIR}
345 196 100 33     505 || $appdir) unless Dancer::setting('confdir');
346              
347             Dancer::setting(public => $ENV{DANCER_PUBLIC}
348 196   33     1226 || Dancer::FileUtils::path($appdir, 'public'));
349              
350             Dancer::setting(views => $ENV{DANCER_VIEWS}
351 196   33     1588 || Dancer::FileUtils::path($appdir, 'views'));
352              
353 196         566 my ($res, $error) = Dancer::ModuleLoader->use_lib(Dancer::FileUtils::path($appdir, 'lib'));
354 196 50       949 $res or raise core => "unable to set libdir : $error";
355             }
356              
357              
358             # Scheme grammar as defined in RFC 2396
359             # scheme = alpha *( alpha | digit | "+" | "-" | "." )
360             my $scheme_re = qr{ [a-z][a-z0-9\+\-\.]* }ix;
361             sub _redirect {
362 19     19   40 my ($destination, $status) = @_;
363              
364             # RFC 2616 requires an absolute URI with a scheme,
365             # turn the URI into that if it needs it
366 19 100       245 if ($destination !~ m{^ $scheme_re : }x) {
367 18         55 my $request = Dancer::SharedData->request;
368 18         56 $destination = $request->uri_for($destination, {}, 1);
369             }
370 19         1523 my $response = Dancer::SharedData->response;
371 19   50     107 $response->status($status || 302);
372 19         51 $response->headers('Location' => $destination);
373             }
374              
375             sub _session {
376 18 50   18   43 engine 'session'
377             or raise core => "Must specify session engine in settings prior to using 'session' keyword";
378 18 100       88 @_ == 0 ? Dancer::Session->get
    100          
379             : @_ == 1 ? Dancer::Session->read(@_)
380             : Dancer::Session->write(@_);
381             }
382              
383             sub _send_file {
384 9     9   21 my ($path, %options) = @_;
385 9         21 my $env = Dancer::SharedData->request->env;
386              
387 9         24 my $request = Dancer::Request->new_for_request('GET' => $path);
388 9         24 Dancer::SharedData->request($request);
389              
390             # if you asked for streaming but it's not supported in PSGI
391 9 50 33     39 if ( $options{'streaming'} && ! $env->{'psgi.streaming'} ) {
392             # TODO: throw a fit (AKA "exception") or a Dancer::Error?
393 0         0 raise core => 'Sorry, streaming is not supported on this server.';
394             }
395              
396 9 100       22 if (exists($options{content_type})) {
397 3         6 $request->content_type($options{content_type});
398             }
399              
400             # If we're given an IO::Scalar object, DTRT (take the scalar ref from it)
401 9 50 33     25 if (Scalar::Util::blessed($path) && $path->isa('IO::Scalar')) {
402 0         0 $path = $path->sref;
403             }
404              
405 9         9 my $resp;
406 9 100       48 if (ref($path) eq "SCALAR") {
407             # send_data
408 1   33     2 $resp = Dancer::SharedData->response() || Dancer::Response->new();
409             $resp->header('Content-Type' => exists($options{content_type}) ?
410 1 50       5 $options{content_type} : Dancer::MIME->default());
411 1         39 $resp->content($$path);
412             } else {
413             # real send_file
414 8 100 66     46 if ($options{system_path} && -f $path) {
415 2         8 $resp = Dancer::Renderer->get_file_response_for_path($path);
416             } else {
417 6         21 $resp = Dancer::Renderer->get_file_response();
418             }
419             }
420              
421 9 100       20 if ($resp) {
422              
423 8 100       17 if (exists($options{filename})) {
424 2         8 $resp->push_header('Content-Disposition' =>
425             "attachment; filename=\"$options{filename}\""
426             );
427             }
428              
429 8 50       77 if ( $options{'streaming'} ) {
430             # handle streaming
431             $resp->streamed( sub {
432 0     0   0 my ( $status, $headers ) = @_;
433             my %callbacks = defined $options{'callbacks'} ?
434 0 0       0 %{ $options{'callbacks'} } :
  0         0  
435             ();
436              
437             return sub {
438 0         0 my $respond = shift;
439             exists $callbacks{'override'}
440 0 0       0 and return $callbacks{'override'}->( $respond, $resp );
441              
442             # get respond callback and set headers, get writer in return
443 0         0 my $writer = $respond->( [
444             $status,
445             $headers,
446             ] );
447              
448             # get content from original response
449 0         0 my $content = $resp->content;
450              
451             exists $callbacks{'around'}
452 0 0       0 and return $callbacks{'around'}->( $writer, $content );
453              
454 0 0       0 if ( ref $content ) {
455 0   0     0 my $bytes = $options{'bytes'} || '43008'; # 42K (dams)
456 0         0 my $buf;
457 0         0 while ( ( my $read = sysread $content, $buf, $bytes ) != 0 ) {
458 0 0       0 if ( exists $callbacks{'around_content'} ) {
459 0         0 $callbacks{'around_content'}->( $writer, $buf );
460             } else {
461 0         0 $writer->write($buf);
462             }
463             }
464             } else {
465 0         0 $writer->write($content);
466             }
467 0         0 };
468 0         0 } );
469             }
470              
471 8         60 return $resp;
472              
473             }
474              
475             Dancer::Error->new(
476 1         15 code => 404,
477             message => "No such file: `$path'"
478             )->render();
479             }
480              
481             # Start/Run the application with the chosen apphandler
482             sub _start {
483 0     0     my ($class, $request) = @_;
484 0           Dancer::Config->load;
485              
486             # Backward compatibility for app.psgi that has sub { Dancer->dance($req) }
487 0 0         if ($request) {
488 0           Dancer::Handler->init_request_headers( $request->env );
489             # TODO _build_headers should either not be private, or we should call
490             # init
491 0           $request->_build_headers;
492 0           return Dancer::Handler->handle_request($request);
493             }
494              
495 0           my $handler = Dancer::Handler->get_handler;
496 0           Dancer::Logger::core("loading handler '".ref($handler)."'");
497 0           return $handler->dance;
498             }
499              
500              
501             1;
502              
503             __END__