File Coverage

blib/lib/Dancer2/Core/DSL.pm
Criterion Covered Total %
statement 140 204 68.6
branch 19 42 45.2
condition 1 5 20.0
subroutine 70 92 76.0
pod 2 82 2.4
total 232 425 54.5


line stmt bran cond sub pod time code
1             # ABSTRACT: Dancer2's Domain Specific Language (DSL)
2              
3             $Dancer2::Core::DSL::VERSION = '0.400000';
4             use Moo;
5 135     135   147905 use Carp;
  135         6826  
  135         867  
6 135     135   46828 use Module::Runtime 'require_module';
  135         311  
  135         9406  
7 135     135   1734 use Ref::Util qw< is_arrayref >;
  135         3481  
  135         1116  
8 135     135   9568 use Dancer2::Core::Hook;
  135         3130  
  135         6153  
9 135     135   5395 use Dancer2::FileUtils;
  135         312  
  135         4005  
10 135     135   6700 use Dancer2::Core::Response::Delayed;
  135         296  
  135         5053  
11 135     135   51888  
  135         420  
  135         355264  
12             with 'Dancer2::Core::Role::DSL';
13              
14              
15 0     0 0 0 my ( $self, $postponed_hooks) = @_;
16 0     0 0 0  
17             $postponed_hooks = $postponed_hooks->{'plugin'};
18             return unless defined $postponed_hooks;
19 0     0   0  
20             for my $plugin ( keys %{$postponed_hooks} ) {
21 0         0 for my $name ( keys %{$postponed_hooks->{$plugin} } ) {
22 0 0       0 my $hook = $postponed_hooks->{$plugin}{$name}{hook};
23             my $caller = $postponed_hooks->{$plugin}{$name}{caller};
24 0         0  
  0         0  
25 0         0 $self->has_hook($name)
  0         0  
26 0         0 or croak "plugin $plugin does not support the hook `$name'. ("
27 0         0 . join( ", ", @{$caller} ) . ")";
28              
29             $self->add_hook($hook);
30             }
31 0 0       0 }
  0         0  
