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