e4988e83bc07f07ad9932cd2ba55f56a5a157675
[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.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           ($^O ne 'MSWin32' ? ('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         ) : ()
425     },
426   },
427
428   test_rdbms_db2 => {
429     req => {
430       $ENV{DBICTEST_DB2_DSN}
431         ? (
432           %$rdbms_db2,
433         ) : ()
434     },
435   },
436
437   test_rdbms_firebird_odbc => {
438     req => {
439       $ENV{DBICTEST_FIREBIRD_ODBC_DSN}
440         ? (
441           %$rdbms_firebird_odbc,
442         ) : ()
443     },
444   },
445
446   test_memcached => {
447     req => {
448       $ENV{DBICTEST_MEMCACHED}
449         ? (
450           'Cache::Memcached' => 0,
451         ) : ()
452     },
453   },
454
455 };
456
457
458 sub req_list_for {
459   my ($class, $group) = @_;
460
461   Carp::croak "req_list_for() expects a requirement group name"
462     unless $group;
463
464   my $deps = $reqs->{$group}{req}
465     or Carp::croak "Requirement group '$group' does not exist";
466
467   return { %$deps };
468 }
469
470
471 our %req_availability_cache;
472 sub req_ok_for {
473   my ($class, $group) = @_;
474
475   Carp::croak "req_ok_for() expects a requirement group name"
476     unless $group;
477
478   return $class->_check_deps($group)->{status};
479 }
480
481 sub req_missing_for {
482   my ($class, $group) = @_;
483
484   Carp::croak "req_missing_for() expects a requirement group name"
485     unless $group;
486
487   return $class->_check_deps($group)->{missing};
488 }
489
490 sub req_errorlist_for {
491   my ($class, $group) = @_;
492
493   Carp::croak "req_errorlist_for() expects a requirement group name"
494     unless $group;
495
496   return $class->_check_deps($group)->{errorlist};
497 }
498
499 sub _check_deps {
500   my ($class, $group) = @_;
501
502   return $req_availability_cache{$group} ||= do {
503
504     my $deps = $class->req_list_for ($group);
505
506     my %errors;
507     for my $mod (keys %$deps) {
508       my $req_line = "require $mod;";
509       if (my $ver = $deps->{$mod}) {
510         $req_line .= "$mod->VERSION($ver);";
511       }
512
513       eval $req_line;
514
515       $errors{$mod} = $@ if $@;
516     }
517
518     my $res;
519
520     if (keys %errors) {
521       my $missing = join (', ', map { $deps->{$_} ? "$_ >= $deps->{$_}" : $_ } (sort keys %errors) );
522       $missing .= " (see $class for details)" if $reqs->{$group}{pod};
523       $missing .= "\n";
524       $res = {
525         status => 0,
526         errorlist => \%errors,
527         missing => $missing,
528       };
529     }
530     else {
531       $res = {
532         status => 1,
533         errorlist => {},
534         missing => '',
535       };
536     }
537
538     $res;
539   };
540 }
541
542 sub req_group_list {
543   return { map { $_ => { %{ $reqs->{$_}{req} || {} } } } (keys %$reqs) };
544 }
545
546 # This is to be called by the author only (automatically in Makefile.PL)
547 sub _gen_pod {
548   my ($class, $distver) = @_;
549
550   my $modfn = __PACKAGE__ . '.pm';
551   $modfn =~ s/\:\:/\//g;
552
553   my $podfn = __FILE__;
554   $podfn =~ s/\.pm$/\.pod/;
555
556   $distver ||=
557     eval { require DBIx::Class; DBIx::Class->VERSION; }
558       ||
559     die
560 "\n\n---------------------------------------------------------------------\n" .
561 'Unable to load core DBIx::Class module to determine current version, '.
562 'possibly due to missing dependencies. Author-mode autodocumentation ' .
563 "halted\n\n" . $@ .
564 "\n\n---------------------------------------------------------------------\n"
565   ;
566
567   my $sqltver = $class->req_list_for ('deploy')->{'SQL::Translator'}
568     or die "Hrmm? No sqlt dep?";
569
570   my @chunks = (
571     <<'EOC',
572 #########################################################################
573 #####################  A U T O G E N E R A T E D ########################
574 #########################################################################
575 #
576 # The contents of this POD file are auto-generated.  Any changes you make
577 # will be lost. If you need to change the generated text edit _gen_pod()
578 # at the end of $modfn
579 #
580 EOC
581     '=head1 NAME',
582     "$class - Optional module dependency specifications (for module authors)",
583     '=head1 SYNOPSIS',
584     <<"EOS",
585 Somewhere in your build-file (e.g. L<Module::Install>'s Makefile.PL):
586
587   ...
588
589   configure_requires 'DBIx::Class' => '$distver';
590
591   require $class;
592
593   my \$deploy_deps = $class->req_list_for('deploy');
594
595   for (keys %\$deploy_deps) {
596     requires \$_ => \$deploy_deps->{\$_};
597   }
598
599   ...
600
601 Note that there are some caveats regarding C<configure_requires()>, more info
602 can be found at L<Module::Install/configure_requires>
603 EOS
604     '=head1 DESCRIPTION',
605     <<'EOD',
606 Some of the less-frequently used features of L<DBIx::Class> have external
607 module dependencies on their own. In order not to burden the average user
608 with modules he will never use, these optional dependencies are not included
609 in the base Makefile.PL. Instead an exception with a descriptive message is
610 thrown when a specific feature is missing one or several modules required for
611 its operation. This module is the central holding place for  the current list
612 of such dependencies, for DBIx::Class core authors, and DBIx::Class extension
613 authors alike.
614 EOD
615     '=head1 CURRENT REQUIREMENT GROUPS',
616     <<'EOD',
617 Dependencies are organized in C<groups> and each group can list one or more
618 required modules, with an optional minimum version (or 0 for any version).
619 The group name can be used in the
620 EOD
621   );
622
623   for my $group (sort keys %$reqs) {
624     my $p = $reqs->{$group}{pod}
625       or next;
626
627     my $modlist = $reqs->{$group}{req}
628       or next;
629
630     next unless keys %$modlist;
631
632     push @chunks, (
633       "=head2 $p->{title}",
634       "$p->{desc}",
635       '=over',
636       ( map { "=item * $_" . ($modlist->{$_} ? " >= $modlist->{$_}" : '') } (sort keys %$modlist) ),
637       '=back',
638       "Requirement group: B<$group>",
639     );
640   }
641
642   push @chunks, (
643     '=head1 METHODS',
644     '=head2 req_group_list',
645     '=over',
646     '=item Arguments: none',
647     '=item Returns: \%list_of_requirement_groups',
648     '=back',
649     <<'EOD',
650 This method should be used by DBIx::Class packagers, to get a hashref of all
651 dependencies keyed by dependency group. Each key (group name) can be supplied
652 to one of the group-specific methods below.
653 EOD
654
655     '=head2 req_list_for',
656     '=over',
657     '=item Arguments: $group_name',
658     '=item Returns: \%list_of_module_version_pairs',
659     '=back',
660     <<'EOD',
661 This method should be used by DBIx::Class extension authors, to determine the
662 version of modules a specific feature requires in the B<current> version of
663 DBIx::Class. See the L</SYNOPSIS> for a real-world
664 example.
665 EOD
666
667     '=head2 req_ok_for',
668     '=over',
669     '=item Arguments: $group_name',
670     '=item Returns: 1|0',
671     '=back',
672     <<'EOD',
673 Returns true or false depending on whether all modules required by
674 C<$group_name> are present on the system and loadable.
675 EOD
676
677     '=head2 req_missing_for',
678     '=over',
679     '=item Arguments: $group_name',
680     '=item Returns: $error_message_string',
681     '=back',
682     <<"EOD",
683 Returns a single line string suitable for inclusion in larger error messages.
684 This method would normally be used by DBIx::Class core-module author, to
685 indicate to the user that he needs to install specific modules before he will
686 be able to use a specific feature.
687
688 For example if some of the requirements for C<deploy> are not available,
689 the returned string could look like:
690
691  SQL::Translator >= $sqltver (see $class for details)
692
693 The author is expected to prepend the necessary text to this message before
694 returning the actual error seen by the user.
695 EOD
696
697     '=head2 req_errorlist_for',
698     '=over',
699     '=item Arguments: $group_name',
700     '=item Returns: \%list_of_loaderrors_per_module',
701     '=back',
702     <<'EOD',
703 Returns a hashref containing the actual errors that occured while attempting
704 to load each module in the requirement group.
705 EOD
706     '=head1 AUTHOR',
707     'See L<DBIx::Class/CONTRIBUTORS>.',
708     '=head1 LICENSE',
709     'You may distribute this code under the same terms as Perl itself',
710   );
711
712   open (my $fh, '>', $podfn) or Carp::croak "Unable to write to $podfn: $!";
713   print $fh join ("\n\n", @chunks);
714   close ($fh);
715 }
716
717 1;