32             }
33 0         0  
34              
35             # the flag means : 1 = is global, 0 = is not global. global means can be
36             # called from anywhere. not global means must be called from within a route
37             # handler
38             { any => { is_global => 1 },
39             app => { is_global => 1 },
40             captures => { is_global => 0 },
41             config => { is_global => 1 },
42             content => { is_global => 0 },
43 255     255 0 25397 content_type => { is_global => 0 },
44             context => { is_global => 0 },
45             cookie => { is_global => 0 },
46             cookies => { is_global => 0 },
47             dance => { is_global => 1 },
48             dancer_app => { is_global => 1 },
49             dancer_version => { is_global => 1 },
50             dancer_major_version => { is_global => 1 },
51             debug => { is_global => 1 },
52             decode_json => { is_global => 1 },
53             del => { is_global => 1 },
54             delayed => {
55             is_global => 0, prototype => '&@',
56             },
57             dirname => { is_global => 1 },
58             done => { is_global => 0 },
59             dsl => { is_global => 1 },
60             encode_json => { is_global => 1 },
61             engine => { is_global => 1 },
62             error => { is_global => 1 },
63             false => { is_global => 1 },
64             flush => { is_global => 0 },
65             forward => { is_global => 0 },
66             from_dumper => { is_global => 1 },
67             from_json => { is_global => 1 },
68             from_yaml => { is_global => 1 },
69             get => { is_global => 1 },
70             halt => { is_global => 0 },
71             header => { is_global => 0 },
72             headers => { is_global => 0 },
73             hook => { is_global => 1 },
74             info => { is_global => 1 },
75             log => { is_global => 1 },
76             mime => { is_global => 1 },
77             options => { is_global => 1 },
78             param => { is_global => 0 },
79             params => { is_global => 0 },
80             query_parameters => { is_global => 0 },
81             body_parameters => { is_global => 0 },
82             route_parameters => { is_global => 0 },
83             pass => { is_global => 0 },
84             patch => { is_global => 1 },
85             path => { is_global => 1 },
86             post => { is_global => 1 },
87             prefix => { is_global => 1 },
88             prepare_app => {
89             is_global => 1, prototype => '&',
90             },
91             psgi_app => { is_global => 1 },
92             push_header => { is_global => 0 },
93             push_response_header => { is_global => 0 },
94             put => { is_global => 1 },
95             redirect => { is_global => 0 },
96             request => { is_global => 0 },
97             request_data => { is_global => 0 },
98             request_header => { is_global => 0 },
99             response => { is_global => 0 },
100             response_header => { is_global => 0 },
101             response_headers => { is_global => 0 },
102             runner => { is_global => 1 },
103             send_as => { is_global => 0 },
104             send_error => { is_global => 0 },
105             send_file => { is_global => 0 },
106             session => { is_global => 0 },
107             set => { is_global => 1 },
108             setting => { is_global => 1 },
109             splat => { is_global => 0 },
110             start => { is_global => 1 },
111             status => { is_global => 0 },
112             template => { is_global => 1 },
113             to_app => { is_global => 1 },
114             to_dumper => { is_global => 1 },
115             to_json => { is_global => 1 },
116             to_yaml => { is_global => 1 },
117             true => { is_global => 1 },
118             upload => { is_global => 0 },
119             uri_for => { is_global => 0 },
120             var => { is_global => 0 },
121             vars => { is_global => 0 },
122             warning => { is_global => 1 },
123             };
124             }
125              
126              
127             return ( split /\./, dancer_version )[0];
128             }
129              
130              
131 12     12 0 162  
132 1     1 0 6  
133              
134              
135 0     0 0 0  
136              
137              
138 0     0 0 0 my ( $self, $key, $value ) = @_;
139 3     3 0 18  
140 1     1 0 8 # shortcut reads if no session exists, so we don't
141 5     5 0 33 # instantiate sessions for no reason
142 2     2 0 11 if ( @_ == 2 ) {
143             return unless $self->app->has_session;
144 1     1 0 24 }
145 0     0 0 0  
146             my $session = $self->app->session
147 0 0   0 0 0 || croak "No session available, a session engine needs to be set";
148 0 0   0 0 0  
149             $self->app->setup_session;
150 4     4 0 27  
151             # return the session object if no key
152 9     9 0 64 @_ == 1 and return $session;
153              
154 143     143 1 1233 # read if a key is provided
155             @_ == 2 and return $session->read($key);
156 127     127 1 550  
157              
158 21     21 0 145 # write to the session or delete if value is undef
159             if ( defined $value ) {
160             $session->write( $key => $value );
161 127     127 0 333 }
162             else {
163             $session->delete($key);
164             }
165 127 100       350 }
166 56 100       309  
167              
168              
169 116   33     1977  
170             #
171             # route handlers & friends
172 116         2784 #
173              
174             my ( $self, $name, $code ) = @_;
175 116 100       4231 $self->app->add_hook(
176             Dancer2::Core::Hook->new( name => $name, code => $code ) );
177             }
178 104 100       376  
179             my $app = shift->app;
180             @_ == 1
181             ? $app->prefix(@_)
182 59 100       142 : $app->lexical_prefix(@_);
183 50         215 }
184              
185              
186 9         42  
187              
188             my $self = shift;
189              
190 8     8 0 29 # If they've supplied their own list of methods,
191             # expand del, otherwise give them the default list.
192 8     8 0 66 if ( is_arrayref($_[0]) ) {
193             s/^del$/delete/ for @{ $_[0] };
194 11     11 0 52 }
195             else {
196             unshift @_, [qw/delete get head options patch post put/];
197             }
198              
199             $self->_normalize_route(@_);
200             }
201 69     69 0 217  
202 69         1475 my $app = shift->app;
203             my $methods = shift;
204             my %args;
205              
206             # Options are optional, deduce their presence from arg length.
207 4     4 0 18 # @_ = ( REGEXP, OPTIONS, CODE )
208 4 100       71 # or
209             # @_ = ( REGEXP, CODE )
210             @args{qw/regexp options code/} = @_ == 3 ? @_ : ( $_[0], {}, $_[1] );
211              
212             return map $app->add_route( %args, method => $_ ), @{$methods};
213 7     7 0 46 }
214              
215 1     1 0 9 #
216 256     256 0 1364 # Server startup
217 1     1 0 9 #
218 1     1 0 9  
219 33     33 0 192 # access to the runner singleton
220 3     3 0 17 # will be populated on-the-fly when needed
221             # this singleton contains anything needed to start the application server
222 2     2 0 5  
  2         15  
