Switch CDBICompat and its tests to OptDeps
[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   my $mods_missing = $self->modreq_missing_for($groups);
774
775   return '' if
776     ! $mods_missing
777       and
778     ! $reqs->{missing_envvars}
779   ;
780
781   my @res = $mods_missing || ();
782
783   push @res, 'the following group(s) of environment variables: ' . join ' and ', sort map
784     { __envvar_group_desc($_) }
785     @{$reqs->{missing_envvars}}
786   if $reqs->{missing_envvars};
787
788   return (
789     ( join ' as well as ', @res )
790       .
791     ( $reqs->{modreqs_fully_documented} ? " (see @{[ ref $self || $self ]} documentation for details)" : '' ),
792   );
793 }
794
795 sub modreq_missing_for {
796   my ($self, $groups) = @_;
797
798   my $reqs = $self->_groups_to_reqs($groups);
799   my $modreq_errors = $self->_errorlist_for_modreqs($reqs->{modreqs})
800     or return '';
801
802   join ' ', map
803     { $reqs->{modreqs}{$_} ? qq("$_~>=$reqs->{modreqs}{$_}") : $_ }
804     sort { lc($a) cmp lc($b) } keys %$modreq_errors
805   ;
806 }
807
808 my $tb;
809 sub skip_without {
810   my ($self, $groups) = @_;
811
812   $tb ||= do { local $@; eval { Test::Builder->new } }
813     or croak "Calling skip_without() before loading Test::Builder makes no sense";
814
815   if ( my $err = $self->req_missing_for($groups) ) {
816     my ($fn, $ln) = (caller(0))[1,2];
817     $tb->skip("block in $fn around line $ln requires $err");
818     local $^W = 0;
819     last SKIP;
820   }
821
822   1;
823 }
824
825 sub die_unless_req_ok_for {
826   if (my $err = shift->req_missing_for(shift) ) {
827     die "Unable to continue due to missing requirements: $err\n";
828   }
829 }
830
831
832
833 ### Private functions
834
835 # potentially shorten group desc
836 sub __envvar_group_desc {
837   my @envs = @{$_[0]};
838
839   my (@res, $last_prefix);
840   while (my $ev = shift @envs) {
841     my ($pref, $sep, $suff) = split / ([\_\-]) (?= [^\_\-]+ \z )/x, $ev;
842
843     if ( defined $sep and ($last_prefix||'') eq $pref ) {
844         push @res, "...${sep}${suff}"
845     }
846     else {
847       push @res, $ev;
848     }
849
850     $last_prefix = $pref if $sep;
851   }
852
853   join '/', @res;
854 }
855
856 my $groupname_re = qr/ [A-Z_a-z][0-9A-Z_a-z]* /x;
857 my $modname_re = qr/ [A-Z_a-z] [0-9A-Z_a-z]* (?:::[0-9A-Z_a-z]+)* /x;
858 my $modver_re = qr/ [0-9]+ (?: \. [0-9]+ )? /x;
859
860 # Expand includes from a random group in a specific order:
861 # nonvariable groups first, then their includes, then the variable groups,
862 # then their includes.
863 # This allows reliably marking the rest of the mod reqs as variable (this is
864 # also why variable includes are currently not allowed)
865 sub __expand_includes {
866   my ($groups, $seen) = @_;
867
868   # !! DIFFERENT !! behavior and return depending on invocation mode
869   # (easier to recurse this way)
870   my $is_toplevel = $seen
871     ? 0
872     : !! ($seen = {})
873   ;
874
875   my ($res_per_type, $missing_envvars);
876
877   # breadth-first evaluation, with non-variable includes on top
878   for my $g (@$groups) {
879
880     croak "Invalid requirement group name '$g': only ascii alphanumerics and _ are allowed"
881       if $g !~ qr/ \A $groupname_re \z/x;
882
883     my $r = $dbic_reqs->{$g}
884       or croak "Requirement group '$g' is not defined";
885
886     # always do this check *before* the $seen check
887     croak "Group '$g' with variable effective_modreqs can not be specified as an 'include'"
888       if ( $r->{env} and ! $is_toplevel );
889
890     next if $seen->{$g}++;
891
892     my $req_type = 'static';
893
894     if ( my @e = @{$r->{env}||[]} ) {
895
896       croak "Unexpected 'env' attribute under group '$g' (only allowed in test_* groups)"
897         unless $g =~ /^test_/;
898
899       croak "Unexpected *odd* list in 'env' under group '$g'"
900         if @e % 2;
901
902       # deconstruct the whole thing
903       my (@group_envnames_list, $some_envs_required, $some_required_missing);
904       while (@e) {
905         push @group_envnames_list, my $envname = shift @e;
906
907         # env required or not
908         next unless shift @e;
909
910         $some_envs_required ||= 1;
911
912         $some_required_missing ||= (
913           ! defined $ENV{$envname}
914             or
915           ! length $ENV{$envname}
916         );
917       }
918
919       croak "None of the envvars in group '$g' declared as required, making the requirement moot"
920         unless $some_envs_required;
921
922       if ($some_required_missing) {
923         push @{$missing_envvars->{$g}}, \@group_envnames_list;
924         $req_type = 'variable';
925       }
926     }
927
928     push @{$res_per_type->{"base_${req_type}"}}, $g;
929
930     if (my $i = $dbic_reqs->{$g}{include}) {
931       $i = [ $i ] unless ref $i eq 'ARRAY';
932
933       croak "Malformed 'include' for group '$g': must be another existing group name or arrayref of existing group names"
934         unless @$i;
935
936       push @{$res_per_type->{"incs_${req_type}"}}, @$i;
937     }
938   }
939
940   my @ret = map {
941     @{ $res_per_type->{"base_${_}"} || [] },
942     ( $res_per_type->{"incs_${_}"} ? __expand_includes( $res_per_type->{"incs_${_}"}, $seen ) : () ),
943   } qw(static variable);
944
945   return ! $is_toplevel ? @ret : do {
946     my $rv = {};
947     $rv->{$_} = {
948       idx => 1 + keys %$rv,
949       missing_envvars => $missing_envvars->{$_},
950     } for @ret;
951     $rv->{$_}{user_requested} = 1 for @$groups;
952     $rv;
953   };
954 }
955
956 ### Private OO API
957 our %req_unavailability_cache;
958
959 # this method is just a lister and envvar/metadata checker - it does not try to load anything
960 sub _groups_to_reqs {
961   my ($self, $want) = @_;
962
963   $want = [ $want || () ]
964     unless ref $want eq 'ARRAY';
965
966   croak "@{[ (caller(1))[3] ]}() expects a requirement group name or arrayref of group names"
967     unless @$want;
968
969   my $ret = {
970     modreqs => {},
971     modreqs_fully_documented => 1,
972   };
973
974   my $groups;
975   for my $piece (@$want) {
976     if ($piece =~ qr/ \A $groupname_re \z /x) {
977       push @$groups, $piece;
978     }
979     elsif ( my ($mod, $ver) = $piece =~ qr/ \A ($modname_re) \>\= ($modver_re) \z /x ) {
980       croak "Ad hoc module specification lists '$mod' twice"
981         if exists $ret->{modreqs}{$mod};
982
983       croak "Ad hoc module specification '${mod} >= $ver' (or greater) not listed in the test_adhoc optdep group" if (
984         ! defined $dbic_reqs->{test_adhoc}{req}{$mod}
985           or
986         $dbic_reqs->{test_adhoc}{req}{$mod} < $ver
987       );
988
989       $ret->{modreqs}{$mod} = $ver;
990       $ret->{modreqs_fully_documented} = 0;
991     }
992     else {
993       croak "Unsupported argument '$piece' supplied to @{[ (caller(1))[3] ]}()"
994     }
995   }
996
997   my $all_groups = __expand_includes($groups);
998
999   # pre-assemble list of augmentations, perform basic sanity checks
1000   # Note that below we *DO NOT* respect the source/target reationship, but
1001   # instead always default to augment the "later" group
1002   # This is done so that the "stable/variable" boundary keeps working as
1003   # expected
1004   my $augmentations;
1005   for my $requesting_group (keys %$all_groups) {
1006     if (my $ag = $dbic_reqs->{$requesting_group}{augment}) {
1007       for my $target_group (keys %$ag) {
1008
1009         croak "Group '$requesting_group' claims to augment a non-existent group '$target_group'"
1010           unless $dbic_reqs->{$target_group};
1011
1012         croak "Augmentation combined with variable effective_modreqs currently unsupported for group '$requesting_group'"
1013           if $dbic_reqs->{$requesting_group}{env};
1014
1015         croak "Augmentation of group '$target_group' with variable effective_modreqs unsupported (requested by '$requesting_group')"
1016           if $dbic_reqs->{$target_group}{env};
1017
1018         if (my @foreign = grep { $_ ne 'req' } keys %{$ag->{$target_group}} ) {
1019           croak "Only 'req' augmentations are currently supported (group '$requesting_group' attempts to alter '$foreign[0]' of group '$target_group'";
1020         }
1021
1022         $ret->{augments}{$target_group} = 1;
1023
1024         # no augmentation for stuff that hasn't been selected
1025         if ( $all_groups->{$target_group} and my $ar = $ag->{$target_group}{req} ) {
1026           push @{$augmentations->{
1027             ( $all_groups->{$requesting_group}{idx} < $all_groups->{$target_group}{idx} )
1028               ? $target_group
1029               : $requesting_group
1030           }}, $ar;
1031         }
1032       }
1033     }
1034   }
1035
1036   for my $group (sort { $all_groups->{$a}{idx} <=> $all_groups->{$b}{idx} } keys %$all_groups ) {
1037
1038     my $group_reqs = $dbic_reqs->{$group}{req};
1039
1040     # sanity-check
1041     for my $req_bag ($group_reqs, @{ $augmentations->{$group} || [] } ) {
1042       for (keys %$req_bag) {
1043
1044         $_ =~ / \A $modname_re \z /x
1045           or croak "Requirement '$_' in group '$group' is not a valid module name";
1046
1047         # !!!DO NOT CHANGE!!!
1048         # remember - version.pm may not be available on the system
1049         croak "Requirement '$_' in group '$group' specifies an invalid version '$req_bag->{$_}' (only plain non-underscored floating point decimals are supported)"
1050           if ( ($req_bag->{$_}||0) !~ qr/ \A $modver_re \z /x );
1051       }
1052     }
1053
1054     if (my $e = $all_groups->{$group}{missing_envvars}) {
1055       push @{$ret->{missing_envvars}}, @$e;
1056     }
1057
1058     # assemble into the final ret
1059     for my $type (
1060       'modreqs',
1061       ( $ret->{missing_envvars} ? () : 'effective_modreqs' ),
1062     ) {
1063       for my $req_bag ($group_reqs, @{ $augmentations->{$group} || [] } ) {
1064         for my $mod (keys %$req_bag) {
1065
1066           $ret->{$type}{$mod} = $req_bag->{$mod}||0 if (
1067
1068             ! exists $ret->{$type}{$mod}
1069               or
1070             # we sanitized the version to be numeric above - we can just -gt it
1071             ($req_bag->{$mod}||0) > $ret->{$type}{$mod}
1072
1073           );
1074         }
1075       }
1076     }
1077
1078     $ret->{modreqs_fully_documented} &&= !!$dbic_reqs->{$group}{pod}
1079       if $all_groups->{$group}{user_requested};
1080
1081     $ret->{release_testing_mandatory} ||= !!$dbic_reqs->{$group}{release_testing_mandatory};
1082   }
1083
1084   return $ret;
1085 }
1086
1087
1088 # this method tries to load specified modreqs and returns a hashref of
1089 # module/loaderror pairs for anything that failed
1090 sub _errorlist_for_modreqs {
1091   # args supposedly already went through _groups_to_reqs and are therefore sanitized
1092   # safe to eval at will
1093   my ($self, $reqs) = @_;
1094
1095   my $ret;
1096
1097   for my $m ( keys %$reqs ) {
1098     my $v = $reqs->{$m};
1099
1100     if (! exists $req_unavailability_cache{$m}{$v} ) {
1101       local $@;
1102       eval( "require $m;" . ( $v ? "$m->VERSION(q($v))" : '' ) );
1103       $req_unavailability_cache{$m}{$v} = $@;
1104     }
1105
1106     $ret->{$m} = $req_unavailability_cache{$m}{$v}
1107       if $req_unavailability_cache{$m}{$v};
1108   }
1109
1110   $ret;
1111 }
1112
1113
1114 # This is to be called by the author only (automatically in Makefile.PL)
1115 sub _gen_pod {
1116   my ($class, $distver, $pod_dir) = @_;
1117
1118   die "No POD root dir supplied" unless $pod_dir;
1119
1120   $distver ||=
1121     eval { require DBIx::Class; DBIx::Class->VERSION; }
1122       ||
1123     die
1124 "\n\n---------------------------------------------------------------------\n" .
1125 'Unable to load core DBIx::Class module to determine current version, '.
1126 'possibly due to missing dependencies. Author-mode autodocumentation ' .
1127 "halted\n\n" . $@ .
1128 "\n\n---------------------------------------------------------------------\n"
1129   ;
1130
1131   # do not ask for a recent version, use 1.x API calls
1132   # this *may* execute on a smoker with old perl or whatnot
1133   require File::Path;
1134
1135   (my $modfn = __PACKAGE__ . '.pm') =~ s|::|/|g;
1136
1137   (my $podfn = "$pod_dir/$modfn") =~ s/\.pm$/\.pod/;
1138   (my $dir = $podfn) =~ s|/[^/]+$||;
1139
1140   File::Path::mkpath([$dir]);
1141
1142   my $sqltver = $class->req_list_for('deploy')->{'SQL::Translator'}
1143     or die "Hrmm? No sqlt dep?";
1144
1145
1146   my @chunks;
1147
1148 #@@
1149 #@@ HEADER
1150 #@@
1151   push @chunks, <<"EOC";
1152 #########################################################################
1153 #####################  A U T O G E N E R A T E D ########################
1154 #########################################################################
1155 #
1156 # The contents of this POD file are auto-generated.  Any changes you make
1157 # will be lost. If you need to change the generated text edit _gen_pod()
1158 # at the end of $modfn
1159 #
1160
1161 =head1 NAME
1162
1163 $class - Optional module dependency specifications (for module authors)
1164 EOC
1165
1166
1167 #@@
1168 #@@ SYNOPSIS HEADING
1169 #@@
1170   push @chunks, <<"EOC";
1171 =head1 SYNOPSIS
1172
1173 Somewhere in your build-file (e.g. L<ExtUtils::MakeMaker>'s F<Makefile.PL>):
1174
1175   ...
1176
1177   \$EUMM_ARGS{CONFIGURE_REQUIRES} = {
1178     \%{ \$EUMM_ARGS{CONFIGURE_REQUIRES} || {} },
1179     'DBIx::Class' => '$distver',
1180   };
1181
1182   ...
1183
1184   my %DBIC_DEPLOY_AND_ORACLE_DEPS = %{ eval {
1185     require $class;
1186     $class->req_list_for([qw( deploy rdbms_oracle icdt )]);
1187   } || {} };
1188
1189   \$EUMM_ARGS{PREREQ_PM} = {
1190     \%DBIC_DEPLOY_AND_ORACLE_DEPS,
1191     \%{ \$EUMM_ARGS{PREREQ_PM} || {} },
1192   };
1193
1194   ...
1195
1196   ExtUtils::MakeMaker::WriteMakefile(\%EUMM_ARGS);
1197
1198 B<Note>: The C<eval> protection within the example is due to support for
1199 requirements during L<the C<configure> build phase|CPAN::Meta::Spec/Phases>
1200 not being available on a sufficient portion of production installations of
1201 Perl. Robust support for such dependency requirements is available in the
1202 L<CPAN> installer only since version C<1.94_56> first made available for
1203 production with perl version C<5.12>. It is the belief of the current
1204 maintainer that support for requirements during the C<configure> build phase
1205 will not be sufficiently ubiquitous until the B<year 2020> at the earliest,
1206 hence the extra care demonstrated above. It should also be noted that some
1207 3rd party installers (e.g. L<cpanminus|App::cpanminus>) do the right thing
1208 with configure requirements independent from the versions of perl and CPAN
1209 available.
1210 EOC
1211
1212
1213 #@@
1214 #@@ DESCRIPTION HEADING
1215 #@@
1216   push @chunks, <<'EOC';
1217 =head1 DESCRIPTION
1218
1219 Some of the less-frequently used features of L<DBIx::Class> have external
1220 module dependencies on their own. In order not to burden the average user
1221 with modules they will never use, these optional dependencies are not included
1222 in the base Makefile.PL. Instead an exception with a descriptive message is
1223 thrown when a specific feature can't find one or several modules required for
1224 its operation. This module is the central holding place for the current list
1225 of such dependencies, for DBIx::Class core authors, and DBIx::Class extension
1226 authors alike.
1227
1228 Dependencies are organized in L<groups|/CURRENT REQUIREMENT GROUPS> where each
1229 group can list one or more required modules, with an optional minimum version
1230 (or 0 for any version). In addition groups prefixed with C<test_> can specify
1231 a set of environment variables, some (or all) of which are marked as required
1232 for the group to be considered by L</req_list_for>
1233
1234 Each group name (or a combination thereof) can be used in the
1235 L<public methods|/METHODS> as described below.
1236 EOC
1237
1238
1239 #@@
1240 #@@ REQUIREMENT GROUPLIST HEADING
1241 #@@
1242   push @chunks, '=head1 CURRENT REQUIREMENT GROUPS';
1243
1244   my $standalone_info;
1245
1246   for my $group (sort keys %$dbic_reqs) {
1247
1248     my $info = $standalone_info->{$group} ||= $class->_groups_to_reqs($group);
1249
1250     next unless (
1251       $info->{modreqs_fully_documented}
1252         and
1253       ( $info->{augments} or $info->{modreqs} )
1254     );
1255
1256     my $p = $dbic_reqs->{$group}{pod};
1257
1258     push @chunks, (
1259       "=head2 $p->{title}",
1260       "=head3 $group",
1261       $p->{desc},
1262       '=over',
1263     );
1264
1265     if ( keys %{ $info->{modreqs}||{} } ) {
1266       push @chunks, map
1267         { "=item * $_" . ($info->{modreqs}{$_} ? " >= $info->{modreqs}{$_}" : '') }
1268         ( sort keys %{ $info->{modreqs} } )
1269       ;
1270     }
1271     else {
1272       push @chunks, '=item * No standalone requirements',
1273     }
1274
1275     push @chunks, '=back';
1276
1277     for my $ag ( sort keys %{ $info->{augments} || {} } ) {
1278       my $ag_info = $standalone_info->{$ag} ||= $class->_groups_to_reqs($ag);
1279
1280       my $newreqs = $class->modreq_list_for([ $group, $ag ]);
1281       for (keys %$newreqs) {
1282         delete $newreqs->{$_} if (
1283           ( defined $info->{modreqs}{$_}    and $info->{modreqs}{$_}    == $newreqs->{$_} )
1284             or
1285           ( defined $ag_info->{modreqs}{$_} and $ag_info->{modreqs}{$_} == $newreqs->{$_} )
1286         );
1287       }
1288
1289       if (keys %$newreqs) {
1290         push @chunks, (
1291           "Combined with L</$ag> additionally requires:",
1292           '=over',
1293           ( map
1294             { "=item * $_" . ($newreqs->{$_} ? " >= $newreqs->{$_}" : '') }
1295             ( sort keys %$newreqs )
1296           ),
1297           '=back',
1298         );
1299       }
1300     }
1301   }
1302
1303
1304 #@@
1305 #@@ API DOCUMENTATION HEADING
1306 #@@
1307   push @chunks, <<'EOC';
1308
1309 =head1 IMPORT-LIKE ACTIONS
1310
1311 Even though this module is not an L<Exporter>, it recognizes several C<actions>
1312 supplied to its C<import> method.
1313
1314 =head2 -skip_all_without
1315
1316 =over
1317
1318 =item Arguments: @group_names
1319
1320 =back
1321
1322 A convenience wrapper for use during testing:
1323 EOC
1324
1325   push @chunks, " use $class -skip_all_without => qw(admin test_rdbms_mysql);";
1326
1327   push @chunks, 'Roughly equivalent to the following code:';
1328
1329   push @chunks, sprintf <<'EOS', ($class) x 2;
1330
1331  BEGIN {
1332    require %s;
1333    if ( my $missing = %s->req_missing_for(\@group_names_) ) {
1334      print "1..0 # SKIP requirements not satisfied: $missing\n";
1335      exit 0;
1336    }
1337  }
1338 EOS
1339
1340   push @chunks, <<'EOC';
1341
1342 It also takes into account the C<RELEASE_TESTING> environment variable and
1343 behaves like L</-die_without> for any requirement groups marked as
1344 C<release_testing_mandatory>.
1345
1346 =head2 -die_without
1347
1348 =over
1349
1350 =item Arguments: @group_names
1351
1352 =back
1353
1354 A convenience wrapper around L</die_unless_req_ok_for>:
1355 EOC
1356
1357   push @chunks, " use $class -die_without => qw(deploy admin);";
1358
1359   push @chunks, <<'EOC';
1360
1361 =head2 -list_missing
1362
1363 =over
1364
1365 =item Arguments: @group_names
1366
1367 =back
1368
1369 A convenience wrapper around L</modreq_missing_for>:
1370
1371  perl -Ilib -MDBIx::Class::Optional::Dependencies=-list_missing,deploy,admin | cpanm
1372
1373 =head1 METHODS
1374
1375 =head2 req_group_list
1376
1377 =over
1378
1379 =item Arguments: none
1380
1381 =item Return Value: \%list_of_requirement_groups
1382
1383 =back
1384
1385 This method should be used by DBIx::Class packagers, to get a hashref of all
1386 dependencies B<keyed> by dependency group. Each key (group name), or a combination
1387 thereof (as an arrayref) can be supplied to the methods below.
1388 The B<values> of the returned hash are currently a set of options B<without a
1389 well defined structure>. If you have use for any of the contents - contact the
1390 maintainers, instead of treating this as public (left alone stable) API.
1391
1392 =head2 req_list_for
1393
1394 =over
1395
1396 =item Arguments: $group_name | \@group_names
1397
1398 =item Return Value: \%set_of_module_version_pairs
1399
1400 =back
1401
1402 This method should be used by DBIx::Class extension authors, to determine the
1403 version of modules a specific set of features requires for this version of
1404 DBIx::Class (regardless of their availability on the system).
1405 See the L</SYNOPSIS> for a real-world example.
1406
1407 When handling C<test_*> groups this method behaves B<differently> from
1408 L</modreq_list_for> below (and is the only such inconsistency among the
1409 C<req_*> methods). If a particular group declares as requirements some
1410 C<environment variables> and these requirements are not satisfied (the envvars
1411 are unset) - then the C<module requirements> of this group are not included in
1412 the returned list.
1413
1414 =head2 modreq_list_for
1415
1416 =over
1417
1418 =item Arguments: $group_name | \@group_names
1419
1420 =item Return Value: \%set_of_module_version_pairs
1421
1422 =back
1423
1424 Same as L</req_list_for> but does not take into consideration any
1425 C<environment variable requirements> - returns just the list of required
1426 modules.
1427
1428 =head2 req_ok_for
1429
1430 =over
1431
1432 =item Arguments: $group_name | \@group_names
1433
1434 =item Return Value: 1|0
1435
1436 =back
1437
1438 Returns true or false depending on whether all modules/envvars required by
1439 the group(s) are loadable/set on the system.
1440
1441 =head2 req_missing_for
1442
1443 =over
1444
1445 =item Arguments: $group_name | \@group_names
1446
1447 =item Return Value: $error_message_string
1448
1449 =back
1450
1451 Returns a single-line string suitable for inclusion in larger error messages.
1452 This method would normally be used by DBIx::Class core features, to indicate to
1453 the user that they need to install specific modules and/or set specific
1454 environment variables before being able to use a specific feature set.
1455
1456 For example if some of the requirements for C<deploy> are not available,
1457 the returned string could look like:
1458 EOC
1459
1460   push @chunks, qq{ "SQL::Translator~>=$sqltver" (see $class documentation for details)};
1461
1462   push @chunks, <<'EOC';
1463 The author is expected to prepend the necessary text to this message before
1464 returning the actual error seen by the user. See also L</modreq_missing_for>
1465
1466 =head2 modreq_missing_for
1467
1468 =over
1469
1470 =item Arguments: $group_name | \@group_names
1471
1472 =item Return Value: $error_message_string
1473
1474 =back
1475
1476 Same as L</req_missing_for> except that the error string is guaranteed to be
1477 either empty, or contain a set of module requirement specifications suitable
1478 for piping to e.g. L<cpanminus|App::cpanminus>. The method explicitly does not
1479 attempt to validate the state of required environment variables (if any).
1480
1481 For instance if some of the requirements for C<deploy> are not available,
1482 the returned string could look like:
1483 EOC
1484
1485   push @chunks, qq{ "SQL::Translator~>=$sqltver"};
1486
1487   push @chunks, <<'EOC';
1488
1489 See also L</-list_missing>.
1490
1491 =head2 skip_without
1492
1493 =over
1494
1495 =item Arguments: $group_name | \@group_names
1496
1497 =back
1498
1499 A convenience wrapper around L<skip|Test::More/SKIP>. It does not take neither
1500 a reason (it is generated by L</req_missing_for>) nor an amount of skipped tests
1501 (it is always C<1>, thus mandating unconditional use of
1502 L<done_testing|Test::More/done_testing>). Most useful in combination with ad hoc
1503 requirement specifications:
1504 EOC
1505
1506   push @chunks, <<EOC;
1507   SKIP: {
1508     $class->skip_without([ deploy YAML>=0.90 ]);
1509
1510     ...
1511   }
1512 EOC
1513
1514   push @chunks, <<'EOC';
1515
1516 =head2 die_unless_req_ok_for
1517
1518 =over
1519
1520 =item Arguments: $group_name | \@group_names
1521
1522 =back
1523
1524 Checks if L</req_ok_for> passes for the supplied group(s), and
1525 in case of failure throws an exception including the information
1526 from L</req_missing_for>. See also L</-die_without>.
1527
1528 =head2 modreq_errorlist_for
1529
1530 =over
1531
1532 =item Arguments: $group_name | \@group_names
1533
1534 =item Return Value: \%set_of_loaderrors_per_module
1535
1536 =back
1537
1538 Returns a hashref containing the actual errors that occurred while attempting
1539 to load each module in the requirement group(s).
1540
1541 =head2 req_errorlist_for
1542
1543 Deprecated method name, equivalent (via proxy) to L</modreq_errorlist_for>.
1544
1545 EOC
1546
1547 #@@
1548 #@@ FOOTER
1549 #@@
1550   push @chunks, <<'EOC';
1551 =head1 FURTHER QUESTIONS?
1552
1553 Check the list of L<additional DBIC resources|DBIx::Class/GETTING HELP/SUPPORT>.
1554
1555 =head1 COPYRIGHT AND LICENSE
1556
1557 This module is free software L<copyright|DBIx::Class/COPYRIGHT AND LICENSE>
1558 by the L<DBIx::Class (DBIC) authors|DBIx::Class/AUTHORS>. You can
1559 redistribute it and/or modify it under the same terms as the
1560 L<DBIx::Class library|DBIx::Class/COPYRIGHT AND LICENSE>.
1561 EOC
1562
1563   eval {
1564     open (my $fh, '>', $podfn) or die;
1565     print $fh join ("\n\n", @chunks) or die;
1566     print $fh "\n" or die;
1567     close ($fh) or die;
1568   } or croak( "Unable to write $podfn: " . ( $! || $@ || 'unknown error') );
1569 }
1570
1571 1;