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