File Coverage

blib/lib/Kelp/Context.pm
Criterion Covered Total %
statement 33 33 100.0
branch 9 10 90.0
condition 3 6 50.0
subroutine 8 8 100.0
pod 5 5 100.0
total 58 62 93.5


line stmt bran cond sub pod time code
1             package Kelp::Context;
2              
3 25     25   19035 use Kelp::Base;
  25         59  
  25         192  
4 25     25   196 use Kelp::Util;
  25         70  
  25         1787  
5 25     25   333 use Carp;
  25         65  
  25         20588  
6              
7             attr req => undef;
8             attr res => undef;
9              
10             attr -app => sub { croak 'app is required' };
11             attr -_controllers => sub { {} };
12             attr persistent_controllers => sub { $_[0]->app->config('persistent_controllers') };
13             attr current => sub { shift->app };
14              
15             sub build_controller
16             {
17 17     17 1 34 my ($self, $controller_class) = @_;
18 17         39 return $self->app->_clone($controller_class);
19             }
20              
21             # loads the class, reblesses and returns - can be used to get controller on
22             # demand with partial or unloaded class name
23             sub controller
24             {
25 7     7 1 12 my ($self, $controller) = @_;
26 7         18 my $base = $self->app->routes->base;
27 7 100       17 $controller = '+' . $base
28             if !defined $controller;
29              
30 7         17 $controller = Kelp::Util::camelize($controller, $base, 1);
31 7         19 Kelp::Util::load_package($controller);
32              
33 7 50       43 croak "Invalid controller, not subclassing $base"
34             unless $controller->isa($base);
35              
36 7   33     19 return $self->_controllers->{$controller} //=
37             $self->build_controller($controller);
38             }
39              
40             # reblesses, remembers and sets the current controller - used internally
41             sub set_controller
42             {
43 266     266 1 662 my ($self, $controller) = @_;
44 266 100       1099 return $self->current($self->app)
45             unless $controller;
46              
47             # the controller class should already be loaded by the router
48 20   66     50 my $current = $self->_controllers->{$controller} //=
49             $self->build_controller($controller);
50              
51 20         65 $self->current($current);
52 20         31 return $current;
53             }
54              
55             # clears the object for the next route - used internally
56             sub clear
57             {
58 251     251 1 466 my $self = shift;
59              
60 251 100       939 %{$self->_controllers} = ()
  244         796  
61             unless $self->persistent_controllers;
62 251         859 $self->current($self->app);
63             }
64              
65             # run method in current context. If current context does not provide this
66             # method, run it in app instead.
67             sub run_method
68             {
69 596     596 1 869 my $self = shift;
70 596         944 my $method = shift;
71 596         1337 my $c = $self->current;
72              
73 596 100       2524 if ($c->can($method)) {
74 589         2013 $c->$method(@_);
75             }
76             else {
77 7         10 $self->app->$method(@_);
78             }
79             }
80              
81             1;
82              
83             __END__
84              
85             =pod
86              
87             =head1 NAME
88              
89             Kelp::Context - Tracks Kelp application's current execution context
90              
91             =head1 SYNOPSIS
92              
93             # get current controller
94             $app->context->current;
95              
96             # get the application
97             $app->context->app;
98              
99             # get the named controller
100             $app->context->controller('Controller');
101              
102             =head1 DESCRIPTION
103              
104             This is a small helper object which keeps track of the context in which the
105             app currently is. It also remembers all the constructed controllers until it
106             is cleared - which usually is at the start of the request.
107              
108             Advanced usage only.
109              
110             It can be subclassed to change how controllers are built and handled. This
111             would usually involve overriding the C<build_controller> method.
112              
113             =head1 ATTRIBUTES
114              
115             =head2 app
116              
117             Main application object. This will always be the main app, not a controller.
118              
119             =head2 current
120              
121             Current controller object. This will be automatically set to a proper
122             controller by the router.
123              
124             =head2 req
125              
126             =head2 res
127              
128             Current request and response objects, also accessible from C<< $app->req >> and
129             C<< $app->res >>.
130              
131             =head2 persistent_controllers
132              
133             A configuration field which defines whether L</clear> destroys constructed
134             controllers. By default it is taken from app's configuration field of the same
135             name.
136              
137             =head1 METHODS
138              
139             =head2 build_controller
140              
141             Defines how a controller is built. Can be overridden to introduce a custom
142             controller object instead of reblessed application.
143              
144             =head2 controller
145              
146             Returns a controller of a given name. The name will be mangled according to the
147             base route class of the application. Contains extra checks to ensure the input
148             is valid and loads the controller class if it wasn't loaded yet.
149              
150             If the controller name is C<undef>, the base controller is returned.
151              
152             =head2 set_controller
153              
154             Similar to L</controller>, but does not have any special checks for correctness
155             and only accepts a full class name. It also modifies the L</current> to the
156             controller after constructing it. Passing a false value will result in
157             reverting the current context back to the app object.
158              
159             It's optimized for speed and only used internally, so it's not recommended to
160             use it unless you extend Kelp router itself.
161              
162             =head2 clear
163              
164             Clears context in anticipation of the next request. Called automatically at the
165             start of every request.
166              
167             =head2 run_method
168              
169             $self->context->run_method($name => @args);
170              
171             This method runs method C<$name> in current context. If the current context
172             does not provide that method, it will be run in application context instead. It
173             should only be used for methods which are known to be available in application
174             instance.
175