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