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