File Coverage

blib/lib/Kelp/Module/Symbiosis.pm
Criterion Covered Total %
statement 11 11 100.0
branch n/a
condition 2 2 100.0
subroutine 4 4 100.0
pod 1 1 100.0
total 18 18 100.0


line stmt bran cond sub pod time code
1             package Kelp::Module::Symbiosis;
2             $Kelp::Module::Symbiosis::VERSION = '2.11';
3 13     13   3117406 use Kelp::Base qw(Kelp::Module);
  13         28  
  13         97  
4 13     13   10426 use KelpX::Symbiosis::Adapter;
  13         41  
  13         82  
5              
6             sub build
7             {
8 13     13 1 1657 my ($self, %args) = @_;
9              
10             my $adapter = KelpX::Symbiosis::Adapter->new(
11             app => $self->app,
12 13   100     64 engine => delete $args{engine} // 'URLMap'
13             );
14              
15 13         82 $adapter->build(%args);
16             $self->register(
17             symbiosis => $adapter,
18 56     56   592 run_all => sub { shift->symbiosis->run(@_); },
19 13         184 );
20              
21             }
22              
23             1;
24             __END__
25              
26             =head1 NAME
27              
28             Kelp::Module::Symbiosis - Fertile ground for building Plack apps
29              
30             =head1 SYNOPSIS
31              
32             # in configuration file
33             modules => [qw/Symbiosis SomeSymbioticModule/],
34             modules_init => {
35             Symbiosis => {
36             mount => '/kelp', # a path to mount Kelp main instance
37             },
38             SomeSymbioticModule => {
39             mount => '/elsewhere', # a path to mount SomeSymbioticModule
40             },
41             },
42              
43             # in kelp application - can be skipped if all mount paths are specified in config above
44             my $symbiosis = $kelp->symbiosis;
45             $symbiosis->mount('/app-path' => $kelp);
46             $symbiosis->mount('/other-path' => $kelp->module_method);
47             $symbiosis->mount('/other-path' => 'module_name'); # alternative - finds a module by name
48              
49             # in psgi script
50             my $app = KelpApp->new();
51             $app->run_all; # instead of run
52              
53             =head1 DESCRIPTION
54              
55             This module is an attempt to standardize the way many standalone Plack
56             applications should be ran alongside the Kelp framework. The intended use is to
57             introduce new "organisms" into symbiotic interaction by creating Kelp modules
58             that are then attached onto Kelp. Then, the added method I<run_all> should be
59             invoked in place of Kelp's I<run>, which will construct and return an ecosystem.
60              
61             =head2 Module state with Kelp 2.10
62              
63             This module isn't as useful anymore with Kelp version C<2.10> adding easy
64             mounting of Plack apps. It may still be useful in some specific scenarios, like
65             automatic URLMap usage or when some modules use it, but generally you can
66             consider it outdated, as the problem it was solving is now easily solved using
67             core L<Kelp>. See L<Kelp::Manual/Nesting Plack apps> for details on how Kelp
68             deals with mounting applications under a route natively.
69              
70             =head2 Why not just use Plack::Builder in a .psgi script?
71              
72             One reason is not to put too much logic into .psgi script. It my opinion a
73             framework should be capable enough not to make adding an additional application
74             feel like a hack. This is of course subjective.
75              
76             The main functional reason to use this module is the ability to access the Kelp
77             application instance inside another Plack application. If that application is
78             configurable, it can be configured to call Kelp methods. This way, Kelp can
79             become a glue for multiple standalone Plack applications, the central point of
80             a Plack mixture:
81              
82             # in Symbiont's Kelp module (extends Kelp::Module::Symbiosis::Base)
83              
84             sub psgi {
85             my ($self) = @_;
86              
87             my $app = Some::Plack::App->new(
88             on_something => sub {
89             my $kelp = $self->app; # we can access Kelp!
90             $kelp->something_happened;
91             },
92             );
93              
94             return $app->to_app;
95             }
96              
97             # in Kelp application class
98              
99             sub something_happened {
100             ... # handle another app's signal
101             }
102              
103              
104             =head2 What can be mounted?
105              
106             The sole requirement for a module to be mounted into Symbiosis is its ability
107             to I<run()>, returning the psgi application. A module also needs to be a
108             blessed reference, of course. Fun fact: Symbiosis module itself meets that
109             requirements, so one symbiotic app can be mounted inside another.
110              
111             It can also be just a plain psgi app, which happens to be a code reference.
112              
113             Whichever it is, it should be a psgi application ready to be ran by the server,
114             wrapped in all the needed middlewares. This is made easier with
115             L<Kelp::Module::Symbiosis::Base>, which allows you to add symbionts in the
116             configuration for Kelp along with the middlewares. Because of this, this should
117             be a preferred way of defining symbionts.
118              
119             For very simple use cases, this will work though:
120              
121             # in application build method
122             my $some_app = SomePlackApp->new->to_app;
123             $self->symbiosis->mount('/path', $some_app);
124              
125             =head1 METHODS
126              
127             These methods are available on the object in C<< $app->symbiosis >>:
128              
129             =head2 mount
130              
131             sig: mount($self, $path, $app)
132              
133             Adds a new $app to the ecosystem under $path. I<$app> can be:
134              
135             =over
136              
137             =item
138              
139             A blessed reference - will try to call C<run> on it
140              
141             =item
142              
143             A code reference - will try calling it
144              
145             =item
146              
147             A string - will try finding a symbiotic module with that name and mounting it.
148             See L<Kelp::Module::Symbiosis::Base/name>
149              
150             =back
151              
152             =head2 run
153              
154             Returns a coderef ready to be run by a Plack handler. Details on that depend on
155             the engine used, see L</engine>.
156              
157             Note: it will not run mounted object twice. This means that it is safe to mount
158             something in two paths at once, and it will just be an alias to the same
159             application.
160              
161             =head2 mounted
162              
163             sig: mounted($self)
164              
165             Returns a hashref containing a list of mounted modules, keyed by their specified mount paths.
166              
167             =head2 loaded
168              
169             sig: loaded($self)
170              
171             I<new in 1.10>
172              
173             Returns a hashref containing a list of loaded modules, keyed by their names.
174              
175             A module is loaded once it is added to Kelp configuration. This can be used to
176             access a module that does not introduce new methods to Kelp.
177              
178             =head1 METHODS INTRODUCED TO KELP
179              
180             =head2 symbiosis
181              
182             Returns an instance of this class.
183              
184             =head2 run_all
185              
186             Shortcut method, same as C<< $kelp->symbiosis->run() >>.
187              
188             =head1 CONFIGURATION
189              
190             # Symbiosis MUST be specified as the first one
191             modules => [qw/Symbiosis Module::Some/],
192             modules_init => {
193             Symbiosis => {
194             mount => '/kelp',
195             },
196             'Module::Some' => {
197             mount => '/some',
198             ...
199             },
200             }
201              
202             Symbiosis should be the first of the symbiotic modules specified in your Kelp
203             configuration. Failure to meet this requirement will cause your application to
204             crash immediately.
205              
206             =head2 engine
207              
208             I<new in 2.00>
209              
210             Engine is the approach taken by the module to run your apps. Engines are
211             implemented in namespace C<KelpX::Symbiosis::Engine>. Bundled engines include:
212              
213             =over
214              
215             =item * L<KelpX::Symbiosis::Engine::URLMap>, which uses L<App::Plack::URLMap> and mounts Kelp into it alongside other apps
216              
217             =item * L<KelpX::Symbiosis::Engine::Kelp>, which uses app routing to mount other apps
218              
219             =back
220              
221             Default is C<URLMap>. See their documentation for caveats regarding each implementation.
222              
223             =head2 mount
224              
225             I<new in 1.10>
226              
227             A path to mount the Kelp instance, which defaults to I<'/'>. Specify a string
228             if you wish a to use different path. Specify an I<undef> or empty string to
229             avoid mounting at all - you will have to run something like C<<
230             $kelp->symbiosis->mount($mount_path, $kelp); >> in Kelp's I<build> method
231             (unless the engine is C<Kelp>, in which case you can't mount the app anywhere -
232             it will always be root).
233              
234             =head2 reverse_proxy
235              
236             I<new in 1.11>
237              
238             A boolean flag (I<1/0>) which enables reverse proxy for all the Plack apps at
239             once. Requires L<Plack::Middleware::ReverseProxy> to be installed.
240              
241             =head2 middleware, middleware_init
242              
243             I<new in 1.12>
244              
245             Middleware specs for the entire ecosystem. Every application mounted in
246             Symbiosis will be wrapped in these middleware. They are configured exactly the
247             same as middlewares in Kelp. Regular Kelp middleware will be used just for the
248             Kelp application, so if you want to wrap all symbionts at once, this is the
249             place to do it.
250              
251             =head1 CAVEATS
252              
253             Routes specified in symbiosis will be matched before routes in Kelp. Once you
254             mount something under I</api> for example, you will no longer be able to
255             specify Kelp route for anything under I</api>.
256              
257             =head1 SEE ALSO
258              
259             =over
260              
261             =item * L<Kelp::Module::Symbiosis::Base>, a base for symbiotic modules
262              
263             =item * L<Kelp::Module::WebSocket::AnyEvent>, a reference symbiotic module
264              
265             =item * L<Plack::App::URLMap>, Plack URL mapper application
266              
267             =back
268              
269             =head1 AUTHOR
270              
271             Bartosz Jarzyna, E<lt>bbrtj.pro@gmail.comE<gt>
272              
273             =head1 COPYRIGHT AND LICENSE
274              
275             Copyright (C) 2020 - 2024 by Bartosz Jarzyna
276              
277             This library is free software; you can redistribute it and/or modify
278             it under the same terms as Perl itself.
279              
280              
281             =cut
282