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