File Coverage

blib/lib/Kelp/Module/Symbiosis/Base.pm
Criterion Covered Total %
statement 26 27 96.3
branch 2 4 50.0
condition 3 5 60.0
subroutine 5 6 83.3
pod 4 4 100.0
total 40 46 86.9


line stmt bran cond sub pod time code
1             package Kelp::Module::Symbiosis::Base;
2              
3             our $VERSION = '1.10';
4              
5 4     4   3904 use Kelp::Base qw(Kelp::Module);
  4         15  
  4         31  
6 4     4   689 use Plack::Util;
  4         8  
  4         1745  
7              
8             attr "-middleware" => sub { [] };
9              
10             # should likely be overriden for a more suitable name
11             # won't break backcompat though
12 3     3 1 39 sub name { ref shift }
13              
14             sub run
15             {
16 25     25 1 50 my ($self) = shift;
17              
18 25         89 my $app = $self->psgi(@_);
19 25         181 for (@{$self->middleware}) {
  25         60  
20 4         28 my ($class, $args) = @$_;
21              
22             # Same middleware loading procedure as Kelp
23 4 50 66     25 next if $self->{_loaded_middleware}->{$class}++ && !$ENV{KELP_TESTING};
24              
25 4         14 my $mw = Plack::Util::load_class($class, "Plack::Middleware");
26 4   50     1718 $app = $mw->wrap($app, %{$args // {}});
  4         40  
27             }
28 25         345 return $app;
29             }
30              
31             sub psgi
32             {
33 0     0 1 0 die __PACKAGE__ . " - psgi needs to be reimplemented";
34             }
35              
36             sub build
37             {
38 7     7 1 50318 my ($self, %args) = @_;
39              
40 7 50       22 die 'Kelp::Module::Symbiosis needs to be loaded before ' . ref $self
41             unless $self->app->can('symbiosis');
42              
43 7         76 my $middleware = $self->middleware;
44 7         23 foreach my $mw (@{$args{middleware}}) {
  7         29  
45 1         3 my $config = $args{middleware_init}{$mw};
46 1         8 push @$middleware, [$mw, $config];
47             }
48              
49 7         33 $self->app->symbiosis->_link($self->name, $self, $args{mount});
50 7         24 return;
51             }
52              
53             1;
54             __END__
55              
56             =head1 NAME
57              
58             Kelp::Module::Symbiosis::Base - Base class for symbiotic modules
59              
60             =head1 SYNOPSIS
61              
62             package Kelp::Module::MyModule;
63              
64             use Kelp::Base qw(Kelp::Module::Symbiosis::Base);
65              
66             sub psgi
67             {
68             # write code that returns psgi application without middlewares
69             }
70              
71             sub build
72             {
73             my ($self, %args) = @_;
74             $self->SUPER::build(%args);
75              
76             # write initialization code as usual
77             $self->register(some_method => sub { ... });
78             }
79              
80             =head1 DESCRIPTION
81              
82             This class serves as a base for a Kelp module that is supposed to be ran as a standalone Plack application (mounted separately). It takes care of middleware management, mounting into Symbiosis manager and some basic initialization chores. To write a new module that introduces a standalone Plack application as a Kelp module, simply extend this class and override methods: C<psgi build name> (see below for details).
83              
84             =head2 Purpose
85              
86             It is a base for Kelp modules that are meant to be used with Symbiosis - it inherits from L<Kelp::Module>. It can also come very handy because of the built in middleware handling and access to Kelp application's configuration.
87              
88             =head1 METHODS
89              
90             =head2 run
91              
92             sig: run($self)
93              
94             Calls I<psgi()> and wraps its contents in middlewares. Returns a Plack application.
95              
96             =head2 psgi
97              
98             sig: psgi($self, @more_data)
99              
100             By default, this method will throw an exception. It has to be replaced with an actual application producing code in the child class. The resulting application will be wrapped in middlewares from config in I<run()>.
101              
102             B<Must be reimplemented> in a module.
103              
104             =head2 build
105              
106             sig: build($self, %args)
107              
108             Standard Kelp module building method. When reimplementing it's best to call parent's implementation, as middleware initialization happens in base implementation.
109              
110             B<Should be reimplemented> in a module. If it isn't, no extra methods will be added to the Kelp instance, but all the middleware and module registration in Symbiosis will happen anyway.
111              
112             =head2 name
113              
114             sig: name($self)
115              
116             I<new in 1.10>
117              
118             Returns a name of a module - a string. This name will be available in L<Kelp::Module::Symbiosis/loaded> hash as a key, containing the module instance as a value.
119              
120             B<Should be reimplemented> in a module. If it isn't, it will return the name of the package.
121              
122             =head2 middleware
123              
124             sig: middleware($self)
125              
126             Returns an array containing all the middlewares in format: C<[ middleware_class, { middleware_config } ]>. By default, this config comes from module configuration.
127              
128             =head1 CONFIGURATION
129              
130             example configuration could look like this (for L<Kelp::Module::WebSocket::AnyEvent>):
131              
132             modules => [qw/JSON Symbiosis WebSocket::AnyEvent/],
133             modules_init => {
134             Symbiosis => {
135             mount => undef, # kelp will be mounted manually under different path
136             },
137             "WebSocket::AnyEvent" => {
138             serializer => "json",
139             middleware => [qw/Recorder/],
140             middleware_init => {
141             Recorder => { output => "~/recorder.out" },
142             }
143             },
144             }
145              
146             =head2 middleware, middleware_init
147              
148             Middleware specs for this application - see above example. Every module basing on this class can specify its own set of middlewares. They are configured exactly the same as middlewares in Kelp. There's currently no standarized way to retrieve middleware configurations from Kelp into another application (to wrap that application in the same middleware as Kelp), so custom code is needed if such need arise.
149              
150             =head2 mount
151              
152             modules_init => {
153             "Symbiotic::Module" => {
154             mount => '/path',
155             ...
156             },
157             }
158              
159             I<new in 1.10>
160              
161             Should be a string value. If specified, the module will be automatically mounted under that path - there will be no need to call that explicitly, and it will work like: C<< $kelp->symbiosis->mount($path => $module); >>.
162              
163             =head1 SEE ALSO
164              
165             =over 2
166              
167             =item * L<Kelp::Module::Symbiosis>, the module manager
168              
169             =back