Trailing WS crusade - got to save them bits
[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       'DBIx::ContextualFetch'     => '0',
235       'Class::DBI::Plugin::DeepAbstractSearch' => '0',
236       'Class::Trigger'            => '0',
237       'Time::Piece::MySQL'        => '0',
238       'Clone'                     => '0',
239       'Date::Simple'              => '3.03',
240     },
241   },
242
243   # this is just for completeness as SQLite
244   # is a core dep of DBIC for testing
245   rdbms_sqlite => {
246     req => {
247       %$rdbms_sqlite,
248     },
249     pod => {
250       title => 'SQLite support',
251       desc => 'Modules required to connect to SQLite',
252     },
253   },
254
255   rdbms_pg => {
256     req => {
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           %$rdbms_pg,
432           ($^O ne 'MSWin32' ? ('Sys::SigAction' => '0') : ()),
433           'DBD::Pg'               => '2.009002',
434         ) : ()
435     },
436   },
437
438   test_rdbms_mssql_odbc => {
439     req => {
440       $ENV{DBICTEST_MSSQL_ODBC_DSN}
441         ? (
442           %$rdbms_mssql_odbc,
443         ) : ()
444     },
445   },
446
447   test_rdbms_mssql_ado => {
448     req => {
449       $ENV{DBICTEST_MSSQL_ADO_DSN}
450         ? (
451           %$rdbms_mssql_ado,
452         ) : ()
453     },
454   },
455
456   test_rdbms_mssql_sybase => {
457     req => {
458       $ENV{DBICTEST_MSSQL_DSN}
459         ? (
460           %$rdbms_mssql_sybase,
461         ) : ()
462     },
463   },
464
465   test_rdbms_msaccess_odbc => {
466     req => {
467       $ENV{DBICTEST_MSACCESS_ODBC_DSN}
468         ? (
469           %$rdbms_msaccess_odbc,
470           %$datetime_basic,
471           'Data::GUID' => '0',
472         ) : ()
473     },
474   },
475
476   test_rdbms_msaccess_ado => {
477     req => {
478       $ENV{DBICTEST_MSACCESS_ADO_DSN}
479         ? (
480           %$rdbms_msaccess_ado,
481           %$datetime_basic,
482           'Data::GUID' => 0,
483         ) : ()
484     },
485   },
486
487   test_rdbms_mysql => {
488     req => {
489       $ENV{DBICTEST_MYSQL_DSN}
490         ? (
491           %$rdbms_mysql,
492         ) : ()
493     },
494   },
495
496   test_rdbms_oracle => {
497     req => {
498       $ENV{DBICTEST_ORA_DSN}
499         ? (
500           %$rdbms_oracle,
501           'DateTime::Format::Oracle' => '0',
502           'DBD::Oracle'              => '1.24',
503         ) : ()
504     },
505   },
506
507   test_rdbms_ase => {
508     req => {
509       $ENV{DBICTEST_SYBASE_DSN}
510         ? (
511           %$rdbms_ase,
512         ) : ()
513     },
514   },
515
516   test_rdbms_db2 => {
517     req => {
518       $ENV{DBICTEST_DB2_DSN}
519         ? (
520           %$rdbms_db2,
521         ) : ()
522     },
523   },
524
525   test_rdbms_db2_400 => {
526     req => {
527       $ENV{DBICTEST_DB2_400_DSN}
528         ? (
529           %$rdbms_db2_400,
530         ) : ()
531     },
532   },
533
534   test_rdbms_informix => {
535     req => {
536       $ENV{DBICTEST_INFORMIX_DSN}
537         ? (
538           %$rdbms_informix,
539         ) : ()
540     },
541   },
542
543   test_rdbms_sqlanywhere => {
544     req => {
545       $ENV{DBICTEST_SQLANYWHERE_DSN}
546         ? (
547           %$rdbms_sqlanywhere,
548         ) : ()
549     },
550   },
551
552   test_rdbms_sqlanywhere_odbc => {
553     req => {
554       $ENV{DBICTEST_SQLANYWHERE_ODBC_DSN}
555         ? (
556           %$rdbms_sqlanywhere_odbc,
557         ) : ()
558     },
559   },
560
561   test_rdbms_firebird => {
562     req => {
563       $ENV{DBICTEST_FIREBIRD_DSN}
564         ? (
565           %$rdbms_firebird,
566         ) : ()
567     },
568   },
569
570   test_rdbms_firebird_interbase => {
571     req => {
572       $ENV{DBICTEST_FIREBIRD_INTERBASE_DSN}
573         ? (
574           %$rdbms_firebird_interbase,
575         ) : ()
576     },
577   },
578
579   test_rdbms_firebird_odbc => {
580     req => {
581       $ENV{DBICTEST_FIREBIRD_ODBC_DSN}
582         ? (
583           %$rdbms_firebird_odbc,
584         ) : ()
585     },
586   },
587
588   test_memcached => {
589     req => {
590       $ENV{DBICTEST_MEMCACHED}
591         ? (
592           'Cache::Memcached' => 0,
593         ) : ()
594     },
595   },
596
597 };
598
599
600 sub req_list_for {
601   my ($class, $group) = @_;
602
603   Carp::croak "req_list_for() expects a requirement group name"
604     unless $group;
605
606   my $deps = $reqs->{$group}{req}
607     or Carp::croak "Requirement group '$group' does not exist";
608
609   return { %$deps };
610 }
611
612
613 our %req_availability_cache;
614 sub req_ok_for {
615   my ($class, $group) = @_;
616
617   Carp::croak "req_ok_for() expects a requirement group name"
618     unless $group;
619
620   return $class->_check_deps($group)->{status};
621 }
622
623 sub req_missing_for {
624   my ($class, $group) = @_;
625
626   Carp::croak "req_missing_for() expects a requirement group name"
627     unless $group;
628
629   return $class->_check_deps($group)->{missing};
630 }
631
632 sub req_errorlist_for {
633   my ($class, $group) = @_;
634
635   Carp::croak "req_errorlist_for() expects a requirement group name"
636     unless $group;
637
638   return $class->_check_deps($group)->{errorlist};
639 }
640
641 sub _check_deps {
642   my ($class, $group) = @_;
643
644   return $req_availability_cache{$group} ||= do {
645
646     my $deps = $class->req_list_for ($group);
647
648     my %errors;
649     for my $mod (keys %$deps) {
650       my $req_line = "require $mod;";
651       if (my $ver = $deps->{$mod}) {
652         $req_line .= "$mod->VERSION($ver);";
653       }
654
655       eval $req_line;
656
657       $errors{$mod} = $@ if $@;
658     }
659
660     my $res;
661
662     if (keys %errors) {
663       my $missing = join (', ', map { $deps->{$_} ? "$_ >= $deps->{$_}" : $_ } (sort keys %errors) );
664       $missing .= " (see $class for details)" if $reqs->{$group}{pod};
665       $res = {
666         status => 0,
667         errorlist => \%errors,
668         missing => $missing,
669       };
670     }
671     else {
672       $res = {
673         status => 1,
674         errorlist => {},
675         missing => '',
676       };
677     }
678
679     $res;
680   };
681 }
682
683 sub req_group_list {
684   return { map { $_ => { %{ $reqs->{$_}{req} || {} } } } (keys %$reqs) };
685 }
686
687 # This is to be called by the author only (automatically in Makefile.PL)
688 sub _gen_pod {
689   my ($class, $distver) = @_;
690
691   my $modfn = __PACKAGE__ . '.pm';
692   $modfn =~ s/\:\:/\//g;
693
694   my $podfn = __FILE__;
695   $podfn =~ s/\.pm$/\.pod/;
696
697   $distver ||=
698     eval { require DBIx::Class; DBIx::Class->VERSION; }
699       ||
700     die
701 "\n\n---------------------------------------------------------------------\n" .
702 'Unable to load core DBIx::Class module to determine current version, '.
703 'possibly due to missing dependencies. Author-mode autodocumentation ' .
704 "halted\n\n" . $@ .
705 "\n\n---------------------------------------------------------------------\n"
706   ;
707
708   my $sqltver = $class->req_list_for ('deploy')->{'SQL::Translator'}
709     or die "Hrmm? No sqlt dep?";
710
711   my @chunks = (
712     <<'EOC',
713 #########################################################################
714 #####################  A U T O G E N E R A T E D ########################
715 #########################################################################
716 #
717 # The contents of this POD file are auto-generated.  Any changes you make
718 # will be lost. If you need to change the generated text edit _gen_pod()
719 # at the end of $modfn
720 #
721 EOC
722     '=head1 NAME',
723     "$class - Optional module dependency specifications (for module authors)",
724     '=head1 SYNOPSIS',
725     <<"EOS",
726 Somewhere in your build-file (e.g. L<Module::Install>'s Makefile.PL):
727
728   ...
729
730   configure_requires 'DBIx::Class' => '$distver';
731
732   require $class;
733
734   my \$deploy_deps = $class->req_list_for('deploy');
735
736   for (keys %\$deploy_deps) {
737     requires \$_ => \$deploy_deps->{\$_};
738   }
739
740   ...
741
742 Note that there are some caveats regarding C<configure_requires()>, more info
743 can be found at L<Module::Install/configure_requires>
744 EOS
745     '=head1 DESCRIPTION',
746     <<'EOD',
747 Some of the less-frequently used features of L<DBIx::Class> have external
748 module dependencies on their own. In order not to burden the average user
749 with modules he will never use, these optional dependencies are not included
750 in the base Makefile.PL. Instead an exception with a descriptive message is
751 thrown when a specific feature is missing one or several modules required for
752 its operation. This module is the central holding place for  the current list
753 of such dependencies, for DBIx::Class core authors, and DBIx::Class extension
754 authors alike.
755 EOD
756     '=head1 CURRENT REQUIREMENT GROUPS',
757     <<'EOD',
758 Dependencies are organized in C<groups> and each group can list one or more
759 required modules, with an optional minimum version (or 0 for any version).
760 The group name can be used in the
761 EOD
762   );
763
764   for my $group (sort keys %$reqs) {
765     my $p = $reqs->{$group}{pod}
766       or next;
767
768     my $modlist = $reqs->{$group}{req}
769       or next;
770
771     next unless keys %$modlist;
772
773     push @chunks, (
774       "=head2 $p->{title}",
775       "$p->{desc}",
776       '=over',
777       ( map { "=item * $_" . ($modlist->{$_} ? " >= $modlist->{$_}" : '') } (sort keys %$modlist) ),
778       '=back',
779       "Requirement group: B<$group>",
780     );
781   }
782
783   push @chunks, (
784     '=head1 METHODS',
785     '=head2 req_group_list',
786     '=over',
787     '=item Arguments: none',
788     '=item Returns: \%list_of_requirement_groups',
789     '=back',
790     <<'EOD',
791 This method should be used by DBIx::Class packagers, to get a hashref of all
792 dependencies keyed by dependency group. Each key (group name) can be supplied
793 to one of the group-specific methods below.
794 EOD
795
796     '=head2 req_list_for',
797     '=over',
798     '=item Arguments: $group_name',
799     '=item Returns: \%list_of_module_version_pairs',
800     '=back',
801     <<'EOD',
802 This method should be used by DBIx::Class extension authors, to determine the
803 version of modules a specific feature requires in the B<current> version of
804 DBIx::Class. See the L</SYNOPSIS> for a real-world
805 example.
806 EOD
807
808     '=head2 req_ok_for',
809     '=over',
810     '=item Arguments: $group_name',
811     '=item Returns: 1|0',
812     '=back',
813     <<'EOD',
814 Returns true or false depending on whether all modules required by
815 C<$group_name> are present on the system and loadable.
816 EOD
817
818     '=head2 req_missing_for',
819     '=over',
820     '=item Arguments: $group_name',
821     '=item Returns: $error_message_string',
822     '=back',
823     <<"EOD",
824 Returns a single line string suitable for inclusion in larger error messages.
825 This method would normally be used by DBIx::Class core-module author, to
826 indicate to the user that he needs to install specific modules before he will
827 be able to use a specific feature.
828
829 For example if some of the requirements for C<deploy> are not available,
830 the returned string could look like:
831
832  SQL::Translator >= $sqltver (see $class for details)
833
834 The author is expected to prepend the necessary text to this message before
835 returning the actual error seen by the user.
836 EOD
837
838     '=head2 req_errorlist_for',
839     '=over',
840     '=item Arguments: $group_name',
841     '=item Returns: \%list_of_loaderrors_per_module',
842     '=back',
843     <<'EOD',
844 Returns a hashref containing the actual errors that occured while attempting
845 to load each module in the requirement group.
846 EOD
847     '=head1 AUTHOR',
848     'See L<DBIx::Class/CONTRIBUTORS>.',
849     '=head1 LICENSE',
850     'You may distribute this code under the same terms as Perl itself',
851   );
852
853   open (my $fh, '>', $podfn) or Carp::croak "Unable to write to $podfn: $!";
854   print $fh join ("\n\n", @chunks);
855   close ($fh);
856 }
857
858 1;