File Coverage

blib/lib/Ark/Context.pm
Criterion Covered Total %
statement 123 138 89.1
branch 37 52 71.1
condition 12 19 63.1
subroutine 27 28 96.4
pod 0 21 0.0
total 199 258 77.1


line stmt bran cond sub pod time code
1             package Ark::Context;
2 60     60   342 use Mouse;
  60         121  
  60         748  
3              
4 60     60   16792 use Scalar::Util ();
  60         172  
  60         1108  
5 60     60   58782 use Try::Tiny;
  60         106976  
  60         3846  
6 60     60   57525 use URI::WithBase;
  60         627364  
  60         17478  
7              
8             our $DETACH = 'ARK_DETACH';
9             our $DEFERRED = 'ARK_DEFERRED';
10             our $STREAMING = 'ARK_STREAMING';
11              
12             extends 'Ark::Component';
13              
14             has request => (
15             is => 'rw',
16             isa => 'Object',
17             required => 1,
18             );
19              
20             has response => (
21             is => 'rw',
22             isa => 'Ark::Response',
23             lazy => 1,
24             default => sub {
25             Ark::Response->new;
26             },
27             );
28              
29             has app => (
30             is => 'rw',
31             isa => 'Ark::Core',
32             required => 1,
33             weak_ref => 1,
34             handles => ['debug', 'log', 'get_actions', 'get_action', 'ensure_class_loaded',
35             'component', 'controller', 'view', 'model', 'path_to', 'config',
36             'router',],
37             );
38              
39             has stash => (
40             is => 'rw',
41             isa => 'HashRef',
42             lazy => 1,
43             default => sub { {} },
44             );
45              
46             has stack => (
47             is => 'rw',
48             isa => 'ArrayRef',
49             lazy => 1,
50             default => sub { [] },
51             );
52              
53             has state => (
54             is => 'rw',
55             default => 0,
56             );
57              
58             has error => (
59             is => 'rw',
60             isa => 'ArrayRef',
61             lazy => 1,
62             default => sub { [] },
63             );
64              
65             has [qw/detached finalized/] => (
66             is => 'rw',
67             isa => 'Bool',
68             default => 0,
69             );
70              
71             { # alias
72 60     60   554 no warnings 'once';
  60         750  
  60         118102  
73             *req = \&request;
74             *res = \&response;
75             }
76              
77             sub process {
78 280     280 0 6788 my $self = shift;
79              
80 280         1407 $self->prepare;
81 280         1305 $self->dispatch;
82 280 50       5811 $self->finalize unless $self->response->is_deferred;
83             }
84              
85             sub prepare {
86 280     280 0 1388 my $self = shift;
87              
88 280         1235 $self->prepare_action;
89 280         78275 $self->prepare_encoding;
90 280         1027 $self->prepare_headers;
91 280         1237 $self->prepare_body;
92             }
93              
94             sub prepare_action {
95 280     280 0 546 my $self = shift;
96 280         1123 my $req = $self->request;
97              
98 280         2026 $req->match( $self->router->match($req->path) );
99             }
100              
101 280     280 0 456 sub prepare_headers {}
102              
103 280     280 0 523 sub prepare_body {}
104              
105             sub forward {
106 142     142 0 8674 my ($self, $target, @args) = @_;
107 142 100       432 return 0 unless $target;
108              
109 136 100       387 unless (@args) {
110 121         612 @args = @{ $self->req->captures } ? @{ $self->req->captures }
  10         179  
  111         1795  
111 121 100       175 : @{ $self->req->args };
112             }
113              
114 136 100       1553 if (Scalar::Util::blessed($target)) {
115 25 50       411 if ($target->isa('Ark::Action')) {
    50          
116 0         0 $target->dispatch($self, @args);
117 0         0 return $self->state;
118             }
119             elsif ($target->can('process')) {
120 25         112 $self->execute($target, 'process', @args);
121 25         126 return $self->state;
122             }
123             }
124             else {
125 111 100       367 if ($target =~ m!^/.+!) {
126 18         104 my ($namespace, $name) = $target =~ m!^(.*/)([^/]+)$!;
127 18         113 $namespace =~ s!(^/|/$)!!g;
128 18 50 100     168 if (my $action = $self->get_action($name, $namespace || '')) {
129 18         845 $action->dispatch($self, @args);
130 18         85 return $self->state;
131             }
132             }
133             else {
134 93         298 my $last = $self->stack->[-1];
135              
136 93 50 33     1409 if ($last
      33        
137             and $last->{obj}->isa('Ark::Controller')
138             and my $action = $self->get_action($target, $last->{obj}->namespace)) {
139              
140 93         3777 $action->dispatch($self, @args);
141 91         382 return $self->state;
142             }
143             }
144             }
145              
146 0         0 my $error = qq/Couldn't forward to $target, Invalid action or component/;
147 0         0 $self->log( error => $error );
148 0         0 push @{ $self->error }, $error;
  0         0  
149              
150 0         0 return 0;
151             }
152              
153             sub detach {
154 10     10 0 368 shift->forward(@_);
155 10         108 die $DETACH;
156             }
157              
158             sub dispatch {
159 271     271 0 527 my $self = shift;
160              
161 271         1145 my $match = $self->request->match;
162 271 100       1128 if ($match) {
163 270 100 66     1299 $self->dispatch_private_action('begin')
164             and $self->dispatch_auto_action
165             and $match->dispatch($self);
166              
167 270         1115 $self->detached(0);
168 270 50 33     3800 $self->dispatch_private_action('end')
169             unless $self->res->is_deferred or $self->res->is_streaming;
170             }
171             else {
172 1         11 $self->log( error => 'no action found' );
173             }
174             }
175              
176             sub dispatch_action {
177 0     0 0 0 my ($self, $name) = @_;
178              
179 0 0       0 my $action = ($self->router->get_actions($name, $self->req->action->namespace))[-1]
180             or return 1;
181 0         0 $action->dispatch($self);
182              
183 0         0 !@{ $self->error };
  0         0  
184             }
185              
186             sub dispatch_private_action {
187 540     540 0 1849 my ($self, $name) = @_;
188              
189 540         1883 my $action = ($self->router->get_actions($name, $self->req->action->namespace))[-1];
190 540 100 100     27561 return 1 unless ($action and $action->attributes->{Private});
191              
192 8         32 $action->dispatch($self);
193              
194 8         10 !@{ $self->error };
  8         41  
195             }
196              
197             sub dispatch_auto_action {
198 270     270 0 591 my $self = shift;
199              
200 270         1915 for my $auto ($self->router->get_actions('auto', $self->req->action->namespace)) {
201 4 50       121 next unless $auto->attributes->{Private};
202 4         10 $auto->dispatch($self);
203 4 100       19 return 0 unless $self->state;
204             }
205              
206 269         19145 1;
207             }
208              
209             sub depth {
210 12     12 0 19 scalar @{ shift->stack };
  12         79  
211             }
212              
213             sub execute {
214 487     487 0 1426 my ($self, $obj, $method, @args) = @_;
215 487         950 my $class = ref $obj;
216              
217 487         1467 $self->state(0);
218 487         807 push @{ $self->stack }, {
  487         3428  
219             obj => $obj,
220             method => $method,
221             args => \@args,
222             as_string => "${class}->${method}"
223             };
224              
225 487         2334 my $error;
226             try {
227 487     487   16366 $self->execute_action($obj, $method, @args);
228             } catch {
229 13     13   179 $error = $_;
230 487         4306 };
231              
232 487         6081 pop @{ $self->stack };
  487         1505  
233              
234 487 100       2706 if ($error) {
235 13 100       169 if ($error =~ /^${DETACH} at /) {
236 12 100       66 die $DETACH if ($self->depth >= 1);
237 10         61 $self->detached(1);
238             }
239             else {
240 1         1 push @{ $self->error }, $error;
  1         8  
241 1         3 $self->state(0);
242             }
243             }
244              
245 485         3292 $self->state;
246             }
247              
248             sub execute_action {
249 487     487 0 3787 my ($self, $obj, $method, @args) = @_;
250              
251 487         2935 my $state = $obj->$method($self, @args);
252 474 100       535359 $self->state( defined $state ? $state : undef );
253             }
254              
255             sub redirect {
256 2     2 0 5 my ($self, $uri, $status) = @_;
257              
258 2   100     10 $status ||= '302';
259              
260 2         18 $self->res->status($status);
261 2         104 $self->res->header( Location => $uri );
262             }
263              
264             sub redirect_and_detach {
265 2     2 0 748 my $self = shift;
266 2         10 $self->redirect(@_);
267 2         132 $self->detach;
268             }
269              
270             sub uri_for {
271 10     10 0 145 my ($self, @path) = @_;
272 10 50       64 my $params = ref $path[-1] eq 'HASH' ? pop @path : {};
273              
274 10         70 my $base = $self->req->base;
275 10         607 $base =~ s!/*$!!;
276              
277 10         290 (my $path = join '/', @path) =~ s!/{2,}!/!g;
278 10         48 $path =~ s!^/+!!;
279 10         93 my $uri = URI::WithBase->new($path, $base . '/');
280 10         926 $uri->query_form($params);
281              
282 10         716 $uri->abs;
283             }
284              
285             sub finalize {
286 280     280 0 1003 my $self = shift;
287              
288 280         2322 my $is_deferred = $self->response->is_deferred;
289              
290 280 50       758 if ($is_deferred) {
291 0         0 my $action = $self->request->action;
292 0 0       0 if ($action) {
293 0         0 $self->dispatch_private_action('end');
294             }
295             }
296              
297 280         1403 $self->finalize_headers;
298 280         1095 $self->finalize_body;
299 280         1433 $self->finalize_encoding;
300 280 50       3325 $self->response->finalize if $self->response->is_deferred;
301 280         1589 $self->finalized(1);
302             }
303              
304 280     280 0 434 sub finalize_headers {}
305 280     280 0 679 sub finalize_body {}
306              
307             sub DEMOLISH {
308 271     271 0 555 my $self = shift;
309 271 50       5372 $self->finalize unless $self->finalized;
310             }
311              
312             __PACKAGE__->meta->make_immutable;