Add import-time action stub to OptDeps, switch distbuild checks to it
[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 # NOTE: the rationale for 2 JSON::Any versions is that
15 # we need the newer only to work around JSON::XS, which
16 # itself is an optional dep
17 my $min_json_any = {
18   'JSON::Any'                     => '1.23',
19 };
20 my $test_and_dist_json_any = {
21   'JSON::Any'                     => '1.31',
22 };
23
24 my $moose_basic = {
25   'Moose'                         => '0.98',
26   'MooseX::Types'                 => '0.21',
27   'MooseX::Types::LoadableClass'  => '0.011',
28 };
29
30 my $dbic_reqs = {
31   replicated => {
32     req => $moose_basic,
33     pod => {
34       title => 'Storage::Replicated',
35       desc => 'Modules required for L<DBIx::Class::Storage::DBI::Replicated>',
36     },
37   },
38
39   test_replicated => {
40     include => 'replicated',
41     req => {
42       'Test::Moose' => '0',
43     },
44   },
45
46   admin => {
47     req => {
48       %$moose_basic,
49       %$min_json_any,
50       'MooseX::Types::Path::Class' => '0.05',
51       'MooseX::Types::JSON' => '0.02',
52     },
53     pod => {
54       title => 'DBIx::Class::Admin',
55       desc => 'Modules required for the DBIx::Class administrative library',
56     },
57   },
58
59   admin_script => {
60     include => 'admin',
61     req => {
62       'Getopt::Long::Descriptive' => '0.081',
63       'Text::CSV' => '1.16',
64     },
65     pod => {
66       title => 'dbicadmin',
67       desc => 'Modules required for the CLI DBIx::Class interface dbicadmin',
68     },
69   },
70
71   deploy => {
72     req => {
73       'SQL::Translator'           => '0.11018',
74     },
75     pod => {
76       title => 'Storage::DBI::deploy()',
77       desc => 'Modules required for L<DBIx::Class::Storage::DBI/deployment_statements> and L<DBIx::Class::Schema/deploy>',
78     },
79   },
80
81   id_shortener => {
82     req => {
83       'Math::BigInt' => '1.80',
84       'Math::Base36' => '0.07',
85     },
86   },
87
88   test_component_accessor => {
89     req => {
90       'Class::Unload'             => '0.07',
91     },
92   },
93
94   test_pod => {
95     req => {
96       'Test::Pod'                 => '1.42',
97     },
98   },
99
100   test_podcoverage => {
101     req => {
102       'Test::Pod::Coverage'       => '1.08',
103       'Pod::Coverage'             => '0.20',
104     },
105   },
106
107   test_whitespace => {
108     req => {
109       'Test::EOL'                 => '1.0',
110       'Test::NoTabs'              => '0.9',
111     },
112   },
113
114   test_strictures => {
115     req => {
116       'Test::Strict'              => '0.20',
117     },
118   },
119
120   test_prettydebug => {
121     req => $min_json_any,
122   },
123
124   test_admin_script => {
125     include => 'admin_script',
126     req => {
127       %$test_and_dist_json_any,
128       'JSON' => 0,
129       'JSON::PP' => 0,
130       'Cpanel::JSON::XS' => 0,
131       'JSON::XS' => 0,
132       $^O eq 'MSWin32'
133         # for t/admin/10script.t
134         ? ('Win32::ShellQuote' => 0)
135         # DWIW does not compile (./configure even) on win32
136         : ('JSON::DWIW' => 0 )
137       ,
138     }
139   },
140
141   test_leaks_heavy => {
142     req => {
143       'Class::MethodCache' => '0.02',
144       'PadWalker' => '1.06',
145     },
146   },
147
148   test_dt => {
149     req => {
150       'DateTime'                    => '0.55',
151       'DateTime::Format::Strptime'  => '1.2',
152     },
153   },
154
155   test_dt_sqlite => {
156     include => 'test_dt',
157     req => {
158       # t/36datetime.t
159       # t/60core.t
160       'DateTime::Format::SQLite'  => '0',
161     },
162   },
163
164   test_dt_mysql => {
165     include => 'test_dt',
166     req => {
167       # t/inflate/datetime_mysql.t
168       # (doesn't need Mysql itself)
169       'DateTime::Format::MySQL'   => '0',
170     },
171   },
172
173   test_dt_pg => {
174     include => 'test_dt',
175     req => {
176       # t/inflate/datetime_pg.t
177       # (doesn't need PG itself)
178       'DateTime::Format::Pg'      => '0.16004',
179     },
180   },
181
182   test_cdbicompat => {
183     include => 'test_dt',
184     req => {
185       'Class::DBI::Plugin::DeepAbstractSearch' => '0',
186       'Time::Piece::MySQL'        => '0',
187       'Date::Simple'              => '3.03',
188     },
189   },
190
191   rdbms_generic_odbc => {
192     req => {
193       'DBD::ODBC' => 0,
194     }
195   },
196
197   rdbms_generic_ado => {
198     req => {
199       'DBD::ADO' => 0,
200     }
201   },
202
203   # this is just for completeness as SQLite
204   # is a core dep of DBIC for testing
205   rdbms_sqlite => {
206     req => {
207       'DBD::SQLite' => 0,
208     },
209     pod => {
210       title => 'SQLite support',
211       desc => 'Modules required to connect to SQLite',
212     },
213   },
214
215   rdbms_pg => {
216     req => {
217       # when changing this list make sure to adjust xt/optional_deps.t
218       'DBD::Pg' => 0,
219     },
220     pod => {
221       title => 'PostgreSQL support',
222       desc => 'Modules required to connect to PostgreSQL',
223     },
224   },
225
226   rdbms_mssql_odbc => {
227     include => 'rdbms_generic_odbc',
228     pod => {
229       title => 'MSSQL support via DBD::ODBC',
230       desc => 'Modules required to connect to MSSQL via DBD::ODBC',
231     },
232   },
233
234   rdbms_mssql_sybase => {
235     req => {
236       'DBD::Sybase' => 0,
237     },
238     pod => {
239       title => 'MSSQL support via DBD::Sybase',
240       desc => 'Modules required to connect to MSSQL via DBD::Sybase',
241     },
242   },
243
244   rdbms_mssql_ado => {
245     include => 'rdbms_generic_ado',
246     pod => {
247       title => 'MSSQL support via DBD::ADO (Windows only)',
248       desc => 'Modules required to connect to MSSQL via DBD::ADO. This particular DBD is available on Windows only',
249     },
250   },
251
252   rdbms_msaccess_odbc => {
253     include => 'rdbms_generic_odbc',
254     pod => {
255       title => 'MS Access support via DBD::ODBC',
256       desc => 'Modules required to connect to MS Access via DBD::ODBC',
257     },
258   },
259
260   rdbms_msaccess_ado => {
261     include => 'rdbms_generic_ado',
262     pod => {
263       title => 'MS Access support via DBD::ADO (Windows only)',
264       desc => 'Modules required to connect to MS Access via DBD::ADO. This particular DBD is available on Windows only',
265     },
266   },
267
268   rdbms_mysql => {
269     req => {
270       'DBD::mysql' => 0,
271     },
272     pod => {
273       title => 'MySQL support',
274       desc => 'Modules required to connect to MySQL',
275     },
276   },
277
278   rdbms_oracle => {
279     include => 'id_shortener',
280     req => {
281       'DBD::Oracle' => 0,
282     },
283     pod => {
284       title => 'Oracle support',
285       desc => 'Modules required to connect to Oracle',
286     },
287   },
288
289   rdbms_ase => {
290     req => {
291       'DBD::Sybase' => 0,
292     },
293     pod => {
294       title => 'Sybase ASE support',
295       desc => 'Modules required to connect to Sybase ASE',
296     },
297   },
298
299   rdbms_db2 => {
300     req => {
301       'DBD::DB2' => 0,
302     },
303     pod => {
304       title => 'DB2 support',
305       desc => 'Modules required to connect to DB2',
306     },
307   },
308
309   rdbms_db2_400 => {
310     include => 'rdbms_generic_odbc',
311     pod => {
312       title => 'DB2 on AS/400 support',
313       desc => 'Modules required to connect to DB2 on AS/400',
314     },
315   },
316
317   rdbms_informix => {
318     req => {
319       'DBD::Informix' => 0,
320     },
321     pod => {
322       title => 'Informix support',
323       desc => 'Modules required to connect to Informix',
324     },
325   },
326
327   rdbms_sqlanywhere => {
328     req => {
329       'DBD::SQLAnywhere' => 0,
330     },
331     pod => {
332       title => 'SQLAnywhere support',
333       desc => 'Modules required to connect to SQLAnywhere',
334     },
335   },
336
337   rdbms_sqlanywhere_odbc => {
338     include => 'rdbms_generic_odbc',
339     pod => {
340       title => 'SQLAnywhere support via DBD::ODBC',
341       desc => 'Modules required to connect to SQLAnywhere via DBD::ODBC',
342     },
343   },
344
345   rdbms_firebird => {
346     req => {
347       'DBD::Firebird' => 0,
348     },
349     pod => {
350       title => 'Firebird support',
351       desc => 'Modules required to connect to Firebird',
352     },
353   },
354
355   rdbms_firebird_interbase => {
356     req => {
357       'DBD::InterBase' => 0,
358     },
359     pod => {
360       title => 'Firebird support via DBD::InterBase',
361       desc => 'Modules required to connect to Firebird via DBD::InterBase',
362     },
363   },
364
365   rdbms_firebird_odbc => {
366     include => 'rdbms_generic_odbc',
367     pod => {
368       title => 'Firebird support via DBD::ODBC',
369       desc => 'Modules required to connect to Firebird via DBD::ODBC',
370     },
371   },
372
373   test_rdbms_pg => {
374     include => 'rdbms_pg',
375     env => [
376       DBICTEST_PG_DSN => 1,
377       DBICTEST_PG_USER => 0,
378       DBICTEST_PG_PASS => 0,
379     ],
380     req => {
381       # the order does matter because the rdbms support group might require
382       # a different version that the test group
383       #
384       # when changing this list make sure to adjust xt/optional_deps.t
385       'DBD::Pg' => '2.009002',  # specific version to test bytea
386     },
387   },
388
389   test_rdbms_mssql_odbc => {
390     include => 'rdbms_mssql_odbc',
391     env => [
392       DBICTEST_MSSQL_ODBC_DSN => 1,
393       DBICTEST_MSSQL_ODBC_USER => 0,
394       DBICTEST_MSSQL_ODBC_PASS => 0,
395     ],
396   },
397
398   test_rdbms_mssql_ado => {
399     include => 'rdbms_mssql_ado',
400     env => [
401       DBICTEST_MSSQL_ADO_DSN => 1,
402       DBICTEST_MSSQL_ADO_USER => 0,
403       DBICTEST_MSSQL_ADO_PASS => 0,
404     ],
405   },
406
407   test_rdbms_mssql_sybase => {
408     include => 'rdbms_mssql_sybase',
409     env => [
410       DBICTEST_MSSQL_DSN => 1,
411       DBICTEST_MSSQL_USER => 0,
412       DBICTEST_MSSQL_PASS => 0,
413     ],
414   },
415
416   test_rdbms_msaccess_odbc => {
417     include => [qw(rdbms_msaccess_odbc test_dt)],
418     env => [
419       DBICTEST_MSACCESS_ODBC_DSN => 1,
420       DBICTEST_MSACCESS_ODBC_USER => 0,
421       DBICTEST_MSACCESS_ODBC_PASS => 0,
422     ],
423     req => {
424       'Data::GUID' => '0',
425     },
426   },
427
428   test_rdbms_msaccess_ado => {
429     include => [qw(rdbms_msaccess_ado test_dt)],
430     env => [
431       DBICTEST_MSACCESS_ADO_DSN => 1,
432       DBICTEST_MSACCESS_ADO_USER => 0,
433       DBICTEST_MSACCESS_ADO_PASS => 0,
434     ],
435     req => {
436       'Data::GUID' => 0,
437     },
438   },
439
440   test_rdbms_mysql => {
441     include => 'rdbms_mysql',
442     env => [
443       DBICTEST_MYSQL_DSN => 1,
444       DBICTEST_MYSQL_USER => 0,
445       DBICTEST_MYSQL_PASS => 0,
446     ],
447   },
448
449   test_rdbms_oracle => {
450     include => 'rdbms_oracle',
451     env => [
452       DBICTEST_ORA_DSN => 1,
453       DBICTEST_ORA_USER => 0,
454       DBICTEST_ORA_PASS => 0,
455     ],
456     req => {
457       'DateTime::Format::Oracle' => '0',
458       'DBD::Oracle'              => '1.24',
459     },
460   },
461
462   test_rdbms_ase => {
463     include => 'rdbms_ase',
464     env => [
465       DBICTEST_SYBASE_DSN => 1,
466       DBICTEST_SYBASE_USER => 0,
467       DBICTEST_SYBASE_PASS => 0,
468     ],
469   },
470
471   test_rdbms_db2 => {
472     include => 'rdbms_db2',
473     env => [
474       DBICTEST_DB2_DSN => 1,
475       DBICTEST_DB2_USER => 0,
476       DBICTEST_DB2_PASS => 0,
477     ],
478   },
479
480   test_rdbms_db2_400 => {
481     include => 'rdbms_db2_400',
482     env => [
483       DBICTEST_DB2_400_DSN => 1,
484       DBICTEST_DB2_400_USER => 0,
485       DBICTEST_DB2_400_PASS => 0,
486     ],
487   },
488
489   test_rdbms_informix => {
490     include => 'rdbms_informix',
491     env => [
492       DBICTEST_INFORMIX_DSN => 1,
493       DBICTEST_INFORMIX_USER => 0,
494       DBICTEST_INFORMIX_PASS => 0,
495     ],
496   },
497
498   test_rdbms_sqlanywhere => {
499     include => 'rdbms_sqlanywhere',
500     env => [
501       DBICTEST_SQLANYWHERE_DSN => 1,
502       DBICTEST_SQLANYWHERE_USER => 0,
503       DBICTEST_SQLANYWHERE_PASS => 0,
504     ],
505   },
506
507   test_rdbms_sqlanywhere_odbc => {
508     include => 'rdbms_sqlanywhere_odbc',
509     env => [
510       DBICTEST_SQLANYWHERE_ODBC_DSN => 1,
511       DBICTEST_SQLANYWHERE_ODBC_USER => 0,
512       DBICTEST_SQLANYWHERE_ODBC_PASS => 0,
513     ],
514   },
515
516   test_rdbms_firebird => {
517     include => 'rdbms_firebird',
518     env => [
519       DBICTEST_FIREBIRD_DSN => 1,
520       DBICTEST_FIREBIRD_USER => 0,
521       DBICTEST_FIREBIRD_PASS => 0,
522     ],
523   },
524
525   test_rdbms_firebird_interbase => {
526     include => 'rdbms_firebird_interbase',
527     env => [
528       DBICTEST_FIREBIRD_INTERBASE_DSN => 1,
529       DBICTEST_FIREBIRD_INTERBASE_USER => 0,
530       DBICTEST_FIREBIRD_INTERBASE_PASS => 0,
531     ],
532   },
533
534   test_rdbms_firebird_odbc => {
535     include => 'rdbms_firebird_odbc',
536     env => [
537       DBICTEST_FIREBIRD_ODBC_DSN => 1,
538       DBICTEST_FIREBIRD_ODBC_USER => 0,
539       DBICTEST_FIREBIRD_ODBC_PASS => 0,
540     ],
541   },
542
543   test_memcached => {
544     env => [
545       DBICTEST_MEMCACHED => 1,
546     ],
547     req => {
548       'Cache::Memcached' => 0,
549     },
550   },
551
552   dist_dir => {
553     req => {
554       %$test_and_dist_json_any,
555       'ExtUtils::MakeMaker' => '6.64',
556       'Pod::Inherit'        => '0.91',
557     },
558   },
559
560   dist_upload => {
561     req => {
562       'CPAN::Uploader' => '0.103001',
563     },
564   },
565 };
566
567
568
569 ### Public API
570
571 sub import {
572   my $class = shift;
573
574   if (@_) {
575
576     my $action = shift;
577
578     if ($action eq '-die_without') {
579       my $err;
580       {
581         local $@;
582         eval { $class->die_unless_req_ok_for(\@_); 1 }
583           or $err = $@;
584       }
585       die "\n$err\n" if $err;
586     }
587     elsif ($action eq '-list_missing') {
588       print $class->modreq_missing_for(\@_);
589       print "\n";
590       exit 0;
591     }
592     elsif ($action =~ /^-/) {
593       croak "Unknown import-time action '$action'";
594     }
595     else {
596       croak "$class is not an exporter, unable to import '$action'";
597     }
598   }
599
600   1;
601 }
602
603 sub unimport {
604   croak( __PACKAGE__ . " does not implement unimport" );
605 }
606
607 # OO for (mistakenly considered) ease of extensibility, not due to any need to
608 # carry state of any sort. This API is currently used outside, so leave as-is.
609 # FIXME - make sure to not propagate this further if module is extracted as a
610 # standalone library - keep the stupidity to a DBIC-secific shim!
611 #
612 sub req_list_for {
613   shift->_groups_to_reqs(@_)->{effective_modreqs};
614 }
615
616 sub modreq_list_for {
617   shift->_groups_to_reqs(@_)->{modreqs};
618 }
619
620 sub req_group_list {
621   +{ map
622     { $_ => $_[0]->_groups_to_reqs($_) }
623     keys %$dbic_reqs
624   }
625 }
626
627 sub req_errorlist_for { shift->modreq_errorlist_for(@_) }  # deprecated
628 sub modreq_errorlist_for {
629   my $self = shift;
630   $self->_errorlist_for_modreqs( $self->_groups_to_reqs(@_)->{modreqs} );
631 }
632
633 sub req_ok_for {
634   shift->req_missing_for(@_) ? 0 : 1;
635 }
636
637 sub req_missing_for {
638   my $self = shift;
639
640   my $reqs = $self->_groups_to_reqs(@_);
641   my $mods_missing = $self->modreq_missing_for(@_);
642
643   return '' if
644     ! $mods_missing
645       and
646     ! $reqs->{missing_envvars}
647   ;
648
649   my @res = $mods_missing || ();
650
651   push @res, 'the following group(s) of environment variables: ' . join ' and ', map
652     { __envvar_group_desc($_) }
653     @{$reqs->{missing_envvars}}
654   if $reqs->{missing_envvars};
655
656   return (
657     ( join ' as well as ', @res )
658       .
659     ( $reqs->{modreqs_fully_documented} ? " (see @{[ ref $self || $self ]} documentation for details)" : '' ),
660   );
661 }
662
663 sub modreq_missing_for {
664   my $self = shift;
665
666   my $reqs = $self->_groups_to_reqs(@_);
667   my $modreq_errors = $self->_errorlist_for_modreqs($reqs->{modreqs})
668     or return '';
669
670   join ' ', map
671     { $reqs->{modreqs}{$_} ? qq("$_~>=$reqs->{modreqs}{$_}") : $_ }
672     sort { lc($a) cmp lc($b) } keys %$modreq_errors
673   ;
674 }
675
676 sub die_unless_req_ok_for {
677   if (my $err = shift->req_missing_for(@_) ) {
678     die "Unable to continue due to missing requirements: $err\n";
679   }
680 }
681
682
683
684 ### Private functions
685
686 # potentially shorten group desc
687 sub __envvar_group_desc {
688   my @envs = @{$_[0]};
689
690   my (@res, $last_prefix);
691   while (my $ev = shift @envs) {
692     my ($pref, $sep, $suff) = split / ([\_\-]) (?= [^\_\-]+ \z )/x, $ev;
693
694     if ( defined $sep and ($last_prefix||'') eq $pref ) {
695         push @res, "...${sep}${suff}"
696     }
697     else {
698       push @res, $ev;
699     }
700
701     $last_prefix = $pref if $sep;
702   }
703
704   join '/', @res;
705 }
706
707
708
709 ### Private OO API
710 our %req_unavailability_cache;
711
712 # this method is just a lister and envvar/metadata checker - it does not try to load anything
713 my $processed_groups = {};
714 sub _groups_to_reqs {
715   my ($self, $groups) = @_;
716
717   $groups = [ $groups || () ]
718     unless ref $groups eq 'ARRAY';
719
720   croak "@{[ (caller(1))[3] ]}() expects a requirement group name or arrayref of group names"
721     unless @$groups;
722
723   my $ret = {
724     modreqs => {},
725     modreqs_fully_documented => 1,
726   };
727
728   for my $group ( grep { ! $processed_groups->{$_} } @$groups ) {
729
730     $group =~ /\A [A-Za-z][0-9A-Z_a-z]* \z/x
731       or croak "Invalid requirement group name '$group': only ascii alphanumerics and _ are allowed";
732
733     croak "Requirement group '$group' is not defined" unless defined $dbic_reqs->{$group};
734
735     my $group_reqs = $dbic_reqs->{$group}{req};
736
737     # sanity-check
738     for (keys %$group_reqs) {
739
740       $_ =~ /\A [A-Z_a-z][0-9A-Z_a-z]* (?:::[0-9A-Z_a-z]+)* \z /x
741         or croak "Requirement '$_' in group '$group' is not a valid module name";
742
743       # !!!DO NOT CHANGE!!!
744       # remember - version.pm may not be available on the system
745       croak "Requirement '$_' in group '$group' specifies an invalid version '$group_reqs->{$_}' (only plain non-underscored floating point decimals are supported)"
746         if ( ($group_reqs->{$_}||0) !~ / \A [0-9]+ (?: \. [0-9]+ )? \z /x );
747     }
748
749     # check if we have all required envvars if such names are defined
750     my ($some_envs_required, $some_envs_missing);
751     if (my @e = @{$dbic_reqs->{$group}{env} || [] }) {
752
753       croak "Unexpected 'env' attribute under group '$group' (only allowed in test_* groups)"
754         unless $group =~ /^test_/;
755
756       croak "Unexpected *odd* list in 'env' under group '$group'"
757         if @e % 2;
758
759       my @group_envnames_list;
760
761       # deconstruct the whole thing
762       while (@e) {
763         push @group_envnames_list, my $envname = shift @e;
764
765         # env required or not
766         next unless shift @e;
767
768         $some_envs_required ||= 1;
769
770         $some_envs_missing ||= (
771           ! defined $ENV{$envname}
772             or
773           ! length $ENV{$envname}
774         );
775       }
776
777       croak "None of the envvars in group '$group' declared as required, making the requirement moot"
778         unless $some_envs_required;
779
780       push @{$ret->{missing_envvars}}, \@group_envnames_list if $some_envs_missing;
781     }
782
783     # get the reqs for includes if any
784     my $inc_reqs;
785     if (my $incs = $dbic_reqs->{$group}{include}) {
786       $incs = [ $incs ] unless ref $incs eq 'ARRAY';
787
788       croak "Malformed 'include' for group '$group': must be another existing group name or arrayref of existing group names"
789         unless @$incs;
790
791       local $processed_groups->{$group} = 1;
792
793       my $subreqs = $self->_groups_to_reqs($incs);
794
795       croak "Includes with variable effective_modreqs not yet supported"
796         if $subreqs->{effective_modreqs_differ};
797
798       $inc_reqs = $subreqs->{modreqs};
799
800     }
801
802     # assemble into the final ret
803     for my $type (
804       'modreqs',
805       $some_envs_missing ? () : 'effective_modreqs'
806     ) {
807       for my $req_bag ($group_reqs, $inc_reqs||()) {
808         for my $mod (keys %$req_bag) {
809
810           $ret->{$type}{$mod} = $req_bag->{$mod}||0 if (
811
812             ! exists $ret->{$type}{$mod}
813               or
814             # we sanitized the version to be numeric above - we can just -gt it
815             ($req_bag->{$mod}||0) > $ret->{$type}{$mod}
816
817           );
818         }
819       }
820     }
821
822     $ret->{effective_modreqs_differ} ||= !!$some_envs_missing;
823
824     $ret->{modreqs_fully_documented} &&= !!$dbic_reqs->{$group}{pod};
825   }
826
827   return $ret;
828 }
829
830
831 # this method tries to load specified modreqs and returns a hashref of
832 # module/loaderror pairs for anything that failed
833 sub _errorlist_for_modreqs {
834   # args supposedly already went through _groups_to_reqs and are therefore sanitized
835   # safe to eval at will
836   my ($self, $reqs) = @_;
837
838   my $ret;
839
840   for my $m ( keys %$reqs ) {
841     my $v = $reqs->{$m};
842
843     if (! exists $req_unavailability_cache{$m}{$v} ) {
844       local $@;
845       eval( "require $m;" . ( $v ? "$m->VERSION(q($v))" : '' ) );
846       $req_unavailability_cache{$m}{$v} = $@;
847     }
848
849     $ret->{$m} = $req_unavailability_cache{$m}{$v}
850       if $req_unavailability_cache{$m}{$v};
851   }
852
853   $ret;
854 }
855
856
857 # This is to be called by the author only (automatically in Makefile.PL)
858 sub _gen_pod {
859   my ($class, $distver, $pod_dir) = @_;
860
861   die "No POD root dir supplied" unless $pod_dir;
862
863   $distver ||=
864     eval { require DBIx::Class; DBIx::Class->VERSION; }
865       ||
866     die
867 "\n\n---------------------------------------------------------------------\n" .
868 'Unable to load core DBIx::Class module to determine current version, '.
869 'possibly due to missing dependencies. Author-mode autodocumentation ' .
870 "halted\n\n" . $@ .
871 "\n\n---------------------------------------------------------------------\n"
872   ;
873
874   # do not ask for a recent version, use 1.x API calls
875   # this *may* execute on a smoker with old perl or whatnot
876   require File::Path;
877
878   (my $modfn = __PACKAGE__ . '.pm') =~ s|::|/|g;
879
880   (my $podfn = "$pod_dir/$modfn") =~ s/\.pm$/\.pod/;
881   (my $dir = $podfn) =~ s|/[^/]+$||;
882
883   File::Path::mkpath([$dir]);
884
885   my $sqltver = $class->req_list_for('deploy')->{'SQL::Translator'}
886     or die "Hrmm? No sqlt dep?";
887
888
889   my @chunks;
890
891 #@@
892 #@@ HEADER
893 #@@
894   push @chunks, <<"EOC";
895 #########################################################################
896 #####################  A U T O G E N E R A T E D ########################
897 #########################################################################
898 #
899 # The contents of this POD file are auto-generated.  Any changes you make
900 # will be lost. If you need to change the generated text edit _gen_pod()
901 # at the end of $modfn
902 #
903
904 =head1 NAME
905
906 $class - Optional module dependency specifications (for module authors)
907 EOC
908
909
910 #@@
911 #@@ SYNOPSIS HEADING
912 #@@
913   push @chunks, <<"EOC";
914 =head1 SYNOPSIS
915
916 Somewhere in your build-file (e.g. L<ExtUtils::MakeMaker>'s F<Makefile.PL>):
917
918   ...
919
920   \$EUMM_ARGS{CONFIGURE_REQUIRES} = {
921     \%{ \$EUMM_ARGS{CONFIGURE_REQUIRES} || {} },
922     'DBIx::Class' => '$distver',
923   };
924
925   ...
926
927   my %DBIC_DEPLOY_DEPS = %{ eval {
928     require $class;
929     $class->req_list_for('deploy');
930   } || {} };
931
932   \$EUMM_ARGS{PREREQ_PM} = {
933     \%DBIC_DEPLOY_DEPS,
934     \%{ \$EUMM_ARGS{PREREQ_PM} || {} },
935   };
936
937   ...
938
939   ExtUtils::MakeMaker::WriteMakefile(\%EUMM_ARGS);
940
941 B<Note>: The C<eval> protection within the example is due to support for
942 requirements during L<the C<configure> build phase|CPAN::Meta::Spec/Phases>
943 not being available on a sufficient portion of production installations of
944 Perl. Robust support for such dependency requirements is available in the
945 L<CPAN> installer only since version C<1.94_56> first made available for
946 production with perl version C<5.12>. It is the belief of the current
947 maintainer that support for requirements during the C<configure> build phase
948 will not be sufficiently ubiquitous until the B<year 2020> at the earliest,
949 hence the extra care demonstrated above. It should also be noted that some
950 3rd party installers (e.g. L<cpanminus|App::cpanminus>) do the right thing
951 with configure requirements independent from the versions of perl and CPAN
952 available.
953 EOC
954
955
956 #@@
957 #@@ DESCRIPTION HEADING
958 #@@
959   push @chunks, <<'EOC';
960 =head1 DESCRIPTION
961
962 Some of the less-frequently used features of L<DBIx::Class> have external
963 module dependencies on their own. In order not to burden the average user
964 with modules they will never use, these optional dependencies are not included
965 in the base Makefile.PL. Instead an exception with a descriptive message is
966 thrown when a specific feature can't find one or several modules required for
967 its operation. This module is the central holding place for the current list
968 of such dependencies, for DBIx::Class core authors, and DBIx::Class extension
969 authors alike.
970
971 Dependencies are organized in L<groups|/CURRENT REQUIREMENT GROUPS> where each
972 group can list one or more required modules, with an optional minimum version
973 (or 0 for any version). In addition groups prefixed with C<test_> can specify
974 a set of environment variables, some (or all) of which are marked as required
975 for the group to be considered by L</req_list_for>
976
977 Each group name (or a combination thereof) can be used in the
978 L<public methods|/METHODS> as described below.
979 EOC
980
981
982 #@@
983 #@@ REQUIREMENT GROUPLIST HEADING
984 #@@
985   push @chunks, '=head1 CURRENT REQUIREMENT GROUPS';
986
987   for my $group (sort keys %$dbic_reqs) {
988     my $p = $dbic_reqs->{$group}{pod}
989       or next;
990
991     my $modlist = $class->modreq_list_for($group);
992
993     next unless keys %$modlist;
994
995     push @chunks, (
996       "=head2 $p->{title}",
997       "$p->{desc}",
998       '=over',
999       ( map { "=item * $_" . ($modlist->{$_} ? " >= $modlist->{$_}" : '') } (sort keys %$modlist) ),
1000       '=back',
1001       "Requirement group: B<$group>",
1002     );
1003   }
1004
1005
1006 #@@
1007 #@@ API DOCUMENTATION HEADING
1008 #@@
1009   push @chunks, <<'EOC';
1010
1011 =head1 IMPORT-LIKE ACTIONS
1012
1013 Even though this module is not an L<Exporter>, it recognizes several C<actions>
1014 supplied to its C<import> method.
1015
1016 =head2 -die_without
1017
1018 =over
1019
1020 =item Arguments: @group_names
1021
1022 =back
1023
1024 A convenience wrapper around L</die_unless_req_ok_for>:
1025 EOC
1026
1027   push @chunks, " use $class -die_without => qw(deploy admin);";
1028
1029   push @chunks, <<'EOC';
1030
1031 =head2 -list_missing
1032
1033 =over
1034
1035 =item Arguments: @group_names
1036
1037 =back
1038
1039 A convenience wrapper around L</modreq_missing_for>:
1040
1041  perl -Ilib -MDBIx::Class::Optional::Dependencies=-list_missing,deploy,admin | cpanm
1042
1043 =head1 METHODS
1044
1045 =head2 req_group_list
1046
1047 =over
1048
1049 =item Arguments: none
1050
1051 =item Return Value: \%list_of_requirement_groups
1052
1053 =back
1054
1055 This method should be used by DBIx::Class packagers, to get a hashref of all
1056 dependencies B<keyed> by dependency group. Each key (group name), or a combination
1057 thereof (as an arrayref) can be supplied to the methods below.
1058 The B<values> of the returned hash are currently a set of options B<without a
1059 well defined structure>. If you have use for any of the contents - contact the
1060 maintainers, instead of treating this as public (left alone stable) API.
1061
1062 =head2 req_list_for
1063
1064 =over
1065
1066 =item Arguments: $group_name | \@group_names
1067
1068 =item Return Value: \%set_of_module_version_pairs
1069
1070 =back
1071
1072 This method should be used by DBIx::Class extension authors, to determine the
1073 version of modules a specific set of features requires for this version of
1074 DBIx::Class (regardless of their availability on the system).
1075 See the L</SYNOPSIS> for a real-world example.
1076
1077 When handling C<test_*> groups this method behaves B<differently> from
1078 L</modreq_list_for> below (and is the only such inconsistency among the
1079 C<req_*> methods). If a particular group declares as requirements some
1080 C<environment variables> and these requirements are not satisfied (the envvars
1081 are unset) - then the C<module requirements> of this group are not included in
1082 the returned list.
1083
1084 =head2 modreq_list_for
1085
1086 =over
1087
1088 =item Arguments: $group_name | \@group_names
1089
1090 =item Return Value: \%set_of_module_version_pairs
1091
1092 =back
1093
1094 Same as L</req_list_for> but does not take into consideration any
1095 C<environment variable requirements> - returns just the list of required
1096 modules.
1097
1098 =head2 req_ok_for
1099
1100 =over
1101
1102 =item Arguments: $group_name | \@group_names
1103
1104 =item Return Value: 1|0
1105
1106 =back
1107
1108 Returns true or false depending on whether all modules/envvars required by
1109 the group(s) are loadable/set on the system.
1110
1111 =head2 req_missing_for
1112
1113 =over
1114
1115 =item Arguments: $group_name | \@group_names
1116
1117 =item Return Value: $error_message_string
1118
1119 =back
1120
1121 Returns a single-line string suitable for inclusion in larger error messages.
1122 This method would normally be used by DBIx::Class core features, to indicate to
1123 the user that they need to install specific modules and/or set specific
1124 environment variables before being able to use a specific feature set.
1125
1126 For example if some of the requirements for C<deploy> are not available,
1127 the returned string could look like:
1128 EOC
1129
1130   push @chunks, qq{ "SQL::Translator~>=$sqltver" (see $class documentation for details)};
1131
1132   push @chunks, <<'EOC';
1133 The author is expected to prepend the necessary text to this message before
1134 returning the actual error seen by the user. See also L</modreq_missing_for>
1135
1136 =head2 modreq_missing_for
1137
1138 =over
1139
1140 =item Arguments: $group_name | \@group_names
1141
1142 =item Return Value: $error_message_string
1143
1144 =back
1145
1146 Same as L</req_missing_for> except that the error string is guaranteed to be
1147 either empty, or contain a set of module requirement specifications suitable
1148 for piping to e.g. L<cpanminus|App::cpanminus>. The method explicitly does not
1149 attempt to validate the state of required environment variables (if any).
1150
1151 For instance if some of the requirements for C<deploy> are not available,
1152 the returned string could look like:
1153 EOC
1154
1155   push @chunks, qq{ "SQL::Translator~>=$sqltver"};
1156
1157   push @chunks, <<'EOC';
1158
1159 See also L</-list_missing>.
1160
1161 =head2 die_unless_req_ok_for
1162
1163 =over
1164
1165 =item Arguments: $group_name | \@group_names
1166
1167 =back
1168
1169 Checks if L</req_ok_for> passes for the supplied group(s), and
1170 in case of failure throws an exception including the information
1171 from L</req_missing_for>. See also L</-die_without>.
1172
1173 =head2 modreq_errorlist_for
1174
1175 =over
1176
1177 =item Arguments: $group_name | \@group_names
1178
1179 =item Return Value: \%set_of_loaderrors_per_module
1180
1181 =back
1182
1183 Returns a hashref containing the actual errors that occurred while attempting
1184 to load each module in the requirement group(s).
1185
1186 =head2 req_errorlist_for
1187
1188 Deprecated method name, equivalent (via proxy) to L</modreq_errorlist_for>.
1189
1190 EOC
1191
1192 #@@
1193 #@@ FOOTER
1194 #@@
1195   push @chunks, <<'EOC';
1196 =head1 FURTHER QUESTIONS?
1197
1198 Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
1199
1200 =head1 COPYRIGHT AND LICENSE
1201
1202 This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
1203 by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
1204 redistribute it and/or modify it under the same terms as the
1205 L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.
1206 EOC
1207
1208   eval {
1209     open (my $fh, '>', $podfn) or die;
1210     print $fh join ("\n\n", @chunks) or die;
1211     print $fh "\n" or die;
1212     close ($fh) or die;
1213   } or croak( "Unable to write $podfn: " . ( $! || $@ || 'unknown error') );
1214 }
1215
1216 1;