ff28cad028584400fc4014766a16d36298875072
[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 ### Private OO API
792 our %req_unavailability_cache;
793
794 # this method is just a lister and envvar/metadata checker - it does not try to load anything
795 my $processed_groups = {};
796 sub _groups_to_reqs {
797   my ($self, $groups) = @_;
798
799   $groups = [ $groups || () ]
800     unless ref $groups eq 'ARRAY';
801
802   croak "@{[ (caller(1))[3] ]}() expects a requirement group name or arrayref of group names"
803     unless @$groups;
804
805   my $ret = {
806     modreqs => {},
807     modreqs_fully_documented => 1,
808   };
809
810   for my $group ( grep { ! $processed_groups->{$_} } @$groups ) {
811
812     $group =~ /\A [A-Z_a-z][0-9A-Z_a-z]* \z/x
813       or croak "Invalid requirement group name '$group': only ascii alphanumerics and _ are allowed";
814
815     croak "Requirement group '$group' is not defined" unless defined $dbic_reqs->{$group};
816
817     my $group_reqs = $dbic_reqs->{$group}{req};
818
819     # sanity-check
820     for (keys %$group_reqs) {
821
822       $_ =~ /\A [A-Z_a-z][0-9A-Z_a-z]* (?:::[0-9A-Z_a-z]+)* \z /x
823         or croak "Requirement '$_' in group '$group' is not a valid module name";
824
825       # !!!DO NOT CHANGE!!!
826       # remember - version.pm may not be available on the system
827       croak "Requirement '$_' in group '$group' specifies an invalid version '$group_reqs->{$_}' (only plain non-underscored floating point decimals are supported)"
828         if ( ($group_reqs->{$_}||0) !~ / \A [0-9]+ (?: \. [0-9]+ )? \z /x );
829     }
830
831     # check if we have all required envvars if such names are defined
832     my ($some_envs_required, $some_envs_missing);
833     if (my @e = @{$dbic_reqs->{$group}{env} || [] }) {
834
835       croak "Unexpected 'env' attribute under group '$group' (only allowed in test_* groups)"
836         unless $group =~ /^test_/;
837
838       croak "Unexpected *odd* list in 'env' under group '$group'"
839         if @e % 2;
840
841       my @group_envnames_list;
842
843       # deconstruct the whole thing
844       while (@e) {
845         push @group_envnames_list, my $envname = shift @e;
846
847         # env required or not
848         next unless shift @e;
849
850         $some_envs_required ||= 1;
851
852         $some_envs_missing ||= (
853           ! defined $ENV{$envname}
854             or
855           ! length $ENV{$envname}
856         );
857       }
858
859       croak "None of the envvars in group '$group' declared as required, making the requirement moot"
860         unless $some_envs_required;
861
862       push @{$ret->{missing_envvars}}, \@group_envnames_list if $some_envs_missing;
863     }
864
865     # get the reqs for includes if any
866     my $inc_reqs;
867     if (my $incs = $dbic_reqs->{$group}{include}) {
868       $incs = [ $incs ] unless ref $incs eq 'ARRAY';
869
870       croak "Malformed 'include' for group '$group': must be another existing group name or arrayref of existing group names"
871         unless @$incs;
872
873       local $processed_groups->{$group} = 1;
874
875       my $subreqs = $self->_groups_to_reqs($incs);
876
877       croak "Includes with variable effective_modreqs not yet supported"
878         if $subreqs->{effective_modreqs_differ};
879
880       $inc_reqs = $subreqs->{modreqs};
881
882     }
883
884     # assemble into the final ret
885     for my $type (
886       'modreqs',
887       $some_envs_missing ? () : 'effective_modreqs'
888     ) {
889       for my $req_bag ($group_reqs, $inc_reqs||()) {
890         for my $mod (keys %$req_bag) {
891
892           $ret->{$type}{$mod} = $req_bag->{$mod}||0 if (
893
894             ! exists $ret->{$type}{$mod}
895               or
896             # we sanitized the version to be numeric above - we can just -gt it
897             ($req_bag->{$mod}||0) > $ret->{$type}{$mod}
898
899           );
900         }
901       }
902     }
903
904     $ret->{effective_modreqs_differ} ||= !!$some_envs_missing;
905
906     $ret->{modreqs_fully_documented} &&= !!$dbic_reqs->{$group}{pod};
907
908     $ret->{release_testing_mandatory} ||= !!$dbic_reqs->{$group}{release_testing_mandatory};
909   }
910
911   return $ret;
912 }
913
914
915 # this method tries to load specified modreqs and returns a hashref of
916 # module/loaderror pairs for anything that failed
917 sub _errorlist_for_modreqs {
918   # args supposedly already went through _groups_to_reqs and are therefore sanitized
919   # safe to eval at will
920   my ($self, $reqs) = @_;
921
922   my $ret;
923
924   for my $m ( keys %$reqs ) {
925     my $v = $reqs->{$m};
926
927     if (! exists $req_unavailability_cache{$m}{$v} ) {
928       local $@;
929       eval( "require $m;" . ( $v ? "$m->VERSION(q($v))" : '' ) );
930       $req_unavailability_cache{$m}{$v} = $@;
931     }
932
933     $ret->{$m} = $req_unavailability_cache{$m}{$v}
934       if $req_unavailability_cache{$m}{$v};
935   }
936
937   $ret;
938 }
939
940
941 # This is to be called by the author only (automatically in Makefile.PL)
942 sub _gen_pod {
943   my ($class, $distver, $pod_dir) = @_;
944
945   die "No POD root dir supplied" unless $pod_dir;
946
947   $distver ||=
948     eval { require DBIx::Class; DBIx::Class->VERSION; }
949       ||
950     die
951 "\n\n---------------------------------------------------------------------\n" .
952 'Unable to load core DBIx::Class module to determine current version, '.
953 'possibly due to missing dependencies. Author-mode autodocumentation ' .
954 "halted\n\n" . $@ .
955 "\n\n---------------------------------------------------------------------\n"
956   ;
957
958   # do not ask for a recent version, use 1.x API calls
959   # this *may* execute on a smoker with old perl or whatnot
960   require File::Path;
961
962   (my $modfn = __PACKAGE__ . '.pm') =~ s|::|/|g;
963
964   (my $podfn = "$pod_dir/$modfn") =~ s/\.pm$/\.pod/;
965   (my $dir = $podfn) =~ s|/[^/]+$||;
966
967   File::Path::mkpath([$dir]);
968
969   my $sqltver = $class->req_list_for('deploy')->{'SQL::Translator'}
970     or die "Hrmm? No sqlt dep?";
971
972
973   my @chunks;
974
975 #@@
976 #@@ HEADER
977 #@@
978   push @chunks, <<"EOC";
979 #########################################################################
980 #####################  A U T O G E N E R A T E D ########################
981 #########################################################################
982 #
983 # The contents of this POD file are auto-generated.  Any changes you make
984 # will be lost. If you need to change the generated text edit _gen_pod()
985 # at the end of $modfn
986 #
987
988 =head1 NAME
989
990 $class - Optional module dependency specifications (for module authors)
991 EOC
992
993
994 #@@
995 #@@ SYNOPSIS HEADING
996 #@@
997   push @chunks, <<"EOC";
998 =head1 SYNOPSIS
999
1000 Somewhere in your build-file (e.g. L<ExtUtils::MakeMaker>'s F<Makefile.PL>):
1001
1002   ...
1003
1004   \$EUMM_ARGS{CONFIGURE_REQUIRES} = {
1005     \%{ \$EUMM_ARGS{CONFIGURE_REQUIRES} || {} },
1006     'DBIx::Class' => '$distver',
1007   };
1008
1009   ...
1010
1011   my %DBIC_DEPLOY_DEPS = %{ eval {
1012     require $class;
1013     $class->req_list_for('deploy');
1014   } || {} };
1015
1016   \$EUMM_ARGS{PREREQ_PM} = {
1017     \%DBIC_DEPLOY_DEPS,
1018     \%{ \$EUMM_ARGS{PREREQ_PM} || {} },
1019   };
1020
1021   ...
1022
1023   ExtUtils::MakeMaker::WriteMakefile(\%EUMM_ARGS);
1024
1025 B<Note>: The C<eval> protection within the example is due to support for
1026 requirements during L<the C<configure> build phase|CPAN::Meta::Spec/Phases>
1027 not being available on a sufficient portion of production installations of
1028 Perl. Robust support for such dependency requirements is available in the
1029 L<CPAN> installer only since version C<1.94_56> first made available for
1030 production with perl version C<5.12>. It is the belief of the current
1031 maintainer that support for requirements during the C<configure> build phase
1032 will not be sufficiently ubiquitous until the B<year 2020> at the earliest,
1033 hence the extra care demonstrated above. It should also be noted that some
1034 3rd party installers (e.g. L<cpanminus|App::cpanminus>) do the right thing
1035 with configure requirements independent from the versions of perl and CPAN
1036 available.
1037 EOC
1038
1039
1040 #@@
1041 #@@ DESCRIPTION HEADING
1042 #@@
1043   push @chunks, <<'EOC';
1044 =head1 DESCRIPTION
1045
1046 Some of the less-frequently used features of L<DBIx::Class> have external
1047 module dependencies on their own. In order not to burden the average user
1048 with modules they will never use, these optional dependencies are not included
1049 in the base Makefile.PL. Instead an exception with a descriptive message is
1050 thrown when a specific feature can't find one or several modules required for
1051 its operation. This module is the central holding place for the current list
1052 of such dependencies, for DBIx::Class core authors, and DBIx::Class extension
1053 authors alike.
1054
1055 Dependencies are organized in L<groups|/CURRENT REQUIREMENT GROUPS> where each
1056 group can list one or more required modules, with an optional minimum version
1057 (or 0 for any version). In addition groups prefixed with C<test_> can specify
1058 a set of environment variables, some (or all) of which are marked as required
1059 for the group to be considered by L</req_list_for>
1060
1061 Each group name (or a combination thereof) can be used in the
1062 L<public methods|/METHODS> as described below.
1063 EOC
1064
1065
1066 #@@
1067 #@@ REQUIREMENT GROUPLIST HEADING
1068 #@@
1069   push @chunks, '=head1 CURRENT REQUIREMENT GROUPS';
1070
1071   for my $group (sort keys %$dbic_reqs) {
1072     my $p = $dbic_reqs->{$group}{pod}
1073       or next;
1074
1075     my $modlist = $class->modreq_list_for($group);
1076
1077     next unless keys %$modlist;
1078
1079     push @chunks, (
1080       "=head2 $p->{title}",
1081       "$p->{desc}",
1082       '=over',
1083       ( map { "=item * $_" . ($modlist->{$_} ? " >= $modlist->{$_}" : '') } (sort keys %$modlist) ),
1084       '=back',
1085       "Requirement group: B<$group>",
1086     );
1087   }
1088
1089
1090 #@@
1091 #@@ API DOCUMENTATION HEADING
1092 #@@
1093   push @chunks, <<'EOC';
1094
1095 =head1 IMPORT-LIKE ACTIONS
1096
1097 Even though this module is not an L<Exporter>, it recognizes several C<actions>
1098 supplied to its C<import> method.
1099
1100 =head2 -skip_all_without
1101
1102 =over
1103
1104 =item Arguments: @group_names
1105
1106 =back
1107
1108 A convenience wrapper for use during testing:
1109 EOC
1110
1111   push @chunks, " use $class -skip_all_without => qw(admin test_rdbms_mysql);";
1112
1113   push @chunks, 'Roughly equivalent to the following code:';
1114
1115   push @chunks, sprintf <<'EOS', ($class) x 2;
1116
1117  BEGIN {
1118    require %s;
1119    if ( my $missing = %s->req_missing_for(\@group_names_) ) {
1120      print "1..0 # SKIP requirements not satisfied: $missing\n";
1121      exit 0;
1122    }
1123  }
1124 EOS
1125
1126   push @chunks, <<'EOC';
1127
1128 It also takes into account the C<RELEASE_TESTING> environment variable and
1129 behaves like L</-die_without> for any requirement groups marked as
1130 C<release_testing_mandatory>.
1131
1132 =head2 -die_without
1133
1134 =over
1135
1136 =item Arguments: @group_names
1137
1138 =back
1139
1140 A convenience wrapper around L</die_unless_req_ok_for>:
1141 EOC
1142
1143   push @chunks, " use $class -die_without => qw(deploy admin);";
1144
1145   push @chunks, <<'EOC';
1146
1147 =head2 -list_missing
1148
1149 =over
1150
1151 =item Arguments: @group_names
1152
1153 =back
1154
1155 A convenience wrapper around L</modreq_missing_for>:
1156
1157  perl -Ilib -MDBIx::Class::Optional::Dependencies=-list_missing,deploy,admin | cpanm
1158
1159 =head1 METHODS
1160
1161 =head2 req_group_list
1162
1163 =over
1164
1165 =item Arguments: none
1166
1167 =item Return Value: \%list_of_requirement_groups
1168
1169 =back
1170
1171 This method should be used by DBIx::Class packagers, to get a hashref of all
1172 dependencies B<keyed> by dependency group. Each key (group name), or a combination
1173 thereof (as an arrayref) can be supplied to the methods below.
1174 The B<values> of the returned hash are currently a set of options B<without a
1175 well defined structure>. If you have use for any of the contents - contact the
1176 maintainers, instead of treating this as public (left alone stable) API.
1177
1178 =head2 req_list_for
1179
1180 =over
1181
1182 =item Arguments: $group_name | \@group_names
1183
1184 =item Return Value: \%set_of_module_version_pairs
1185
1186 =back
1187
1188 This method should be used by DBIx::Class extension authors, to determine the
1189 version of modules a specific set of features requires for this version of
1190 DBIx::Class (regardless of their availability on the system).
1191 See the L</SYNOPSIS> for a real-world example.
1192
1193 When handling C<test_*> groups this method behaves B<differently> from
1194 L</modreq_list_for> below (and is the only such inconsistency among the
1195 C<req_*> methods). If a particular group declares as requirements some
1196 C<environment variables> and these requirements are not satisfied (the envvars
1197 are unset) - then the C<module requirements> of this group are not included in
1198 the returned list.
1199
1200 =head2 modreq_list_for
1201
1202 =over
1203
1204 =item Arguments: $group_name | \@group_names
1205
1206 =item Return Value: \%set_of_module_version_pairs
1207
1208 =back
1209
1210 Same as L</req_list_for> but does not take into consideration any
1211 C<environment variable requirements> - returns just the list of required
1212 modules.
1213
1214 =head2 req_ok_for
1215
1216 =over
1217
1218 =item Arguments: $group_name | \@group_names
1219
1220 =item Return Value: 1|0
1221
1222 =back
1223
1224 Returns true or false depending on whether all modules/envvars required by
1225 the group(s) are loadable/set on the system.
1226
1227 =head2 req_missing_for
1228
1229 =over
1230
1231 =item Arguments: $group_name | \@group_names
1232
1233 =item Return Value: $error_message_string
1234
1235 =back
1236
1237 Returns a single-line string suitable for inclusion in larger error messages.
1238 This method would normally be used by DBIx::Class core features, to indicate to
1239 the user that they need to install specific modules and/or set specific
1240 environment variables before being able to use a specific feature set.
1241
1242 For example if some of the requirements for C<deploy> are not available,
1243 the returned string could look like:
1244 EOC
1245
1246   push @chunks, qq{ "SQL::Translator~>=$sqltver" (see $class documentation for details)};
1247
1248   push @chunks, <<'EOC';
1249 The author is expected to prepend the necessary text to this message before
1250 returning the actual error seen by the user. See also L</modreq_missing_for>
1251
1252 =head2 modreq_missing_for
1253
1254 =over
1255
1256 =item Arguments: $group_name | \@group_names
1257
1258 =item Return Value: $error_message_string
1259
1260 =back
1261
1262 Same as L</req_missing_for> except that the error string is guaranteed to be
1263 either empty, or contain a set of module requirement specifications suitable
1264 for piping to e.g. L<cpanminus|App::cpanminus>. The method explicitly does not
1265 attempt to validate the state of required environment variables (if any).
1266
1267 For instance if some of the requirements for C<deploy> are not available,
1268 the returned string could look like:
1269 EOC
1270
1271   push @chunks, qq{ "SQL::Translator~>=$sqltver"};
1272
1273   push @chunks, <<'EOC';
1274
1275 See also L</-list_missing>.
1276
1277 =head2 die_unless_req_ok_for
1278
1279 =over
1280
1281 =item Arguments: $group_name | \@group_names
1282
1283 =back
1284
1285 Checks if L</req_ok_for> passes for the supplied group(s), and
1286 in case of failure throws an exception including the information
1287 from L</req_missing_for>. See also L</-die_without>.
1288
1289 =head2 modreq_errorlist_for
1290
1291 =over
1292
1293 =item Arguments: $group_name | \@group_names
1294
1295 =item Return Value: \%set_of_loaderrors_per_module
1296
1297 =back
1298
1299 Returns a hashref containing the actual errors that occurred while attempting
1300 to load each module in the requirement group(s).
1301
1302 =head2 req_errorlist_for
1303
1304 Deprecated method name, equivalent (via proxy) to L</modreq_errorlist_for>.
1305
1306 EOC
1307
1308 #@@
1309 #@@ FOOTER
1310 #@@
1311   push @chunks, <<'EOC';
1312 =head1 FURTHER QUESTIONS?
1313
1314 Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
1315
1316 =head1 COPYRIGHT AND LICENSE
1317
1318 This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
1319 by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
1320 redistribute it and/or modify it under the same terms as the
1321 L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.
1322 EOC
1323
1324   eval {
1325     open (my $fh, '>', $podfn) or die;
1326     print $fh join ("\n\n", @chunks) or die;
1327     print $fh "\n" or die;
1328     close ($fh) or die;
1329   } or croak( "Unable to write $podfn: " . ( $! || $@ || 'unknown error') );
1330 }
1331
1332 1;