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