Ditch Carp::Clan for our own thing
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Optional / Dependencies.pm
1 package DBIx::Class::Optional::Dependencies;
2
3 use warnings;
4 use strict;
5
6 use Carp ();
7
8 # NO EXTERNAL NON-5.8.1 CORE DEPENDENCIES EVER (e.g. C::A::G)
9 # This module is to be loaded by Makefile.PM on a pristine system
10
11 # POD is generated automatically by calling _gen_pod from the
12 # Makefile.PL in $AUTHOR mode
13
14 my $json_any = {
15   'JSON::Any'                     => '1.22',
16 };
17
18 my $moose_basic = {
19   'Moose'                         => '0.98',
20   'MooseX::Types'                 => '0.21',
21 };
22
23 my $replicated = {
24   %$moose_basic,
25 };
26
27 my $admin_basic = {
28   %$moose_basic,
29   %$json_any,
30   'MooseX::Types::Path::Class'    => '0.05',
31   'MooseX::Types::JSON'           => '0.02',
32   'namespace::autoclean'          => '0.09',
33 };
34
35 my $datetime_basic = {
36   'DateTime'                      => '0.55',
37   'DateTime::Format::Strptime'    => '1.2',
38 };
39
40 my $id_shortener = {
41   'Math::BigInt'                  => '1.89',
42   'Math::Base36'                  => '0.07',
43 };
44
45 my $rdbms_sqlite = {
46   'DBD::SQLite'                   => '0',
47 };
48 my $rdbms_pg = {
49   'DBD::Pg'                       => '0',
50 };
51 my $rdbms_mssql_odbc = {
52   'DBD::ODBC'                     => '0',
53 };
54 my $rdbms_mssql_sybase = {
55   'DBD::Sybase'                   => '0',
56 };
57 my $rdbms_mssql_ado = {
58   'DBD::ADO'                      => '0',
59 };
60 my $rdbms_mysql = {
61   'DBD::mysql'                    => '0',
62 };
63 my $rdbms_oracle = {
64   'DBD::Oracle'                   => '0',
65   %$id_shortener,
66 };
67 my $rdbms_ase = {
68   'DBD::Sybase'                   => '0',
69 };
70 my $rdbms_db2 = {
71   'DBD::DB2'                      => '0',
72 };
73 my $rdbms_firebird_odbc = {
74   'DBD::ODBC'                     => '0',
75 };
76
77 my $reqs = {
78   dist => {
79     #'Module::Install::Pod::Inherit' => '0.01',
80   },
81
82   replicated => {
83     req => $replicated,
84     pod => {
85       title => 'Storage::Replicated',
86       desc => 'Modules required for L<DBIx::Class::Storage::DBI::Replicated>',
87     },
88   },
89
90   test_replicated => {
91     req => {
92       %$replicated,
93       'Test::Moose'               => '0',
94     },
95   },
96
97
98   admin => {
99     req => {
100       %$admin_basic,
101     },
102     pod => {
103       title => 'DBIx::Class::Admin',
104       desc => 'Modules required for the DBIx::Class administrative library',
105     },
106   },
107
108   admin_script => {
109     req => {
110       %$moose_basic,
111       %$admin_basic,
112       'Getopt::Long::Descriptive' => '0.081',
113       'Text::CSV'                 => '1.16',
114     },
115     pod => {
116       title => 'dbicadmin',
117       desc => 'Modules required for the CLI DBIx::Class interface dbicadmin',
118     },
119   },
120
121   deploy => {
122     req => {
123       'SQL::Translator'           => '0.11006',
124     },
125     pod => {
126       title => 'Storage::DBI::deploy()',
127       desc => 'Modules required for L<DBIx::Class::Storage::DBI/deploy> and L<DBIx::Class::Storage::DBI/deployment_statements>',
128     },
129   },
130
131   id_shortener => {
132     req => $id_shortener,
133   },
134
135   test_component_accessor => {
136     req => {
137       'Class::Unload'             => '0.07',
138     },
139   },
140
141   test_pod => {
142     req => {
143       'Test::Pod'                 => '1.41',
144     },
145   },
146
147   test_podcoverage => {
148     req => {
149       'Test::Pod::Coverage'       => '1.08',
150       'Pod::Coverage'             => '0.20',
151     },
152   },
153
154   test_notabs => {
155     req => {
156       'Test::NoTabs'              => '0.9',
157     },
158   },
159
160   test_eol => {
161     req => {
162       'Test::EOL'                 => '0.6',
163     },
164   },
165
166   test_prettydebug => {
167     req => $json_any,
168   },
169
170   test_leaks => {
171     req => {
172       'Test::Memory::Cycle'       => '0',
173       'Devel::Cycle'              => '1.10',
174     },
175   },
176
177   test_dt => {
178     req => $datetime_basic,
179   },
180
181   test_dt_sqlite => {
182     req => {
183       %$datetime_basic,
184       # t/36datetime.t
185       # t/60core.t
186       'DateTime::Format::SQLite'  => '0',
187     },
188   },
189
190   test_dt_mysql => {
191     req => {
192       %$datetime_basic,
193       # t/inflate/datetime_mysql.t
194       # (doesn't need Mysql itself)
195       'DateTime::Format::MySQL'   => '0',
196     },
197   },
198
199   test_dt_pg => {
200     req => {
201       %$datetime_basic,
202       # t/inflate/datetime_pg.t
203       # (doesn't need PG itself)
204       'DateTime::Format::Pg'      => '0.16004',
205     },
206   },
207
208   test_cdbicompat => {
209     req => {
210       'DBIx::ContextualFetch'     => '0',
211       'Class::DBI::Plugin::DeepAbstractSearch' => '0',
212       'Class::Trigger'            => '0',
213       'Time::Piece::MySQL'        => '0',
214       'Clone'                     => '0',
215       'Date::Simple'              => '3.03',
216     },
217   },
218
219   # this is just for completeness as SQLite
220   # is a core dep of DBIC for testing
221   rdbms_sqlite => {
222     req => {
223       %$rdbms_sqlite,
224     },
225     pod => {
226       title => 'SQLite support',
227       desc => 'Modules required to connect to SQLite',
228     },
229   },
230
231   rdbms_pg => {
232     req => {
233       %$rdbms_pg,
234     },
235     pod => {
236       title => 'PostgreSQL support',
237       desc => 'Modules required to connect to PostgreSQL',
238     },
239   },
240
241   rdbms_mssql_odbc => {
242     req => {
243       %$rdbms_mssql_odbc,
244     },
245     pod => {
246       title => 'MSSQL support via DBD::ODBC',
247       desc => 'Modules required to connect to MSSQL via DBD::ODBC',
248     },
249   },
250
251   rdbms_mssql_sybase => {
252     req => {
253       %$rdbms_mssql_sybase,
254     },
255     pod => {
256       title => 'MSSQL support via DBD::Sybase',
257       desc => 'Modules required to connect to MSSQL via DBD::Sybase',
258     },
259   },
260
261   rdbms_mssql_ado => {
262     req => {
263       %$rdbms_mssql_ado,
264     },
265     pod => {
266       title => 'MSSQL support via DBD::ADO (Windows only)',
267       desc => 'Modules required to connect to MSSQL via DBD::ADO. This particular DBD is available on Windows only',
268     },
269   },
270
271   rdbms_mysql => {
272     req => {
273       %$rdbms_mysql,
274     },
275     pod => {
276       title => 'MySQL support',
277       desc => 'Modules required to connect to MySQL',
278     },
279   },
280
281   rdbms_oracle => {
282     req => {
283       %$rdbms_oracle,
284     },
285     pod => {
286       title => 'Oracle support',
287       desc => 'Modules required to connect to Oracle',
288     },
289   },
290
291   rdbms_ase => {
292     req => {
293       %$rdbms_ase,
294     },
295     pod => {
296       title => 'Sybase ASE support',
297       desc => 'Modules required to connect to Sybase ASE',
298     },
299   },
300
301   rdbms_db2 => {
302     req => {
303       %$rdbms_db2,
304     },
305     pod => {
306       title => 'DB2 support',
307       desc => 'Modules required to connect to DB2',
308     },
309   },
310
311 # the order does matter because the rdbms support group might require
312 # a different version that the test group
313   test_rdbms_pg => {
314     req => {
315       $ENV{DBICTEST_PG_DSN}
316         ? (
317           %$rdbms_pg,
318           'Sys::SigAction'        => '0',
319           'DBD::Pg'               => '2.009002',
320         ) : ()
321     },
322   },
323
324   test_rdbms_mssql_odbc => {
325     req => {
326       $ENV{DBICTEST_MSSQL_ODBC_DSN}
327         ? (
328           %$rdbms_mssql_odbc,
329         ) : ()
330     },
331   },
332
333   test_rdbms_mssql_ado => {
334     req => {
335       $ENV{DBICTEST_MSSQL_ADO_DSN}
336         ? (
337           %$rdbms_mssql_ado,
338         ) : ()
339     },
340   },
341
342   test_rdbms_mssql_sybase => {
343     req => {
344       $ENV{DBICTEST_MSSQL_DSN}
345         ? (
346           %$rdbms_mssql_sybase,
347         ) : ()
348     },
349   },
350
351   test_rdbms_mysql => {
352     req => {
353       $ENV{DBICTEST_MYSQL_DSN}
354         ? (
355           %$rdbms_mysql,
356         ) : ()
357     },
358   },
359
360   test_rdbms_oracle => {
361     req => {
362       $ENV{DBICTEST_ORA_DSN}
363         ? (
364           %$rdbms_oracle,
365           'DateTime::Format::Oracle' => '0',
366           'DBD::Oracle'              => '1.24',
367         ) : ()
368     },
369   },
370
371   test_rdbms_ase => {
372     req => {
373       $ENV{DBICTEST_SYBASE_DSN}
374         ? (
375           %$rdbms_ase,
376           'DateTime::Format::Sybase' => '0',
377         ) : ()
378     },
379   },
380
381   test_rdbms_db2 => {
382     req => {
383       $ENV{DBICTEST_DB2_DSN}
384         ? (
385           %$rdbms_db2,
386         ) : ()
387     },
388   },
389
390   test_rdbms_firebird_odbc => {
391     req => {
392       $ENV{DBICTEST_FIREBIRD_ODBC_DSN}
393         ? (
394           %$rdbms_firebird_odbc,
395         ) : ()
396     },
397   },
398
399   test_memcached => {
400     req => {
401       $ENV{DBICTEST_MEMCACHED}
402         ? (
403           'Cache::Memcached' => 0,
404         ) : ()
405     },
406   },
407
408 };
409
410
411 sub req_list_for {
412   my ($class, $group) = @_;
413
414   Carp::croak "req_list_for() expects a requirement group name"
415     unless $group;
416
417   my $deps = $reqs->{$group}{req}
418     or Carp::croak "Requirement group '$group' does not exist";
419
420   return { %$deps };
421 }
422
423
424 our %req_availability_cache;
425 sub req_ok_for {
426   my ($class, $group) = @_;
427
428   Carp::croak "req_ok_for() expects a requirement group name"
429     unless $group;
430
431   return $class->_check_deps($group)->{status};
432 }
433
434 sub req_missing_for {
435   my ($class, $group) = @_;
436
437   Carp::croak "req_missing_for() expects a requirement group name"
438     unless $group;
439
440   return $class->_check_deps($group)->{missing};
441 }
442
443 sub req_errorlist_for {
444   my ($class, $group) = @_;
445
446   Carp::croak "req_errorlist_for() expects a requirement group name"
447     unless $group;
448
449   return $class->_check_deps($group)->{errorlist};
450 }
451
452 sub _check_deps {
453   my ($class, $group) = @_;
454
455   return $req_availability_cache{$group} ||= do {
456
457     my $deps = $class->req_list_for ($group);
458
459     my %errors;
460     for my $mod (keys %$deps) {
461       my $req_line = "require $mod;";
462       if (my $ver = $deps->{$mod}) {
463         $req_line .= "$mod->VERSION($ver);";
464       }
465
466       eval $req_line;
467
468       $errors{$mod} = $@ if $@;
469     }
470
471     my $res;
472
473     if (keys %errors) {
474       my $missing = join (', ', map { $deps->{$_} ? "$_ >= $deps->{$_}" : $_ } (sort keys %errors) );
475       $missing .= " (see $class for details)" if $reqs->{$group}{pod};
476       $missing .= "\n";
477       $res = {
478         status => 0,
479         errorlist => \%errors,
480         missing => $missing,
481       };
482     }
483     else {
484       $res = {
485         status => 1,
486         errorlist => {},
487         missing => '',
488       };
489     }
490
491     $res;
492   };
493 }
494
495 sub req_group_list {
496   return { map { $_ => { %{ $reqs->{$_}{req} || {} } } } (keys %$reqs) };
497 }
498
499 # This is to be called by the author only (automatically in Makefile.PL)
500 sub _gen_pod {
501   my ($class, $distver) = @_;
502
503   my $modfn = __PACKAGE__ . '.pm';
504   $modfn =~ s/\:\:/\//g;
505
506   my $podfn = __FILE__;
507   $podfn =~ s/\.pm$/\.pod/;
508
509   $distver ||=
510     eval { require DBIx::Class; DBIx::Class->VERSION; }
511       ||
512     die
513 "\n\n---------------------------------------------------------------------\n" .
514 'Unable to load core DBIx::Class module to determine current version, '.
515 'possibly due to missing dependencies. Author-mode autodocumentation ' .
516 "halted\n\n" . $@ .
517 "\n\n---------------------------------------------------------------------\n"
518   ;
519
520   my $sqltver = $class->req_list_for ('deploy')->{'SQL::Translator'}
521     or die "Hrmm? No sqlt dep?";
522
523   my @chunks = (
524     <<'EOC',
525 #########################################################################
526 #####################  A U T O G E N E R A T E D ########################
527 #########################################################################
528 #
529 # The contents of this POD file are auto-generated.  Any changes you make
530 # will be lost. If you need to change the generated text edit _gen_pod()
531 # at the end of $modfn
532 #
533 EOC
534     '=head1 NAME',
535     "$class - Optional module dependency specifications (for module authors)",
536     '=head1 SYNOPSIS',
537     <<"EOS",
538 Somewhere in your build-file (e.g. L<Module::Install>'s Makefile.PL):
539
540   ...
541
542   configure_requires 'DBIx::Class' => '$distver';
543
544   require $class;
545
546   my \$deploy_deps = $class->req_list_for('deploy');
547
548   for (keys %\$deploy_deps) {
549     requires \$_ => \$deploy_deps->{\$_};
550   }
551
552   ...
553
554 Note that there are some caveats regarding C<configure_requires()>, more info
555 can be found at L<Module::Install/configure_requires>
556 EOS
557     '=head1 DESCRIPTION',
558     <<'EOD',
559 Some of the less-frequently used features of L<DBIx::Class> have external
560 module dependencies on their own. In order not to burden the average user
561 with modules he will never use, these optional dependencies are not included
562 in the base Makefile.PL. Instead an exception with a descriptive message is
563 thrown when a specific feature is missing one or several modules required for
564 its operation. This module is the central holding place for  the current list
565 of such dependencies, for DBIx::Class core authors, and DBIx::Class extension
566 authors alike.
567 EOD
568     '=head1 CURRENT REQUIREMENT GROUPS',
569     <<'EOD',
570 Dependencies are organized in C<groups> and each group can list one or more
571 required modules, with an optional minimum version (or 0 for any version).
572 The group name can be used in the
573 EOD
574   );
575
576   for my $group (sort keys %$reqs) {
577     my $p = $reqs->{$group}{pod}
578       or next;
579
580     my $modlist = $reqs->{$group}{req}
581       or next;
582
583     next unless keys %$modlist;
584
585     push @chunks, (
586       "=head2 $p->{title}",
587       "$p->{desc}",
588       '=over',
589       ( map { "=item * $_" . ($modlist->{$_} ? " >= $modlist->{$_}" : '') } (sort keys %$modlist) ),
590       '=back',
591       "Requirement group: B<$group>",
592     );
593   }
594
595   push @chunks, (
596     '=head1 METHODS',
597     '=head2 req_group_list',
598     '=over',
599     '=item Arguments: none',
600     '=item Returns: \%list_of_requirement_groups',
601     '=back',
602     <<'EOD',
603 This method should be used by DBIx::Class packagers, to get a hashref of all
604 dependencies keyed by dependency group. Each key (group name) can be supplied
605 to one of the group-specific methods below.
606 EOD
607
608     '=head2 req_list_for',
609     '=over',
610     '=item Arguments: $group_name',
611     '=item Returns: \%list_of_module_version_pairs',
612     '=back',
613     <<'EOD',
614 This method should be used by DBIx::Class extension authors, to determine the
615 version of modules a specific feature requires in the B<current> version of
616 DBIx::Class. See the L</SYNOPSIS> for a real-world
617 example.
618 EOD
619
620     '=head2 req_ok_for',
621     '=over',
622     '=item Arguments: $group_name',
623     '=item Returns: 1|0',
624     '=back',
625     <<'EOD',
626 Returns true or false depending on whether all modules required by
627 C<$group_name> are present on the system and loadable.
628 EOD
629
630     '=head2 req_missing_for',
631     '=over',
632     '=item Arguments: $group_name',
633     '=item Returns: $error_message_string',
634     '=back',
635     <<"EOD",
636 Returns a single line string suitable for inclusion in larger error messages.
637 This method would normally be used by DBIx::Class core-module author, to
638 indicate to the user that he needs to install specific modules before he will
639 be able to use a specific feature.
640
641 For example if some of the requirements for C<deploy> are not available,
642 the returned string could look like:
643
644  SQL::Translator >= $sqltver (see $class for details)
645
646 The author is expected to prepend the necessary text to this message before
647 returning the actual error seen by the user.
648 EOD
649
650     '=head2 req_errorlist_for',
651     '=over',
652     '=item Arguments: $group_name',
653     '=item Returns: \%list_of_loaderrors_per_module',
654     '=back',
655     <<'EOD',
656 Returns a hashref containing the actual errors that occured while attempting
657 to load each module in the requirement group.
658 EOD
659     '=head1 AUTHOR',
660     'See L<DBIx::Class/CONTRIBUTORS>.',
661     '=head1 LICENSE',
662     'You may distribute this code under the same terms as Perl itself',
663   );
664
665   open (my $fh, '>', $podfn) or Carp::croak "Unable to write to $podfn: $!";
666   print $fh join ("\n\n", @chunks);
667   close ($fh);
668 }
669
670 1;