Introduce basic optdeps group inclusion mechanism, simplify many declarations
[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 # OO for (mistakenly considered) ease of extensibility, not due to any need to
572 # carry state of any sort. This API is currently used outside, so leave as-is.
573 # FIXME - make sure to not propagate this further if module is extracted as a
574 # standalone library - keep the stupidity to a DBIC-secific shim!
575 #
576 sub req_list_for {
577   shift->_groups_to_reqs(@_)->{effective_modreqs};
578 }
579
580 sub modreq_list_for {
581   shift->_groups_to_reqs(@_)->{modreqs};
582 }
583
584 sub req_group_list {
585   +{ map
586     { $_ => $_[0]->_groups_to_reqs($_) }
587     keys %$dbic_reqs
588   }
589 }
590
591 sub req_errorlist_for { shift->modreq_errorlist_for(@_) }  # deprecated
592 sub modreq_errorlist_for {
593   my $self = shift;
594   $self->_errorlist_for_modreqs( $self->_groups_to_reqs(@_)->{modreqs} );
595 }
596
597 sub req_ok_for {
598   shift->req_missing_for(@_) ? 0 : 1;
599 }
600
601 sub req_missing_for {
602   my $self = shift;
603
604   my $reqs = $self->_groups_to_reqs(@_);
605   my $mods_missing = $self->modreq_missing_for(@_);
606
607   return '' if
608     ! $mods_missing
609       and
610     ! $reqs->{missing_envvars}
611   ;
612
613   my @res = $mods_missing || ();
614
615   push @res, 'the following group(s) of environment variables: ' . join ' and ', map
616     { __envvar_group_desc($_) }
617     @{$reqs->{missing_envvars}}
618   if $reqs->{missing_envvars};
619
620   return (
621     ( join ' as well as ', @res )
622       .
623     ( $reqs->{modreqs_fully_documented} ? " (see @{[ ref $self || $self ]} documentation for details)" : '' ),
624   );
625 }
626
627 sub modreq_missing_for {
628   my $self = shift;
629
630   my $reqs = $self->_groups_to_reqs(@_);
631   my $modreq_errors = $self->_errorlist_for_modreqs($reqs->{modreqs})
632     or return '';
633
634   join ' ', map
635     { $reqs->{modreqs}{$_} ? qq("$_~>=$reqs->{modreqs}{$_}") : $_ }
636     sort { lc($a) cmp lc($b) } keys %$modreq_errors
637   ;
638 }
639
640 sub die_unless_req_ok_for {
641   if (my $err = shift->req_missing_for(@_) ) {
642     die "Unable to continue due to missing requirements: $err\n";
643   }
644 }
645
646
647
648 ### Private functions
649
650 # potentially shorten group desc
651 sub __envvar_group_desc {
652   my @envs = @{$_[0]};
653
654   my (@res, $last_prefix);
655   while (my $ev = shift @envs) {
656     my ($pref, $sep, $suff) = split / ([\_\-]) (?= [^\_\-]+ \z )/x, $ev;
657
658     if ( defined $sep and ($last_prefix||'') eq $pref ) {
659         push @res, "...${sep}${suff}"
660     }
661     else {
662       push @res, $ev;
663     }
664
665     $last_prefix = $pref if $sep;
666   }
667
668   join '/', @res;
669 }
670
671
672
673 ### Private OO API
674 our %req_unavailability_cache;
675
676 # this method is just a lister and envvar/metadata checker - it does not try to load anything
677 my $processed_groups = {};
678 sub _groups_to_reqs {
679   my ($self, $groups) = @_;
680
681   $groups = [ $groups || () ]
682     unless ref $groups eq 'ARRAY';
683
684   croak "@{[ (caller(1))[3] ]}() expects a requirement group name or arrayref of group names"
685     unless @$groups;
686
687   my $ret = {
688     modreqs => {},
689     modreqs_fully_documented => 1,
690   };
691
692   for my $group ( grep { ! $processed_groups->{$_} } @$groups ) {
693
694     $group =~ /\A [A-Za-z][0-9A-Z_a-z]* \z/x
695       or croak "Invalid requirement group name '$group': only ascii alphanumerics and _ are allowed";
696
697     croak "Requirement group '$group' is not defined" unless defined $dbic_reqs->{$group};
698
699     my $group_reqs = $dbic_reqs->{$group}{req};
700
701     # sanity-check
702     for (keys %$group_reqs) {
703
704       $_ =~ /\A [A-Z_a-z][0-9A-Z_a-z]* (?:::[0-9A-Z_a-z]+)* \z /x
705         or croak "Requirement '$_' in group '$group' is not a valid module name";
706
707       # !!!DO NOT CHANGE!!!
708       # remember - version.pm may not be available on the system
709       croak "Requirement '$_' in group '$group' specifies an invalid version '$group_reqs->{$_}' (only plain non-underscored floating point decimals are supported)"
710         if ( ($group_reqs->{$_}||0) !~ / \A [0-9]+ (?: \. [0-9]+ )? \z /x );
711     }
712
713     # check if we have all required envvars if such names are defined
714     my ($some_envs_required, $some_envs_missing);
715     if (my @e = @{$dbic_reqs->{$group}{env} || [] }) {
716
717       croak "Unexpected 'env' attribute under group '$group' (only allowed in test_* groups)"
718         unless $group =~ /^test_/;
719
720       croak "Unexpected *odd* list in 'env' under group '$group'"
721         if @e % 2;
722
723       my @group_envnames_list;
724
725       # deconstruct the whole thing
726       while (@e) {
727         push @group_envnames_list, my $envname = shift @e;
728
729         # env required or not
730         next unless shift @e;
731
732         $some_envs_required ||= 1;
733
734         $some_envs_missing ||= (
735           ! defined $ENV{$envname}
736             or
737           ! length $ENV{$envname}
738         );
739       }
740
741       croak "None of the envvars in group '$group' declared as required, making the requirement moot"
742         unless $some_envs_required;
743
744       push @{$ret->{missing_envvars}}, \@group_envnames_list if $some_envs_missing;
745     }
746
747     # get the reqs for includes if any
748     my $inc_reqs;
749     if (my $incs = $dbic_reqs->{$group}{include}) {
750       $incs = [ $incs ] unless ref $incs eq 'ARRAY';
751
752       croak "Malformed 'include' for group '$group': must be another existing group name or arrayref of existing group names"
753         unless @$incs;
754
755       local $processed_groups->{$group} = 1;
756
757       my $subreqs = $self->_groups_to_reqs($incs);
758
759       croak "Includes with variable effective_modreqs not yet supported"
760         if $subreqs->{effective_modreqs_differ};
761
762       $inc_reqs = $subreqs->{modreqs};
763
764     }
765
766     # assemble into the final ret
767     for my $type (
768       'modreqs',
769       $some_envs_missing ? () : 'effective_modreqs'
770     ) {
771       for my $req_bag ($group_reqs, $inc_reqs||()) {
772         for my $mod (keys %$req_bag) {
773
774           $ret->{$type}{$mod} = $req_bag->{$mod}||0 if (
775
776             ! exists $ret->{$type}{$mod}
777               or
778             # we sanitized the version to be numeric above - we can just -gt it
779             ($req_bag->{$mod}||0) > $ret->{$type}{$mod}
780
781           );
782         }
783       }
784     }
785
786     $ret->{effective_modreqs_differ} ||= !!$some_envs_missing;
787
788     $ret->{modreqs_fully_documented} &&= !!$dbic_reqs->{$group}{pod};
789   }
790
791   return $ret;
792 }
793
794
795 # this method tries to load specified modreqs and returns a hashref of
796 # module/loaderror pairs for anything that failed
797 sub _errorlist_for_modreqs {
798   # args supposedly already went through _groups_to_reqs and are therefore sanitized
799   # safe to eval at will
800   my ($self, $reqs) = @_;
801
802   my $ret;
803
804   for my $m ( keys %$reqs ) {
805     my $v = $reqs->{$m};
806
807     if (! exists $req_unavailability_cache{$m}{$v} ) {
808       local $@;
809       eval( "require $m;" . ( $v ? "$m->VERSION(q($v))" : '' ) );
810       $req_unavailability_cache{$m}{$v} = $@;
811     }
812
813     $ret->{$m} = $req_unavailability_cache{$m}{$v}
814       if $req_unavailability_cache{$m}{$v};
815   }
816
817   $ret;
818 }
819
820
821 # This is to be called by the author only (automatically in Makefile.PL)
822 sub _gen_pod {
823   my ($class, $distver, $pod_dir) = @_;
824
825   die "No POD root dir supplied" unless $pod_dir;
826
827   $distver ||=
828     eval { require DBIx::Class; DBIx::Class->VERSION; }
829       ||
830     die
831 "\n\n---------------------------------------------------------------------\n" .
832 'Unable to load core DBIx::Class module to determine current version, '.
833 'possibly due to missing dependencies. Author-mode autodocumentation ' .
834 "halted\n\n" . $@ .
835 "\n\n---------------------------------------------------------------------\n"
836   ;
837
838   # do not ask for a recent version, use 1.x API calls
839   # this *may* execute on a smoker with old perl or whatnot
840   require File::Path;
841
842   (my $modfn = __PACKAGE__ . '.pm') =~ s|::|/|g;
843
844   (my $podfn = "$pod_dir/$modfn") =~ s/\.pm$/\.pod/;
845   (my $dir = $podfn) =~ s|/[^/]+$||;
846
847   File::Path::mkpath([$dir]);
848
849   my $sqltver = $class->req_list_for('deploy')->{'SQL::Translator'}
850     or die "Hrmm? No sqlt dep?";
851
852
853   my @chunks;
854
855 #@@
856 #@@ HEADER
857 #@@
858   push @chunks, <<"EOC";
859 #########################################################################
860 #####################  A U T O G E N E R A T E D ########################
861 #########################################################################
862 #
863 # The contents of this POD file are auto-generated.  Any changes you make
864 # will be lost. If you need to change the generated text edit _gen_pod()
865 # at the end of $modfn
866 #
867
868 =head1 NAME
869
870 $class - Optional module dependency specifications (for module authors)
871 EOC
872
873
874 #@@
875 #@@ SYNOPSIS HEADING
876 #@@
877   push @chunks, <<"EOC";
878 =head1 SYNOPSIS
879
880 Somewhere in your build-file (e.g. L<ExtUtils::MakeMaker>'s F<Makefile.PL>):
881
882   ...
883
884   \$EUMM_ARGS{CONFIGURE_REQUIRES} = {
885     \%{ \$EUMM_ARGS{CONFIGURE_REQUIRES} || {} },
886     'DBIx::Class' => '$distver',
887   };
888
889   ...
890
891   my %DBIC_DEPLOY_DEPS = %{ eval {
892     require $class;
893     $class->req_list_for('deploy');
894   } || {} };
895
896   \$EUMM_ARGS{PREREQ_PM} = {
897     \%DBIC_DEPLOY_DEPS,
898     \%{ \$EUMM_ARGS{PREREQ_PM} || {} },
899   };
900
901   ...
902
903   ExtUtils::MakeMaker::WriteMakefile(\%EUMM_ARGS);
904
905 B<Note>: The C<eval> protection within the example is due to support for
906 requirements during L<the C<configure> build phase|CPAN::Meta::Spec/Phases>
907 not being available on a sufficient portion of production installations of
908 Perl. Robust support for such dependency requirements is available in the
909 L<CPAN> installer only since version C<1.94_56> first made available for
910 production with perl version C<5.12>. It is the belief of the current
911 maintainer that support for requirements during the C<configure> build phase
912 will not be sufficiently ubiquitous until the B<year 2020> at the earliest,
913 hence the extra care demonstrated above. It should also be noted that some
914 3rd party installers (e.g. L<cpanminus|App::cpanminus>) do the right thing
915 with configure requirements independent from the versions of perl and CPAN
916 available.
917 EOC
918
919
920 #@@
921 #@@ DESCRIPTION HEADING
922 #@@
923   push @chunks, <<'EOC';
924 =head1 DESCRIPTION
925
926 Some of the less-frequently used features of L<DBIx::Class> have external
927 module dependencies on their own. In order not to burden the average user
928 with modules they will never use, these optional dependencies are not included
929 in the base Makefile.PL. Instead an exception with a descriptive message is
930 thrown when a specific feature can't find one or several modules required for
931 its operation. This module is the central holding place for the current list
932 of such dependencies, for DBIx::Class core authors, and DBIx::Class extension
933 authors alike.
934
935 Dependencies are organized in L<groups|/CURRENT REQUIREMENT GROUPS> where each
936 group can list one or more required modules, with an optional minimum version
937 (or 0 for any version). In addition groups prefixed with C<test_> can specify
938 a set of environment variables, some (or all) of which are marked as required
939 for the group to be considered by L</req_list_for>
940
941 Each group name (or a combination thereof) can be used in the
942 L<public methods|/METHODS> as described below.
943 EOC
944
945
946 #@@
947 #@@ REQUIREMENT GROUPLIST HEADING
948 #@@
949   push @chunks, '=head1 CURRENT REQUIREMENT GROUPS';
950
951   for my $group (sort keys %$dbic_reqs) {
952     my $p = $dbic_reqs->{$group}{pod}
953       or next;
954
955     my $modlist = $class->modreq_list_for($group);
956
957     next unless keys %$modlist;
958
959     push @chunks, (
960       "=head2 $p->{title}",
961       "$p->{desc}",
962       '=over',
963       ( map { "=item * $_" . ($modlist->{$_} ? " >= $modlist->{$_}" : '') } (sort keys %$modlist) ),
964       '=back',
965       "Requirement group: B<$group>",
966     );
967   }
968
969
970 #@@
971 #@@ API DOCUMENTATION HEADING
972 #@@
973   push @chunks, <<'EOC';
974
975 =head1 METHODS
976
977 =head2 req_group_list
978
979 =over
980
981 =item Arguments: none
982
983 =item Return Value: \%list_of_requirement_groups
984
985 =back
986
987 This method should be used by DBIx::Class packagers, to get a hashref of all
988 dependencies B<keyed> by dependency group. Each key (group name), or a combination
989 thereof (as an arrayref) can be supplied to the methods below.
990 The B<values> of the returned hash are currently a set of options B<without a
991 well defined structure>. If you have use for any of the contents - contact the
992 maintainers, instead of treating this as public (left alone stable) API.
993
994 =head2 req_list_for
995
996 =over
997
998 =item Arguments: $group_name | \@group_names
999
1000 =item Return Value: \%set_of_module_version_pairs
1001
1002 =back
1003
1004 This method should be used by DBIx::Class extension authors, to determine the
1005 version of modules a specific set of features requires for this version of
1006 DBIx::Class (regardless of their availability on the system).
1007 See the L</SYNOPSIS> for a real-world example.
1008
1009 When handling C<test_*> groups this method behaves B<differently> from
1010 L</modreq_list_for> below (and is the only such inconsistency among the
1011 C<req_*> methods). If a particular group declares as requirements some
1012 C<environment variables> and these requirements are not satisfied (the envvars
1013 are unset) - then the C<module requirements> of this group are not included in
1014 the returned list.
1015
1016 =head2 modreq_list_for
1017
1018 =over
1019
1020 =item Arguments: $group_name | \@group_names
1021
1022 =item Return Value: \%set_of_module_version_pairs
1023
1024 =back
1025
1026 Same as L</req_list_for> but does not take into consideration any
1027 C<environment variable requirements> - returns just the list of required
1028 modules.
1029
1030 =head2 req_ok_for
1031
1032 =over
1033
1034 =item Arguments: $group_name | \@group_names
1035
1036 =item Return Value: 1|0
1037
1038 =back
1039
1040 Returns true or false depending on whether all modules/envvars required by
1041 the group(s) are loadable/set on the system.
1042
1043 =head2 req_missing_for
1044
1045 =over
1046
1047 =item Arguments: $group_name | \@group_names
1048
1049 =item Return Value: $error_message_string
1050
1051 =back
1052
1053 Returns a single-line string suitable for inclusion in larger error messages.
1054 This method would normally be used by DBIx::Class core features, to indicate to
1055 the user that they need to install specific modules and/or set specific
1056 environment variables before being able to use a specific feature set.
1057
1058 For example if some of the requirements for C<deploy> are not available,
1059 the returned string could look like:
1060 EOC
1061
1062   push @chunks, qq{ "SQL::Translator~>=$sqltver" (see $class documentation for details)};
1063
1064   push @chunks, <<'EOC';
1065 The author is expected to prepend the necessary text to this message before
1066 returning the actual error seen by the user. See also L</modreq_missing_for>
1067
1068 =head2 modreq_missing_for
1069
1070 =over
1071
1072 =item Arguments: $group_name | \@group_names
1073
1074 =item Return Value: $error_message_string
1075
1076 =back
1077
1078 Same as L</req_missing_for> except that the error string is guaranteed to be
1079 either empty, or contain a set of module requirement specifications suitable
1080 for piping to e.g. L<cpanminus|App::cpanminus>. The method explicitly does not
1081 attempt to validate the state of required environment variables (if any).
1082
1083 For instance if some of the requirements for C<deploy> are not available,
1084 the returned string could look like:
1085 EOC
1086
1087   push @chunks, qq{ "SQL::Translator~>=$sqltver"};
1088
1089   push @chunks, <<'EOC';
1090 =head2 die_unless_req_ok_for
1091
1092 =over
1093
1094 =item Arguments: $group_name | \@group_names
1095
1096 =back
1097
1098 Checks if L</req_ok_for> passes for the supplied group(s), and
1099 in case of failure throws an exception including the information
1100 from L</req_missing_for>.
1101
1102 =head2 modreq_errorlist_for
1103
1104 =over
1105
1106 =item Arguments: $group_name | \@group_names
1107
1108 =item Return Value: \%set_of_loaderrors_per_module
1109
1110 =back
1111
1112 Returns a hashref containing the actual errors that occurred while attempting
1113 to load each module in the requirement group(s).
1114
1115 =head2 req_errorlist_for
1116
1117 Deprecated method name, equivalent (via proxy) to L</modreq_errorlist_for>.
1118
1119 EOC
1120
1121 #@@
1122 #@@ FOOTER
1123 #@@
1124   push @chunks, <<'EOC';
1125 =head1 FURTHER QUESTIONS?
1126
1127 Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
1128
1129 =head1 COPYRIGHT AND LICENSE
1130
1131 This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
1132 by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
1133 redistribute it and/or modify it under the same terms as the
1134 L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.
1135 EOC
1136
1137   eval {
1138     open (my $fh, '>', $podfn) or die;
1139     print $fh join ("\n\n", @chunks) or die;
1140     print $fh "\n" or die;
1141     close ($fh) or die;
1142   } or croak( "Unable to write $podfn: " . ( $! || $@ || 'unknown error') );
1143 }
1144
1145 1;