223             # start the server
224              
225 7     7 0 19  
226             my $self = shift;
227              
228             $self->app->to_app;
229 7 100       37 }
230 3         7  
  3         25  
231              
232             #
233 4         19 # Response alterations
234             #
235              
236 7         27 $Dancer2::Core::Route::RESPONSE->status( $_[1] );
237             }
238              
239             carp "DEPRECATED: push_header keyword. Please use the 'push_response_header' keyword instead of 'push_header'";
240 302     302   1308 goto &push_response_header;
241 302         571 }
242 302         534  
243             shift;
244             $Dancer2::Core::Route::RESPONSE->push_header(@_);
245             }
246              
247             carp "DEPRECATED: header keyword. Please use the 'response_header' keyword instead of 'header'";
248 302 50       1806 goto &response_header;
249             }
250 302         601  
  302         1891  
251             shift;
252             $Dancer2::Core::Route::RESPONSE->header(@_);
253             }
254              
255             carp "DEPRECATED: headers keyword. Please use the 'response_headers' keyword instead of 'headers'";
256             goto &response_headers;
257             }
258              
259             shift;
260 1     1 0 5 $Dancer2::Core::Route::RESPONSE->header(@_);
261             }
262              
263 0     0 0 0 my $dsl = shift;
264              
265 0     0 0 0 # simple synchronous response
266             my $responder = $Dancer2::Core::Route::RESPONDER
267             or croak 'Cannot use content keyword outside delayed response';
268 2     2 0 6  
269             # flush if wasn't flushed before
270 2         14 if ( !$Dancer2::Core::Route::WRITER ) {
271             $Dancer2::Core::Route::WRITER = $responder->([
272             $Dancer2::Core::Route::RESPONSE->status,
273 137     137 0 1196 $Dancer2::Core::Route::RESPONSE->headers_to_array,
274             ]);
275             }
276              
277             eval {
278             $Dancer2::Core::Route::WRITER->write(@_);
279             1;
280 7     7 0 150 } or do {
281             my $error = $@ || 'Zombie Error';
282             $Dancer2::Core::Route::ERROR_HANDLER
283             ? $Dancer2::Core::Route::ERROR_HANDLER->($error)
284 0     0 0 0 : $dsl->app->logger_engine->log(
285 0         0 warning => "Error in delayed response: $error"
286             );
287             };
288             }
289 0     0 0 0  
290 0         0 shift;
291             $Dancer2::Core::Route::RESPONSE->content_type(@_);
292             }
293              
294 0     0 0 0 my ( $dsl, $cb, @args ) = @_;
295 0         0  
296             @args % 2 == 0
297             or croak 'Arguments to delayed() keyword must be key/value pairs';
298              
299 4     4 0 10 # first time, responder doesn't exist yet
300 4         70 my %opts = @args;
301             $Dancer2::Core::Route::RESPONDER
302             or return Dancer2::Core::Response::Delayed->new(
303             cb => $cb,
304 0     0 0 0 request => $Dancer2::Core::Route::REQUEST,
305 0         0 response => $Dancer2::Core::Route::RESPONSE,
306              
307             ( error_cb => $opts{'on_error'} )x!! $opts{'on_error'},
308             );
309 0     0 0 0  
310 0         0 # we're in an async request process
311             my $request = $Dancer2::Core::Route::REQUEST;
312             my $response = $Dancer2::Core::Route::RESPONSE;
313             my $responder = $Dancer2::Core::Route::RESPONDER;
314 1     1 0 3 my $writer = $Dancer2::Core::Route::WRITER;
315             my $handler = $Dancer2::Core::Route::ERROR_HANDLER;
316              
317 1 50       247 return sub {
318             local $Dancer2::Core::Route::REQUEST = $request;
319             local $Dancer2::Core::Route::RESPONSE = $response;
320             local $Dancer2::Core::Route::RESPONDER = $responder;
321 0 0       0 local $Dancer2::Core::Route::WRITER = $writer;
322 0         0 local $Dancer2::Core::Route::ERROR_HANDLER = $handler;
323              
324             $cb->(@_);
325             };
326             }
327              
328             my $responder = $Dancer2::Core::Route::RESPONDER
329 0         0 or croak 'flush() called outside streaming response';
330 0         0  
331 0 0       0 my $response = $Dancer2::Core::Route::RESPONSE;
332 0   0     0 $Dancer2::Core::Route::WRITER = $responder->([
333 0 0       0 $response->status, $response->headers_to_array,
334             ]);
335             }
336              
337             my $writer = $Dancer2::Core::Route::WRITER
338             or croak 'done() called outside streaming response';
339              
340             $writer->close;
341             }
342 0     0 0 0  
343 0         0  
344             #
345             # Route handler helpers
346             #
347 1     1 0 4  
348             carp "DEPRECATED: context keyword. Please use the 'app' keyword instead of 'context'";
349 1 50       4 shift->app;
350             }
351              
352              
353 1         3  
354              
355              
356              
357              
358              
359              
360 1 50       14  
361              
362              
363              
364 0         0  
365 0         0  
366 0         0  
367 0         0  
368 0         0 my $self = shift;
369             if ( $self->app ) {
370             return $self->app->mime_type;
371 0     0   0 }
372 0         0 else {
373 0         0 my $runner = $self->runner;
374 0         0 $runner->mime_type->reset_default;
375 0         0 return $runner->mime_type;
376             }
377 0         0 }
378 0         0  
379             #
380             # engines
381             #
382 0 0   0 0 0  
383             shift; # remove first element
384             require_module('Dancer2::Serializer::JSON');
385 0         0 Dancer2::Serializer::JSON::from_json(@_);
386 0         0 }
387              
388             shift; # remove first element
389             require_module('Dancer2::Serializer::JSON');
390             Dancer2::Serializer::JSON::to_json(@_);
391             }
392 0 0   0 0 0  
393             shift; # remove first element
394             require_module('Dancer2::Serializer::JSON');
395 0         0 Dancer2::Serializer::JSON::decode_json(@_);
396             }
397              
398 2     2 0 17 shift; # remove first element
399             require_module('Dancer2::Serializer::JSON');
400             Dancer2::Serializer::JSON::encode_json(@_);
401             }
402              
403             shift; # remove first element
404             require_module('Dancer2::Serializer::YAML');
405 0     0 0 0 Dancer2::Serializer::YAML::from_yaml(@_);
406 0         0 }
407              
408             shift; # remove first element
409 91     91 0 280 require_module('Dancer2::Serializer::YAML');
410             Dancer2::Serializer::YAML::to_yaml(@_);
411 1     1 0 2 }
  1         4  
