File Coverage

blib/lib/Ado/Plugin.pm
Criterion Covered Total %
statement 53 56 94.6
branch 20 24 83.3
condition 13 16 81.2
subroutine 7 7 100.0
pod 2 2 100.0
total 95 105 90.4


line stmt bran cond sub pod time code
1             package Ado::Plugin;
2 23     23   11455 use Mojo::Base 'Mojolicious::Plugin';
  23         45  
  23         174  
3 23     23   5681 use Mojo::Util qw(decamelize class_to_path slurp decode);
  23         42  
  23         28708  
4              
5             File::Spec::Functions->import(qw(catfile catdir));
6              
7             has app => sub { Mojo::Server->new->build_app('Ado') };
8             has name => sub {
9              
10             # Only the last word of the plugin's package name
11             (ref $_[0] || $_[0]) =~ /(\w+)$/ && return $1;
12             };
13              
14             #has config_dir => sub { $_[0]->app->home->rel_dir('etc/plugins') };
15             has config_dir => sub {
16             catdir($_[0]->home_dir, 'etc', 'plugins');
17             };
18              
19             has home_dir => sub {
20             my $p = $INC{class_to_path(ref($_[0]))};
21              
22             # /home/you/dev/Ado-Plugin-Foo/lib/Ado/Plugin/Foo.pm
23             # /home/you/dev/Ado-Plugin-Foo
24             $p =~ s|[\\/]lib.+||x
25             || return $_[0]->app->home;
26             return $p;
27             };
28             has ext => 'conf';
29              
30             has config_classes => sub {
31             { conf => 'Mojolicious::Plugin::Config',
32             json => 'Mojolicious::Plugin::JSONConfig',
33             pl => 'Mojolicious::Plugin::Config'
34             };
35             };
36              
37             sub _get_plugin_config {
38 126     126   234 my ($self) = @_;
39 126         229 state $app = $self->app;
40 126         277 state $mode = $app->mode;
41 126         292 state $home = $app->home;
42 126         413 state $local_config_dir = catdir($home, 'etc', 'plugins');
43 126         1456 my $config_dir = $self->config_dir;
44 126         879 my $ext = $self->ext;
45 126         1328 my $name = decamelize($self->name);
46 126         3214 my $config = {};
47 126         870 my $config_class = $self->config_classes->{$ext};
48 126 50       463 if (my $e = Mojo::Loader::load_class($config_class)) {
49 0 0       0 Carp::croak ref $e ? "Exception: $e" : $config_class . ' - Not found!';
50             }
51 126         5609 my @config_paths = ("$config_dir/$name.$ext", "$config_dir/$name.$mode.$ext");
52 126 100       1037 push @config_paths, "$local_config_dir/$name.$ext", "$local_config_dir/$name.$mode.$ext"
53             if $local_config_dir ne $config_dir;
54              
55             #Try global, global.mode, local, local.mode
56 126         353 foreach my $f (@config_paths) {
57 494 100       10354 if (-f $f) {
58             my $specific =
59 143   66     299 eval { $config_class->new->load($f, {}, $app) } || Carp::croak($@);
60 142         103776 $config = {%$config, %$specific};
61             }
62             }
63 125         808 return $config;
64             }
65              
66             #plugin configuration getter
67             sub config {
68 199     199 1 15736 my ($self, $key, $value) = @_;
69 199   100     1309 $self->{config} //= $self->_get_plugin_config();
70 198 100       531 if (defined $value) {
71 12         31 $self->{config}->{$key} = $value;
72 12         24 return $self->{config};
73             }
74             return $key
75             ? $self->{config}->{$key}
76 186 100       926 : $self->{config};
77             }
78              
79             # one place for initializing plugins in register()
80             sub initialise {
81 122     122 1 1676 my ($self, $app, $conf) = @_;
82              
83 122         785 $self->app($app); #!Needed in $self->config!
84 122         1426 state $mode = $app->mode;
85              
86             #Merge passed configuration with configuration
87             #from etc/ado.conf and etc/plugins/$name.conf
88 122         589 for my $k (keys %$conf) { $self->config($k => $conf->{$k}); }
  12         35  
89 122         665 $conf = $self->config;
90              
91             # Add namespaces if defined.
92 0         0 push @{$app->routes->namespaces}, @{$conf->{namespaces}}
  0         0  
93 121 100       178 if @{$conf->{namespaces} || []};
  121 50       823  
94              
95             # Load routes if defined.
96 121 100       166 $app->load_routes($conf->{routes}) if (@{$conf->{routes} || []});
  121 100       1047  
97              
98             # Add templates folder if the plugin is not in the same folder
99 121         454 my $templates_dir = catdir($self->home_dir, 'templates');
100 121 100 50 230   1411 if ((!List::Util::first { $templates_dir eq $_ // '' } @{$app->renderer->paths})
  230   100     2606  
  121         450  
101             && -d $templates_dir)
102             {
103 24         45 push @{$app->renderer->paths}, $templates_dir;
  24         92  
104             }
105              
106             # Add plugin specific public folder if differs from app public
107 121         715 my $public_dir = catdir($self->home_dir, 'public');
108 121 100 50 351   1010 if ((!List::Util::first { $public_dir eq $_ // '' } @{$app->static->paths})
  351   100     2510  
  121         424  
109             && -d $public_dir)
110             {
111 23         47 push @{$app->static->paths}, $public_dir;
  23         87  
112             }
113              
114 121         727 return ($self, $app, $conf);
115             }
116              
117              
118             1;
119              
120             =pod
121              
122             =encoding utf8
123              
124             =head1 NAME
125              
126             Ado::Plugin - base class for Ado specific plugins.
127              
128              
129             =head1 SYNOPSIS
130              
131             Create your plugin like this:
132              
133             # CamelCase plugin name is recommended.
134             package Ado::Plugin::MyPlugin;
135             use Mojo::Base 'Ado::Plugin';
136              
137             sub register {
138             my ($self, $app, $conf) = shift->initialise(@_);
139              
140             # Your magic here!..
141              
142             return $self;
143             }
144              
145             but better use L to
146             generate all the files for you.
147              
148             =head1 DESCRIPTION
149              
150             Ado::Plugin is a base class for Ado specific plugins.
151             It provides some methods specific to L only.
152              
153             =head1 ATTRIBUTES
154              
155             Ado::Plugin inherits all attributes from L
156             and provides the following for use by subclasses.
157              
158             =head2 app
159              
160             my $app = $self->app;
161             $command = $self->app(MyApp->new);
162              
163             Application for plugin, defaults to a L object.
164              
165             =head2 config_dir
166              
167             Path to plugin directory.
168              
169             my $config_dir = $self->config_dir;
170             $self->config_dir($app->home->rel_dir('etc/plugins'));
171              
172             Defaults to C relative to the plugin base directory, see L.
173             This works both while developing a plugin and after installing the plugin.
174              
175             =head2 config_classes
176              
177             Returns a hash reference containing C class> pairs.
178             Used to detect which configuration plugin to use depending on the file extension.
179             The default mapping is:
180              
181             { conf => 'Mojolicious::Plugin::Config',
182             json => 'Mojolicious::Plugin::JSONConfig',
183             pl => 'Mojolicious::Plugin::Config'
184             };
185              
186             This attribute allows you to use your own configuration plugin as far as it
187             supports the L API.
188              
189             =head2 ext
190              
191             Extension used for the plugin specific configuration file. defaults to 'conf';
192              
193             my $ext = $self->ext;
194              
195             =head2 home_dir
196              
197             my $plugin_home = $self->home_dir;
198              
199             The plugin base directory.
200             This path works both while developing a plugin and after installing the plugin.
201             Using the guessed value allows you to have Ado plugins installed at arbitrary paths,
202             possibly not the same where Ado is installed.
203             As noted elsewhere, Ado plugins can be distributed as separate Ado applications and used
204             together with other plugins to create custom enterprise-grade systems.
205              
206             =head2 name
207              
208             The name - only the last word of the plugin's package name.
209              
210             $self->name # MyPlugin
211              
212             =head1 METHODS
213              
214             Ado::Plugin inherits all methods from L
215             and provides the following for use by subclasses.
216              
217             =head2 config
218              
219             The configuration which is for the currently registering plugin only.
220             In L every plugin can have its own configuration file.
221             When calling this method for the first time it will parse and merge
222             configuration files for the plugin. Options from mode specific
223             configuration file will overwrite options found in the generic file.
224             You usually do not need to invoke this method directly since it is
225             invoked in L.
226              
227             # everything in $self->config_dir.'/$my_plugin.conf'
228             # and/or $self->config_dir.'/$my_plugin.$mode.conf'
229             my $config = $self->config;
230              
231             #get a config value
232             my $value = $self->config('key');
233             #set
234             my $config = $self->config(foo => 'bar');
235              
236             =head2 initialise
237              
238             Used to initialize you plugin and reduce boilerplate code.
239              
240             sub register {
241             my ($self, $app, $config) = shift->initialise(@_);
242             # ...
243              
244             This method
245             should be the first invoked in your L method.
246             If you need to do some very custom stuff, you are free to implement the
247             initialisation yourself.
248              
249             Currently this method does the following:
250              
251             =over
252              
253             =item * Merges configurations (invokes L).
254              
255             =item * Pushes C<@{$conf-E{namespaces}}> to C<$app-Eroutes-Enamespaces>
256             if additional namespaces are defined in configuration file.
257              
258             =item * Loads routes if defined in configuration file.
259              
260             =item * Pushes the plugin C directory to C<@{app-Erenderer-Epaths}>
261             (if it exists and is not the same as Ado's one) so the templates can be found by
262             L while developing your plugin.
263              
264             =item * Pushes the plugin C directory to C<@{app-Estatic-Epaths}>
265             (if it exists and is not the same as Ado's one) so the templates can be found by
266             L while developing your plugin.
267              
268             =item * Returns C<($self, $app, $config)>.
269              
270             =back
271              
272             Look at some of the configuration files of the plugins that come with L.
273              
274              
275             =head1 SEE ALSO
276              
277             L, L,
278             L, L, L,
279             L, L,
280             L.
281              
282             =head1 AUTHOR
283              
284             Красимир Беров (Krasimir Berov)
285              
286             =head1 COPYRIGHT AND LICENSE
287              
288             Copyright 2013-2014 Красимир Беров (Krasimir Berov).
289              
290             This program is free software, you can redistribute it and/or
291             modify it under the terms of the
292             GNU Lesser General Public License v3 (LGPL-3.0).
293             You may copy, distribute and modify the software provided that
294             modifications are open source. However, software that includes
295             the license may release under a different license.
296              
297             See http://opensource.org/licenses/lgpl-3.0.html for more information.
298              
299             =cut