1package Dancer::ModuleLoader;
2our $AUTHORITY = 'cpan:SUKRIA';
3#ABSTRACT: dynamic module loading helpers for Dancer core components
4$Dancer::ModuleLoader::VERSION = '1.3513';
5# Abstraction layer for dynamic module loading
6
7use strict;
8use warnings;
9use Module::Runtime qw/ use_module /;
10
11sub load {
12    my ($class, $module, $version) = @_;
13
14    my ($res, $error) = $class->require($module, $version);
15    return wantarray ? ($res, $error) : $res;
16}
17
18sub require {
19    my ($class, $module, $version) = @_;
20    eval { defined $version ? use_module( $module, $version )
21                            : use_module( $module ) }
22        or return wantarray ? (0, $@) : 0;
23    return 1; #success
24}
25
26sub load_with_params {
27    my ($class, $module, @args) = @_;
28    my ($res, $error) = $class->require($module);
29    $res or return wantarray ? (0, $error) : 0;
30
31    # From perlfunc : If no "import" method can be found then the call is
32    # skipped, even if there is an AUTOLOAD method.
33    if ($module->can('import')) {
34
35        # bump Exporter Level to import symbols in the caller
36        local $Exporter::ExportLevel = ($Exporter::ExportLevel || 0) + 1;
37        local $@;
38        eval { $module->import(@args) };
39        my $error = $@;
40        $error and return wantarray ? (0, $error) : 0;
41    }
42    return 1;
43}
44
45sub use_lib {
46    my ($class, @args) = @_;
47    use lib;
48    local $@;
49    lib->import(@args);
50    my $error = $@;
51    $error and return wantarray ? (0, $error) : 0;
52    return 1;
53}
54
55sub class_from_setting {
56    my ($self, $namespace, $setting) = @_;
57
58    my $class = '';
59    for my $token (split /_/, $setting) {
60        $class .= ucfirst($token);
61    }
62    return "${namespace}::${class}";
63}
64
651;
66
67__END__
68
69=pod
70
71=encoding UTF-8
72
73=head1 NAME
74
75Dancer::ModuleLoader - dynamic module loading helpers for Dancer core components
76
77=head1 VERSION
78
79version 1.3513
80
81=head1 SYNOPSIS
82
83Taken directly from Dancer::Template::TemplateToolkit (which is core):
84
85    die "Template is needed by Dancer::Template::TemplateToolkit"
86      unless Dancer::ModuleLoader->load('Template');
87
88    # we now have Template loaded
89
90=head1 DESCRIPTION
91
92Sometimes in Dancer core we need to use modules, but we don't want to declare
93them all in advance in compile-time. These could be because the specific modules
94provide extra features which depend on code that isn't (and shouldn't) be in
95core, or perhaps because we only want these components loaded in lazy style,
96saving loading time a bit. For example, why load L<Template> (which isn't
97required by L<Dancer>) when you don't use L<Dancer::Template::TemplateToolkit>?
98
99To do such things takes a bit of code for localizing C<$@> and C<eval>ing. That
100code has been refactored into this module to help Dancer core developers.
101
102B<Please only use this for Dancer core modules>. If you're writing an external
103Dancer module (L<Dancer::Template::Tiny>, L<Dancer::Session::Cookie>, etc.),
104please simply "C<use ModuleYouNeed>" in your code and don't use this module.
105
106=head1 METHODS/SUBROUTINES
107
108=head2 load
109
110Runs something like "C<use ModuleYouNeed>" at runtime.
111
112    use Dancer::ModuleLoader;
113    ...
114    Dancer::ModuleLoader->load('Something')
115        or die "Couldn't load Something\n";
116
117    # load version 5.0 or more
118    Dancer::ModuleLoader->load('Something', '5.0')
119        or die "Couldn't load Something\n";
120
121    # load version 5.0 or more
122    my ($res, $error) = Dancer::ModuleLoader->load('Something', '5.0');
123    $res or die "Couldn't load Something : '$error'\n";
124
125Takes in arguments the module name, and optionally the minimum version number required.
126
127In scalar context, returns 1 if successful, 0 if not.
128In list context, returns 1 if successful, C<(0, "error message")> if not.
129
130If you need to give argument to the loading module, please use the method C<load_with_params>
131
132=head2 require
133
134Runs a "C<require ModuleYouNeed>".
135
136    use Dancer::ModuleLoader;
137    ...
138    Dancer::ModuleLoader->require('Something')
139        or die "Couldn't require Something\n";
140    my ($res, $error) = Dancer::ModuleLoader->require('Something');
141    $res or die "Couldn't require Something : '$error'\n";
142
143If you are unsure what you need (C<require> or C<load>), learn the differences
144between C<require> and C<use>.
145
146Takes in arguments the module name.
147
148In scalar context, returns 1 if successful, 0 if not.
149In list context, returns 1 if successful, C<(0, "error message")> if not.
150
151=head2 load_with_params
152
153Runs a "C<use ModuleYouNeed qw(param1 param2 ...)>".
154
155    use Dancer::ModuleLoader;
156    ...
157    Dancer::ModuleLoader->load_with_params('Something', qw(param1 param2) )
158        or die "Couldn't load Something with param1 and param2\n";
159
160    my ($res, $error) = Dancer::ModuleLoader->load_with_params('Something', @params);
161    $res or die "Couldn't load Something with @params: '$error'\n";
162
163Takes in arguments the module name, and optionally parameters to pass to the import internal method.
164
165In scalar context, returns 1 if successful, 0 if not.
166In list context, returns 1 if successful, C<(0, "error message")> if not.
167
168=head2 use_lib
169
170Runs a "C<use lib qw(path1 path2)>" at run time instead of compile time.
171
172    use Dancer::ModuleLoader;
173    ...
174    Dancer::ModuleLoader->use_lib('path1', @other_paths)
175        or die "Couldn't perform use lib\n";
176
177    my ($res, $error) = Dancer::ModuleLoader->use_lib('path1', @other_paths);
178    $res or die "Couldn't perform use lib : '$error'\n";
179
180Takes in arguments a list of path to be prepended to C<@INC>, in a similar way
181than C<use lib>. However, this is performed at run time, so the list of paths
182can be generated and dynamic.
183
184In scalar context, returns 1 if successful, 0 if not.
185In list context, returns 1 if successful, C<(0, "error message")> if not.
186
187=head2 class_from_setting
188
189Given a setting in Dancer::Config, composes the class it should be.
190
191This is the function that translates:
192
193    # in config.yaml
194    template: "template_toolkit"
195
196To the class:
197
198    Dancer::Template::TemplateToolkit
199
200Example:
201
202    use Dancer::ModuleLoader;
203    my $class = Dancer::ModuleLoader->class_from_setting(
204        'Dancer::Template' => 'template_toolkit',
205    );
206
207    # $class == 'Dancer::Template::TemplateToolkit
208
209    $class = Dancer::ModuleLoader->class_from_setting(
210        'Dancer::Template' => 'tiny',
211    );
212
213    # class == 'Dancer::Template::Tiny
214
215=head1 SEE ALSO
216
217L<Module::Load>, L<Module::New::Loader>
218
219=head1 AUTHOR
220
221Dancer Core Developers
222
223=head1 COPYRIGHT AND LICENSE
224
225This software is copyright (c) 2010 by Alexis Sukrieh.
226
227This is free software; you can redistribute it and/or modify it under
228the same terms as the Perl 5 programming language system itself.
229
230=cut
231