Fail gracefully on Config::Any PROFILE-loading errors
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Optional / Dependencies.pm
CommitLineData
8057ed01 1package DBIx::Class::Optional::Dependencies;
2
3use warnings;
4use strict;
5
fb39747c 6use Carp;
7
8# NO EXTERNAL NON-5.8.1 CORE DEPENDENCIES EVER (e.g. C::A::G)
8057ed01 9# This module is to be loaded by Makefile.PM on a pristine system
10
f6b26571 11# POD is generated automatically by calling _gen_pod from the
12# Makefile.PL in $AUTHOR mode
13
2a2a7b23 14my $json_any = {
15 'JSON::Any' => '1.22',
16};
17
2b48ebff 18my $moose_basic = {
68de9438 19 'Moose' => '0.98',
20 'MooseX::Types' => '0.21',
21};
22
23my $replicated = {
24 %$moose_basic,
25 'Hash::Merge' => '0.12',
2b48ebff 26};
27
ebcd0e4f 28my $admin_basic = {
29 %$moose_basic,
2a2a7b23 30 %$json_any,
68de9438 31 'MooseX::Types::Path::Class' => '0.05',
32 'MooseX::Types::JSON' => '0.02',
68de9438 33 'namespace::autoclean' => '0.09',
34};
35
36my $datetime_basic = {
37 'DateTime' => '0.55',
38 'DateTime::Format::Strptime' => '1.2',
ebcd0e4f 39};
40
8057ed01 41my $reqs = {
42 dist => {
43 #'Module::Install::Pod::Inherit' => '0.01',
44 },
45
46 replicated => {
68de9438 47 req => $replicated,
f6b26571 48 pod => {
49 title => 'Storage::Replicated',
50 desc => 'Modules required for L<DBIx::Class::Storage::DBI::Replicated>',
51 },
8057ed01 52 },
53
68de9438 54 test_replicated => {
55 req => {
56 %$replicated,
57 'Test::Moose' => '0',
58 },
59 },
60
61
8057ed01 62 admin => {
2b48ebff 63 req => {
ebcd0e4f 64 %$admin_basic,
65 },
66 pod => {
67 title => 'DBIx::Class::Admin',
68 desc => 'Modules required for the DBIx::Class administrative library',
69 },
70 },
71
a4a02f15 72 admin_script => {
ebcd0e4f 73 req => {
2b48ebff 74 %$moose_basic,
ebcd0e4f 75 %$admin_basic,
2b48ebff 76 'Getopt::Long::Descriptive' => '0.081',
2b48ebff 77 'Text::CSV' => '1.16',
78 },
e144415f 79 pod => {
80 title => 'dbicadmin',
81 desc => 'Modules required for the CLI DBIx::Class interface dbicadmin',
82 },
8057ed01 83 },
84
85 deploy => {
f6b26571 86 req => {
9e34d554 87 'SQL::Translator' => '0.11006',
f6b26571 88 },
89 pod => {
90 title => 'Storage::DBI::deploy()',
91 desc => 'Modules required for L<DBIx::Class::Storage::DBI/deploy> and L<DBIx::Class::Storage::DBI/deploymen_statements>',
92 },
8057ed01 93 },
94
a109c954 95 test_pod => {
f6b26571 96 req => {
14d17d71 97 'Test::Pod' => '1.41',
a109c954 98 },
99 },
100
101 test_podcoverage => {
102 req => {
f6b26571 103 'Test::Pod::Coverage' => '1.08',
104 'Pod::Coverage' => '0.20',
a109c954 105 },
106 },
107
108 test_notabs => {
109 req => {
d146b340 110 'Test::NoTabs' => '0.9',
a109c954 111 },
112 },
113
114 test_eol => {
115 req => {
d146b340 116 'Test::EOL' => '0.6',
f6b26571 117 },
8057ed01 118 },
119
2a2a7b23 120 test_prettydebug => {
121 req => $json_any,
122 },
123
226d1c35 124 test_leaks => {
f6b26571 125 req => {
f6b26571 126 'Test::Memory::Cycle' => '0',
127 'Devel::Cycle' => '1.10',
a109c954 128 },
129 },
f6b26571 130
68de9438 131 test_dt => {
132 req => $datetime_basic,
133 },
134
135 test_dt_sqlite => {
a109c954 136 req => {
68de9438 137 %$datetime_basic,
f6b26571 138 # t/36datetime.t
139 # t/60core.t
140 'DateTime::Format::SQLite' => '0',
68de9438 141 },
142 },
f6b26571 143
68de9438 144 test_dt_mysql => {
145 req => {
146 %$datetime_basic,
9c92bb1c 147 # t/inflate/datetime_mysql.t
148 # (doesn't need Mysql itself)
68de9438 149 'DateTime::Format::MySQL' => '0',
150 },
151 },
9c92bb1c 152
68de9438 153 test_dt_pg => {
154 req => {
155 %$datetime_basic,
9c92bb1c 156 # t/inflate/datetime_pg.t
157 # (doesn't need PG itself)
ab35aeab 158 'DateTime::Format::Pg' => '0.16004',
f6b26571 159 },
8057ed01 160 },
161
68de9438 162 test_cdbicompat => {
f6b26571 163 req => {
164 'DBIx::ContextualFetch' => '0',
165 'Class::DBI::Plugin::DeepAbstractSearch' => '0',
166 'Class::Trigger' => '0',
167 'Time::Piece::MySQL' => '0',
168 'Clone' => '0',
169 'Date::Simple' => '3.03',
170 },
8057ed01 171 },
172
68de9438 173 test_rdbms_pg => {
f6b26571 174 req => {
175 $ENV{DBICTEST_PG_DSN}
176 ? (
177 'Sys::SigAction' => '0',
178 'DBD::Pg' => '2.009002',
f6b26571 179 ) : ()
180 },
8057ed01 181 },
182
afae8507 183 test_rdbms_mssql_odbc => {
184 req => {
185 $ENV{DBICTEST_MSSQL_ODBC_DSN}
186 ? (
187 'DBD::ODBC' => '0',
188 ) : ()
189 },
190 },
191
192 test_rdbms_mssql_sybase => {
193 req => {
194 $ENV{DBICTEST_MSSQL_DSN}
195 ? (
196 'DBD::Sybase' => '0',
197 ) : ()
198 },
199 },
200
68de9438 201 test_rdbms_mysql => {
f6b26571 202 req => {
203 $ENV{DBICTEST_MYSQL_DSN}
204 ? (
afae8507 205 'DBD::mysql' => '0',
f6b26571 206 ) : ()
207 },
8057ed01 208 },
209
68de9438 210 test_rdbms_oracle => {
f6b26571 211 req => {
212 $ENV{DBICTEST_ORA_DSN}
213 ? (
214 'DateTime::Format::Oracle' => '0',
e1b7e9b4 215 'DBD::Oracle' => '1.24',
f6b26571 216 ) : ()
217 },
8057ed01 218 },
219
68de9438 220 test_rdbms_ase => {
f6b26571 221 req => {
222 $ENV{DBICTEST_SYBASE_DSN}
223 ? (
224 'DateTime::Format::Sybase' => 0,
225 ) : ()
226 },
8057ed01 227 },
228
68de9438 229 test_rdbms_db2 => {
f58a165c 230 req => {
231 $ENV{DBICTEST_DB2_DSN}
232 ? (
233 'DBD::DB2' => 0,
234 ) : ()
235 },
236 },
237
8057ed01 238};
239
f6b26571 240
fb39747c 241sub req_list_for {
242 my ($class, $group) = @_;
243
f6b26571 244 croak "req_list_for() expects a requirement group name"
fb39747c 245 unless $group;
246
f6b26571 247 my $deps = $reqs->{$group}{req}
248 or croak "Requirement group '$group' does not exist";
fb39747c 249
250 return { %$deps };
251}
252
253
254our %req_availability_cache;
255sub req_ok_for {
256 my ($class, $group) = @_;
257
258 croak "req_ok_for() expects a requirement group name"
259 unless $group;
260
d8799bab 261 return $class->_check_deps($group)->{status};
fb39747c 262}
263
264sub req_missing_for {
265 my ($class, $group) = @_;
266
267 croak "req_missing_for() expects a requirement group name"
268 unless $group;
269
d8799bab 270 return $class->_check_deps($group)->{missing};
fb39747c 271}
272
273sub req_errorlist_for {
274 my ($class, $group) = @_;
275
276 croak "req_errorlist_for() expects a requirement group name"
277 unless $group;
278
d8799bab 279 return $class->_check_deps($group)->{errorlist};
fb39747c 280}
281
282sub _check_deps {
283 my ($class, $group) = @_;
284
d8799bab 285 return $req_availability_cache{$group} ||= do {
286
287 my $deps = $class->req_list_for ($group);
288
289 my %errors;
290 for my $mod (keys %$deps) {
291 my $req_line = "require $mod;";
292 if (my $ver = $deps->{$mod}) {
293 $req_line .= "$mod->VERSION($ver);";
294 }
295
296 eval $req_line;
297
298 $errors{$mod} = $@ if $@;
299 }
300
301 my $res;
fb39747c 302
d8799bab 303 if (keys %errors) {
304 my $missing = join (', ', map { $deps->{$_} ? "$_ >= $deps->{$_}" : $_ } (sort keys %errors) );
305 $missing .= " (see $class for details)" if $reqs->{$group}{pod};
306 $res = {
307 status => 0,
308 errorlist => \%errors,
309 missing => $missing,
310 };
fb39747c 311 }
312 else {
d8799bab 313 $res = {
314 status => 1,
315 errorlist => {},
316 missing => '',
317 };
fb39747c 318 }
319
d8799bab 320 $res;
321 };
fb39747c 322}
323
e3fc11e1 324sub req_group_list {
325 return { map { $_ => { %{ $reqs->{$_}{req} || {} } } } (keys %$reqs) };
326}
327
328# This is to be called by the author only (automatically in Makefile.PL)
f6b26571 329sub _gen_pod {
ccebe1f1 330 my ($class, $distver) = @_;
31fa1764 331
af4ac504 332 my $modfn = __PACKAGE__ . '.pm';
333 $modfn =~ s/\:\:/\//g;
f6b26571 334
31fa1764 335 my $podfn = __FILE__;
336 $podfn =~ s/\.pm$/\.pod/;
337
ccebe1f1 338 $distver ||=
31fa1764 339 eval { require DBIx::Class; DBIx::Class->VERSION; }
340 ||
ccebe1f1 341 die
31fa1764 342"\n\n---------------------------------------------------------------------\n" .
343'Unable to load core DBIx::Class module to determine current version, '.
344'possibly due to missing dependencies. Author-mode autodocumentation ' .
345"halted\n\n" . $@ .
346"\n\n---------------------------------------------------------------------\n"
31fa1764 347 ;
348
e3fc11e1 349 my $sqltver = $class->req_list_for ('deploy')->{'SQL::Translator'}
350 or die "Hrmm? No sqlt dep?";
7e3dc46f 351
f6b26571 352 my @chunks = (
d8799bab 353 <<'EOC',
af4ac504 354#########################################################################
355##################### A U T O G E N E R A T E D ########################
356#########################################################################
357#
358# The contents of this POD file are auto-generated. Any changes you make
359# will be lost. If you need to change the generated text edit _gen_pod()
360# at the end of $modfn
361#
362EOC
f6b26571 363 '=head1 NAME',
7e3dc46f 364 "$class - Optional module dependency specifications (for module authors)",
e3fc11e1 365 '=head1 SYNOPSIS',
d8799bab 366 <<"EOS",
7e3dc46f 367Somewhere in your build-file (e.g. L<Module::Install>'s Makefile.PL):
368
369 ...
370
371 configure_requires 'DBIx::Class' => '$distver';
372
373 require $class;
374
375 my \$deploy_deps = $class->req_list_for ('deploy');
376
377 for (keys %\$deploy_deps) {
378 requires \$_ => \$deploy_deps->{\$_};
379 }
380
381 ...
382
383Note that there are some caveats regarding C<configure_requires()>, more info
384can be found at L<Module::Install/configure_requires>
385EOS
f6b26571 386 '=head1 DESCRIPTION',
387 <<'EOD',
388Some of the less-frequently used features of L<DBIx::Class> have external
389module dependencies on their own. In order not to burden the average user
390with modules he will never use, these optional dependencies are not included
391in the base Makefile.PL. Instead an exception with a descriptive message is
392thrown when a specific feature is missing one or several modules required for
393its operation. This module is the central holding place for the current list
7e3dc46f 394of such dependencies, for DBIx::Class core authors, and DBIx::Class extension
395authors alike.
f6b26571 396EOD
397 '=head1 CURRENT REQUIREMENT GROUPS',
398 <<'EOD',
399Dependencies are organized in C<groups> and each group can list one or more
400required modules, with an optional minimum version (or 0 for any version).
ecb68746 401The group name can be used in the
f6b26571 402EOD
403 );
404
405 for my $group (sort keys %$reqs) {
406 my $p = $reqs->{$group}{pod}
407 or next;
408
409 my $modlist = $reqs->{$group}{req}
410 or next;
411
412 next unless keys %$modlist;
413
414 push @chunks, (
415 "=head2 $p->{title}",
416 "$p->{desc}",
417 '=over',
418 ( map { "=item * $_" . ($modlist->{$_} ? " >= $modlist->{$_}" : '') } (sort keys %$modlist) ),
419 '=back',
420 "Requirement group: B<$group>",
421 );
422 }
423
424 push @chunks, (
425 '=head1 METHODS',
e3fc11e1 426 '=head2 req_group_list',
427 '=over',
d8799bab 428 '=item Arguments: none',
e3fc11e1 429 '=item Returns: \%list_of_requirement_groups',
430 '=back',
d8799bab 431 <<'EOD',
e3fc11e1 432This method should be used by DBIx::Class packagers, to get a hashref of all
433dependencies keyed by dependency group. Each key (group name) can be supplied
434to one of the group-specific methods below.
435EOD
436
f6b26571 437 '=head2 req_list_for',
438 '=over',
439 '=item Arguments: $group_name',
440 '=item Returns: \%list_of_module_version_pairs',
441 '=back',
d8799bab 442 <<'EOD',
f6b26571 443This method should be used by DBIx::Class extension authors, to determine the
7e3dc46f 444version of modules a specific feature requires in the B<current> version of
e3fc11e1 445DBIx::Class. See the L</SYNOPSIS> for a real-world
7e3dc46f 446example.
f6b26571 447EOD
448
449 '=head2 req_ok_for',
450 '=over',
451 '=item Arguments: $group_name',
452 '=item Returns: 1|0',
453 '=back',
d8799bab 454 <<'EOD',
455Returns true or false depending on whether all modules required by
456C<$group_name> are present on the system and loadable.
457EOD
f6b26571 458
459 '=head2 req_missing_for',
460 '=over',
461 '=item Arguments: $group_name',
462 '=item Returns: $error_message_string',
463 '=back',
d8799bab 464 <<"EOD",
f6b26571 465Returns a single line string suitable for inclusion in larger error messages.
466This method would normally be used by DBIx::Class core-module author, to
467indicate to the user that he needs to install specific modules before he will
468be able to use a specific feature.
469
e3fc11e1 470For example if some of the requirements for C<deploy> are not available,
471the returned string could look like:
f6b26571 472
e3fc11e1 473 SQL::Translator >= $sqltver (see $class for details)
f6b26571 474
475The author is expected to prepend the necessary text to this message before
476returning the actual error seen by the user.
477EOD
478
479 '=head2 req_errorlist_for',
480 '=over',
481 '=item Arguments: $group_name',
482 '=item Returns: \%list_of_loaderrors_per_module',
483 '=back',
484 <<'EOD',
485Returns a hashref containing the actual errors that occured while attempting
486to load each module in the requirement group.
487EOD
fb8ae353 488 '=head1 AUTHOR',
489 'See L<DBIx::Class/CONTRIBUTORS>.',
490 '=head1 LICENSE',
491 'You may distribute this code under the same terms as Perl itself',
f6b26571 492 );
493
31fa1764 494 open (my $fh, '>', $podfn) or croak "Unable to write to $podfn: $!";
f6b26571 495 print $fh join ("\n\n", @chunks);
496 close ($fh);
497}
498
8057ed01 4991;