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