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