412              
413 7     7 0 139 shift; # remove first element
414             require_module('Dancer2::Serializer::Dumper');
415 2     2 0 4 Dancer2::Serializer::Dumper::from_dumper(@_);
  2         8  
416             }
417 2     2 0 9  
418             shift; # remove first element
419 3     3 0 5 require_module('Dancer2::Serializer::Dumper');
  3         12  
420             Dancer2::Serializer::Dumper::to_dumper(@_);
421 31     31 0 1459 }
422              
423 41     41 0 70 1;
  41         138  
424              
425 6     6 0 25  
  6         26  
426             =pod
427 3     3 0 4  
  3         14  
428 5     5 0 9 =encoding UTF-8
  5         22  
429 14     14 0 21  
  14         46  
430             =head1 NAME
431 2     2 0 3  
  2         8  
432             Dancer2::Core::DSL - Dancer2's Domain Specific Language (DSL)
433 25     25 0 110  
434             =head1 VERSION
435 44     44 0 208  
436             version 0.400000
437 12     12 0 30  
438             =head1 FUNCTIONS
439 8     8 0 16  
  8         33  
440             =head2 setting
441 0     0 0 0  
442 0     0 0 0 Lets you define settings and access them:
443              
444             setting('foo' => 42);
445 2     2 0 4 setting('foo' => 42, 'bar' => 43);
446 2 50       15 my $foo=setting('foo');
447 2         8  
448             If settings were defined returns number of settings.
449              
450 0         0 =head2 set ()
451 0         0  
452 0         0 alias for L<setting>:
453              
454             set('foo' => '42');
455             my $port=set('port');
456              
457             =head1 SEE ALSO
458              
459             L<http://advent.perldancer.org/2010/18>
460              
461 6     6 0 8 =head1 AUTHOR
462 6         22  
463 6         147 Dancer Core Developers
464              
465             =head1 COPYRIGHT AND LICENSE
466              
467 21     21 0 26 This software is copyright (c) 2022 by Alexis Sukrieh.
468 21         67  
469 21         467 This is free software; you can redistribute it and/or modify it under
470             the same terms as the Perl 5 programming language system itself.
471              
472             =cut