Fix test failure on MSWin32
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Optional / Dependencies.pm
1 package DBIx::Class::Optional::Dependencies;
2
3 use warnings;
4 use strict;
5
6 use Carp ();
7
8 # NO EXTERNAL NON-5.8.1 CORE DEPENDENCIES EVER (e.g. C::A::G)
9 # This module is to be loaded by Makefile.PM on a pristine system
10
11 # POD is generated automatically by calling _gen_pod from the
12 # Makefile.PL in $AUTHOR mode
13
14 my $json_any = {
15   'JSON::Any'                     => '1.22',
16 };
17
18 my $moose_basic = {
19   'Moose'                         => '0.98',
20   'MooseX::Types'                 => '0.21',
21 };
22
23 my $replicated = {
24   %$moose_basic,
25 };
26
27 my $admin_basic = {
28   %$moose_basic,
29   %$json_any,
30   'MooseX::Types::Path::Class'    => '0.05',
31   'MooseX::Types::JSON'           => '0.02',
32   'namespace::autoclean'          => '0.09',
33 };
34
35 my $datetime_basic = {
36   'DateTime'                      => '0.55',
37   'DateTime::Format::Strptime'    => '1.2',
38 };
39
40 my $id_shortener = {
41   'Math::BigInt'                  => '1.80',
42   'Math::Base36'                  => '0.07',
43 };
44
45 my $rdbms_sqlite = {
46   'DBD::SQLite'                   => '0',
47 };
48 my $rdbms_pg = {
49   'DBD::Pg'                       => '0',
50 };
51 my $rdbms_mssql_odbc = {
52   'DBD::ODBC'                     => '0',
53 };
54 my $rdbms_mssql_sybase = {
55   'DBD::Sybase'                   => '0',
56 };
57 my $rdbms_mssql_ado = {
58   'DBD::ADO'                      => '0',
59 };
60 my $rdbms_msaccess_odbc = {
61   'DBD::ODBC'                     => '0',
62 };
63 my $rdbms_msaccess_ado = {
64   'DBD::ADO'                      => '0',
65 };
66 my $rdbms_mysql = {
67   'DBD::mysql'                    => '0',
68 };
69 my $rdbms_oracle = {
70   'DBD::Oracle'                   => '0',
71   %$id_shortener,
72 };
73 my $rdbms_ase = {
74   'DBD::Sybase'                   => '0',
75 };
76 my $rdbms_db2 = {
77   'DBD::DB2'                      => '0',
78 };
79 my $rdbms_db2_400 = {
80   'DBD::ODBC'                     => '0',
81 };
82 my $rdbms_informix = {
83   'DBD::Informix'                 => '0',
84 };
85 my $rdbms_sqlanywhere = {
86   'DBD::SQLAnywhere'              => '0',
87 };
88 my $rdbms_sqlanywhere_odbc = {
89   'DBD::ODBC'                     => '0',
90 };
91 my $rdbms_firebird = {
92   'DBD::Firebird'                 => '0',
93 };
94 my $rdbms_firebird_interbase = {
95   'DBD::InterBase'                => '0',
96 };
97 my $rdbms_firebird_odbc = {
98   'DBD::ODBC'                     => '0',
99 };
100
101 my $reqs = {
102   dist => {
103     #'Module::Install::Pod::Inherit' => '0.01',
104   },
105
106   replicated => {
107     req => $replicated,
108     pod => {
109       title => 'Storage::Replicated',
110       desc => 'Modules required for L<DBIx::Class::Storage::DBI::Replicated>',
111     },
112   },
113
114   test_replicated => {
115     req => {
116       %$replicated,
117       'Test::Moose'               => '0',
118     },
119   },
120
121
122   admin => {
123     req => {
124       %$admin_basic,
125     },
126     pod => {
127       title => 'DBIx::Class::Admin',
128       desc => 'Modules required for the DBIx::Class administrative library',
129     },
130   },
131
132   admin_script => {
133     req => {
134       %$moose_basic,
135       %$admin_basic,
136       'Getopt::Long::Descriptive' => '0.081',
137       'Text::CSV'                 => '1.16',
138     },
139     pod => {
140       title => 'dbicadmin',
141       desc => 'Modules required for the CLI DBIx::Class interface dbicadmin',
142     },
143   },
144
145   deploy => {
146     req => {
147       'SQL::Translator'           => '0.11006',
148     },
149     pod => {
150       title => 'Storage::DBI::deploy()',
151       desc => 'Modules required for L<DBIx::Class::Storage::DBI/deploy> and L<DBIx::Class::Storage::DBI/deployment_statements>',
152     },
153   },
154
155   id_shortener => {
156     req => $id_shortener,
157   },
158
159   test_component_accessor => {
160     req => {
161       'Class::Unload'             => '0.07',
162     },
163   },
164
165   test_pod => {
166     req => {
167       'Test::Pod'                 => '1.41',
168     },
169   },
170
171   test_podcoverage => {
172     req => {
173       'Test::Pod::Coverage'       => '1.08',
174       'Pod::Coverage'             => '0.20',
175     },
176   },
177
178   test_notabs => {
179     req => {
180       'Test::NoTabs'              => '0.9',
181     },
182   },
183
184   test_eol => {
185     req => {
186       'Test::EOL'                 => '1.0',
187     },
188   },
189
190   test_prettydebug => {
191     req => $json_any,
192   },
193
194   test_leaks => {
195     req => {
196       'Test::Memory::Cycle'       => '0',
197       'Devel::Cycle'              => '1.10',
198     },
199   },
200
201   test_dt => {
202     req => $datetime_basic,
203   },
204
205   test_dt_sqlite => {
206     req => {
207       %$datetime_basic,
208       # t/36datetime.t
209       # t/60core.t
210       'DateTime::Format::SQLite'  => '0',
211     },
212   },
213
214   test_dt_mysql => {
215     req => {
216       %$datetime_basic,
217       # t/inflate/datetime_mysql.t
218       # (doesn't need Mysql itself)
219       'DateTime::Format::MySQL'   => '0',
220     },
221   },
222
223   test_dt_pg => {
224     req => {
225       %$datetime_basic,
226       # t/inflate/datetime_pg.t
227       # (doesn't need PG itself)
228       'DateTime::Format::Pg'      => '0.16004',
229     },
230   },
231
232   test_cdbicompat => {
233     req => {
234       'Class::DBI' => 0,
235       'Class::DBI::Plugin::DeepAbstractSearch' => '0',
236       %$datetime_basic,
237       'Time::Piece::MySQL'        => '0',
238       'Date::Simple'              => '3.03',
239     },
240   },
241
242   # this is just for completeness as SQLite
243   # is a core dep of DBIC for testing
244   rdbms_sqlite => {
245     req => {
246       %$rdbms_sqlite,
247     },
248     pod => {
249       title => 'SQLite support',
250       desc => 'Modules required to connect to SQLite',
251     },
252   },
253
254   rdbms_pg => {
255     req => {
256       # when changing this list make sure to adjust xt/optional_deps.t
257       %$rdbms_pg,
258     },
259     pod => {
260       title => 'PostgreSQL support',
261       desc => 'Modules required to connect to PostgreSQL',
262     },
263   },
264
265   rdbms_mssql_odbc => {
266     req => {
267       %$rdbms_mssql_odbc,
268     },
269     pod => {
270       title => 'MSSQL support via DBD::ODBC',
271       desc => 'Modules required to connect to MSSQL via DBD::ODBC',
272     },
273   },
274
275   rdbms_mssql_sybase => {
276     req => {
277       %$rdbms_mssql_sybase,
278     },
279     pod => {
280       title => 'MSSQL support via DBD::Sybase',
281       desc => 'Modules required to connect to MSSQL via DBD::Sybase',
282     },
283   },
284
285   rdbms_mssql_ado => {
286     req => {
287       %$rdbms_mssql_ado,
288     },
289     pod => {
290       title => 'MSSQL support via DBD::ADO (Windows only)',
291       desc => 'Modules required to connect to MSSQL via DBD::ADO. This particular DBD is available on Windows only',
292     },
293   },
294
295   rdbms_msaccess_odbc => {
296     req => {
297       %$rdbms_msaccess_odbc,
298     },
299     pod => {
300       title => 'MS Access support via DBD::ODBC',
301       desc => 'Modules required to connect to MS Access via DBD::ODBC',
302     },
303   },
304
305   rdbms_msaccess_ado => {
306     req => {
307       %$rdbms_msaccess_ado,
308     },
309     pod => {
310       title => 'MS Access support via DBD::ADO (Windows only)',
311       desc => 'Modules required to connect to MS Access via DBD::ADO. This particular DBD is available on Windows only',
312     },
313   },
314
315   rdbms_mysql => {
316     req => {
317       %$rdbms_mysql,
318     },
319     pod => {
320       title => 'MySQL support',
321       desc => 'Modules required to connect to MySQL',
322     },
323   },
324
325   rdbms_oracle => {
326     req => {
327       %$rdbms_oracle,
328     },
329     pod => {
330       title => 'Oracle support',
331       desc => 'Modules required to connect to Oracle',
332     },
333   },
334
335   rdbms_ase => {
336     req => {
337       %$rdbms_ase,
338     },
339     pod => {
340       title => 'Sybase ASE support',
341       desc => 'Modules required to connect to Sybase ASE',
342     },
343   },
344
345   rdbms_db2 => {
346     req => {
347       %$rdbms_db2,
348     },
349     pod => {
350       title => 'DB2 support',
351       desc => 'Modules required to connect to DB2',
352     },
353   },
354
355   rdbms_db2_400 => {
356     req => {
357       %$rdbms_db2_400,
358     },
359     pod => {
360       title => 'DB2 on AS/400 support',
361       desc => 'Modules required to connect to DB2 on AS/400',
362     },
363   },
364
365   rdbms_informix => {
366     req => {
367       %$rdbms_informix,
368     },
369     pod => {
370       title => 'Informix support',
371       desc => 'Modules required to connect to Informix',
372     },
373   },
374
375   rdbms_sqlanywhere => {
376     req => {
377       %$rdbms_sqlanywhere,
378     },
379     pod => {
380       title => 'SQLAnywhere support',
381       desc => 'Modules required to connect to SQLAnywhere',
382     },
383   },
384
385   rdbms_sqlanywhere_odbc => {
386     req => {
387       %$rdbms_sqlanywhere_odbc,
388     },
389     pod => {
390       title => 'SQLAnywhere support via DBD::ODBC',
391       desc => 'Modules required to connect to SQLAnywhere via DBD::ODBC',
392     },
393   },
394
395   rdbms_firebird => {
396     req => {
397       %$rdbms_firebird,
398     },
399     pod => {
400       title => 'Firebird support',
401       desc => 'Modules required to connect to Firebird',
402     },
403   },
404
405   rdbms_firebird_interbase => {
406     req => {
407       %$rdbms_firebird_interbase,
408     },
409     pod => {
410       title => 'Firebird support via DBD::InterBase',
411       desc => 'Modules required to connect to Firebird via DBD::InterBase',
412     },
413   },
414
415   rdbms_firebird_odbc => {
416     req => {
417       %$rdbms_firebird_odbc,
418     },
419     pod => {
420       title => 'Firebird support via DBD::ODBC',
421       desc => 'Modules required to connect to Firebird via DBD::ODBC',
422     },
423   },
424
425 # the order does matter because the rdbms support group might require
426 # a different version that the test group
427   test_rdbms_pg => {
428     req => {
429       $ENV{DBICTEST_PG_DSN}
430         ? (
431           # when changing this list make sure to adjust xt/optional_deps.t
432           %$rdbms_pg,
433           ($^O ne 'MSWin32' ? ('Sys::SigAction' => '0') : ()),
434           'DBD::Pg'               => '2.009002',
435         ) : ()
436     },
437   },
438
439   test_rdbms_mssql_odbc => {
440     req => {
441       $ENV{DBICTEST_MSSQL_ODBC_DSN}
442         ? (
443           %$rdbms_mssql_odbc,
444         ) : ()
445     },
446   },
447
448   test_rdbms_mssql_ado => {
449     req => {
450       $ENV{DBICTEST_MSSQL_ADO_DSN}
451         ? (
452           %$rdbms_mssql_ado,
453         ) : ()
454     },
455   },
456
457   test_rdbms_mssql_sybase => {
458     req => {
459       $ENV{DBICTEST_MSSQL_DSN}
460         ? (
461           %$rdbms_mssql_sybase,
462         ) : ()
463     },
464   },
465
466   test_rdbms_msaccess_odbc => {
467     req => {
468       $ENV{DBICTEST_MSACCESS_ODBC_DSN}
469         ? (
470           %$rdbms_msaccess_odbc,
471           %$datetime_basic,
472           'Data::GUID' => '0',
473         ) : ()
474     },
475   },
476
477   test_rdbms_msaccess_ado => {
478     req => {
479       $ENV{DBICTEST_MSACCESS_ADO_DSN}
480         ? (
481           %$rdbms_msaccess_ado,
482           %$datetime_basic,
483           'Data::GUID' => 0,
484         ) : ()
485     },
486   },
487
488   test_rdbms_mysql => {
489     req => {
490       $ENV{DBICTEST_MYSQL_DSN}
491         ? (
492           %$rdbms_mysql,
493         ) : ()
494     },
495   },
496
497   test_rdbms_oracle => {
498     req => {
499       $ENV{DBICTEST_ORA_DSN}
500         ? (
501           %$rdbms_oracle,
502           'DateTime::Format::Oracle' => '0',
503           'DBD::Oracle'              => '1.24',
504         ) : ()
505     },
506   },
507
508   test_rdbms_ase => {
509     req => {
510       $ENV{DBICTEST_SYBASE_DSN}
511         ? (
512           %$rdbms_ase,
513         ) : ()
514     },
515   },
516
517   test_rdbms_db2 => {
518     req => {
519       $ENV{DBICTEST_DB2_DSN}
520         ? (
521           %$rdbms_db2,
522         ) : ()
523     },
524   },
525
526   test_rdbms_db2_400 => {
527     req => {
528       $ENV{DBICTEST_DB2_400_DSN}
529         ? (
530           %$rdbms_db2_400,
531         ) : ()
532     },
533   },
534
535   test_rdbms_informix => {
536     req => {
537       $ENV{DBICTEST_INFORMIX_DSN}
538         ? (
539           %$rdbms_informix,
540         ) : ()
541     },
542   },
543
544   test_rdbms_sqlanywhere => {
545     req => {
546       $ENV{DBICTEST_SQLANYWHERE_DSN}
547         ? (
548           %$rdbms_sqlanywhere,
549         ) : ()
550     },
551   },
552
553   test_rdbms_sqlanywhere_odbc => {
554     req => {
555       $ENV{DBICTEST_SQLANYWHERE_ODBC_DSN}
556         ? (
557           %$rdbms_sqlanywhere_odbc,
558         ) : ()
559     },
560   },
561
562   test_rdbms_firebird => {
563     req => {
564       $ENV{DBICTEST_FIREBIRD_DSN}
565         ? (
566           %$rdbms_firebird,
567         ) : ()
568     },
569   },
570
571   test_rdbms_firebird_interbase => {
572     req => {
573       $ENV{DBICTEST_FIREBIRD_INTERBASE_DSN}
574         ? (
575           %$rdbms_firebird_interbase,
576         ) : ()
577     },
578   },
579
580   test_rdbms_firebird_odbc => {
581     req => {
582       $ENV{DBICTEST_FIREBIRD_ODBC_DSN}
583         ? (
584           %$rdbms_firebird_odbc,
585         ) : ()
586     },
587   },
588
589   test_memcached => {
590     req => {
591       $ENV{DBICTEST_MEMCACHED}
592         ? (
593           'Cache::Memcached' => 0,
594         ) : ()
595     },
596   },
597
598 };
599
600
601 sub req_list_for {
602   my ($class, $group) = @_;
603
604   Carp::croak "req_list_for() expects a requirement group name"
605     unless $group;
606
607   my $deps = $reqs->{$group}{req}
608     or Carp::croak "Requirement group '$group' does not exist";
609
610   return { %$deps };
611 }
612
613
614 our %req_availability_cache;
615 sub req_ok_for {
616   my ($class, $group) = @_;
617
618   Carp::croak "req_ok_for() expects a requirement group name"
619     unless $group;
620
621   return $class->_check_deps($group)->{status};
622 }
623
624 sub req_missing_for {
625   my ($class, $group) = @_;
626
627   Carp::croak "req_missing_for() expects a requirement group name"
628     unless $group;
629
630   return $class->_check_deps($group)->{missing};
631 }
632
633 sub req_errorlist_for {
634   my ($class, $group) = @_;
635
636   Carp::croak "req_errorlist_for() expects a requirement group name"
637     unless $group;
638
639   return $class->_check_deps($group)->{errorlist};
640 }
641
642 sub _check_deps {
643   my ($class, $group) = @_;
644
645   return $req_availability_cache{$group} ||= do {
646
647     my $deps = $class->req_list_for ($group);
648
649     my %errors;
650     for my $mod (keys %$deps) {
651       my $req_line = "require $mod;";
652       if (my $ver = $deps->{$mod}) {
653         $req_line .= "$mod->VERSION($ver);";
654       }
655
656       eval $req_line;
657
658       $errors{$mod} = $@ if $@;
659     }
660
661     my $res;
662
663     if (keys %errors) {
664       my $missing = join (', ', map { $deps->{$_} ? "$_ >= $deps->{$_}" : $_ } (sort keys %errors) );
665       $missing .= " (see $class for details)" if $reqs->{$group}{pod};
666       $res = {
667         status => 0,
668         errorlist => \%errors,
669         missing => $missing,
670       };
671     }
672     else {
673       $res = {
674         status => 1,
675         errorlist => {},
676         missing => '',
677       };
678     }
679
680     $res;
681   };
682 }
683
684 sub req_group_list {
685   return { map { $_ => { %{ $reqs->{$_}{req} || {} } } } (keys %$reqs) };
686 }
687
688 # This is to be called by the author only (automatically in Makefile.PL)
689 sub _gen_pod {
690   my ($class, $distver) = @_;
691
692   my $modfn = __PACKAGE__ . '.pm';
693   $modfn =~ s/\:\:/\//g;
694
695   my $podfn = __FILE__;
696   $podfn =~ s/\.pm$/\.pod/;
697
698   $distver ||=
699     eval { require DBIx::Class; DBIx::Class->VERSION; }
700       ||
701     die
702 "\n\n---------------------------------------------------------------------\n" .
703 'Unable to load core DBIx::Class module to determine current version, '.
704 'possibly due to missing dependencies. Author-mode autodocumentation ' .
705 "halted\n\n" . $@ .
706 "\n\n---------------------------------------------------------------------\n"
707   ;
708
709   my $sqltver = $class->req_list_for ('deploy')->{'SQL::Translator'}
710     or die "Hrmm? No sqlt dep?";
711
712   my @chunks = (
713     <<'EOC',
714 #########################################################################
715 #####################  A U T O G E N E R A T E D ########################
716 #########################################################################
717 #
718 # The contents of this POD file are auto-generated.  Any changes you make
719 # will be lost. If you need to change the generated text edit _gen_pod()
720 # at the end of $modfn
721 #
722 EOC
723     '=head1 NAME',
724     "$class - Optional module dependency specifications (for module authors)",
725     '=head1 SYNOPSIS',
726     <<"EOS",
727 Somewhere in your build-file (e.g. L<Module::Install>'s Makefile.PL):
728
729   ...
730
731   configure_requires 'DBIx::Class' => '$distver';
732
733   require $class;
734
735   my \$deploy_deps = $class->req_list_for('deploy');
736
737   for (keys %\$deploy_deps) {
738     requires \$_ => \$deploy_deps->{\$_};
739   }
740
741   ...
742
743 Note that there are some caveats regarding C<configure_requires()>, more info
744 can be found at L<Module::Install/configure_requires>
745 EOS
746     '=head1 DESCRIPTION',
747     <<'EOD',
748 Some of the less-frequently used features of L<DBIx::Class> have external
749 module dependencies on their own. In order not to burden the average user
750 with modules he will never use, these optional dependencies are not included
751 in the base Makefile.PL. Instead an exception with a descriptive message is
752 thrown when a specific feature is missing one or several modules required for
753 its operation. This module is the central holding place for  the current list
754 of such dependencies, for DBIx::Class core authors, and DBIx::Class extension
755 authors alike.
756 EOD
757     '=head1 CURRENT REQUIREMENT GROUPS',
758     <<'EOD',
759 Dependencies are organized in C<groups> and each group can list one or more
760 required modules, with an optional minimum version (or 0 for any version).
761 The group name can be used in the
762 EOD
763   );
764
765   for my $group (sort keys %$reqs) {
766     my $p = $reqs->{$group}{pod}
767       or next;
768
769     my $modlist = $reqs->{$group}{req}
770       or next;
771
772     next unless keys %$modlist;
773
774     push @chunks, (
775       "=head2 $p->{title}",
776       "$p->{desc}",
777       '=over',
778       ( map { "=item * $_" . ($modlist->{$_} ? " >= $modlist->{$_}" : '') } (sort keys %$modlist) ),
779       '=back',
780       "Requirement group: B<$group>",
781     );
782   }
783
784   push @chunks, (
785     '=head1 METHODS',
786     '=head2 req_group_list',
787     '=over',
788     '=item Arguments: none',
789     '=item Returns: \%list_of_requirement_groups',
790     '=back',
791     <<'EOD',
792 This method should be used by DBIx::Class packagers, to get a hashref of all
793 dependencies keyed by dependency group. Each key (group name) can be supplied
794 to one of the group-specific methods below.
795 EOD
796
797     '=head2 req_list_for',
798     '=over',
799     '=item Arguments: $group_name',
800     '=item Returns: \%list_of_module_version_pairs',
801     '=back',
802     <<'EOD',
803 This method should be used by DBIx::Class extension authors, to determine the
804 version of modules a specific feature requires in the B<current> version of
805 DBIx::Class. See the L</SYNOPSIS> for a real-world
806 example.
807 EOD
808
809     '=head2 req_ok_for',
810     '=over',
811     '=item Arguments: $group_name',
812     '=item Returns: 1|0',
813     '=back',
814     <<'EOD',
815 Returns true or false depending on whether all modules required by
816 C<$group_name> are present on the system and loadable.
817 EOD
818
819     '=head2 req_missing_for',
820     '=over',
821     '=item Arguments: $group_name',
822     '=item Returns: $error_message_string',
823     '=back',
824     <<"EOD",
825 Returns a single line string suitable for inclusion in larger error messages.
826 This method would normally be used by DBIx::Class core-module author, to
827 indicate to the user that he needs to install specific modules before he will
828 be able to use a specific feature.
829
830 For example if some of the requirements for C<deploy> are not available,
831 the returned string could look like:
832
833  SQL::Translator >= $sqltver (see $class for details)
834
835 The author is expected to prepend the necessary text to this message before
836 returning the actual error seen by the user.
837 EOD
838
839     '=head2 req_errorlist_for',
840     '=over',
841     '=item Arguments: $group_name',
842     '=item Returns: \%list_of_loaderrors_per_module',
843     '=back',
844     <<'EOD',
845 Returns a hashref containing the actual errors that occured while attempting
846 to load each module in the requirement group.
847 EOD
848     '=head1 AUTHOR',
849     'See L<DBIx::Class/CONTRIBUTORS>.',
850     '=head1 LICENSE',
851     'You may distribute this code under the same terms as Perl itself',
852   );
853
854   open (my $fh, '>', $podfn) or Carp::croak "Unable to write to $podfn: $!";
855   print $fh join ("\n\n", @chunks);
856   close ($fh);
857 }
858
859 1;