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