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