Auto-POD for Optional Deps
Peter Rabbitson [Sat, 13 Feb 2010 01:47:52 +0000 (01:47 +0000)]
.gitignore
Makefile.PL
lib/DBIx/Class/Optional/Dependencies.pm

index ebae942..5aa3840 100644 (file)
@@ -9,5 +9,6 @@ README
 _build/
 blib/
 inc/
+lib/DBIx/Class/Optional/Dependencies.pod
 pm_to_blib
 t/var/
index fad5ce5..ff0149c 100644 (file)
@@ -68,6 +68,10 @@ if ($Module::Install::AUTHOR) {
     unlink 'MANIFEST';
   }
 
+  print "Regenerating Optional/Dependencies.pod\n";
+  require DBIx::Class::Optional::Dependencies;
+  DBIx::Class::Optional::Dependencies->_gen_pod;
+
 # FIXME Disabled due to unsolved issues, ask theorbtwo
 #  require Module::Install::Pod::Inherit;
 #  PodInherit();
@@ -83,7 +87,6 @@ if ($Module::Install::AUTHOR) {
 
 EOW
 
-  require DBIx::Class::Optional::Dependencies;
   $reqs->{test_requires} = {
     %{$reqs->{test_requires}},
     %{DBIx::Class::Optional::Dependencies->all_optional_requirements},
index aa4701f..9cb35e8 100644 (file)
@@ -8,107 +8,138 @@ use Carp;
 # NO EXTERNAL NON-5.8.1 CORE DEPENDENCIES EVER (e.g. C::A::G)
 # This module is to be loaded by Makefile.PM on a pristine system
 
+# POD is generated automatically by calling _gen_pod from the
+# Makefile.PL in $AUTHOR mode
+
 my $reqs = {
   dist => {
     #'Module::Install::Pod::Inherit' => '0.01',
   },
 
   replicated => {
-    'Moose'                     => '0.98',
-    'MooseX::Types'             => '0.21',
-    'namespace::clean'          => '0.11',
-    'Hash::Merge'               => '0.11',
+    req => {
+      'Moose'                     => '0.98',
+      'MooseX::Types'             => '0.21',
+      'namespace::clean'          => '0.11',
+      'Hash::Merge'               => '0.11',
+    },
+    pod => {
+      title => 'Storage::Replicated',
+      desc => 'Modules required for L<DBIx::Class::Storage::DBI::Replicated>',
+    },
   },
 
   admin => {
   },
 
   deploy => {
-    'SQL::Translator'           => '0.11002',
+    req => {
+      'SQL::Translator'           => '0.11002',
+    },
+    pod => {
+      title => 'Storage::DBI::deploy()',
+      desc => 'Modules required for L<DBIx::Class::Storage::DBI/deploy> and L<DBIx::Class::Storage::DBI/deploymen_statements>',
+    },
   },
 
   author => {
-    'Test::Pod'                 => '1.26',
-    'Test::Pod::Coverage'       => '1.08',
-    'Pod::Coverage'             => '0.20',
-    #'Test::NoTabs'              => '0.9',
-    #'Test::EOL'                 => '0.6',
+    req => {
+      'Test::Pod'                 => '1.26',
+      'Test::Pod::Coverage'       => '1.08',
+      'Pod::Coverage'             => '0.20',
+      #'Test::NoTabs'              => '0.9',
+      #'Test::EOL'                 => '0.6',
+    },
   },
 
   core => {
-    # t/52cycle.t
-    'Test::Memory::Cycle'       => '0',
-    'Devel::Cycle'              => '1.10',
-
-    # t/36datetime.t
-    # t/60core.t
-    'DateTime::Format::SQLite'  => '0',
-
-    # t/96_is_deteministic_value.t
-    'DateTime::Format::Strptime'=> '0',
+    req => {
+      # t/52cycle.t
+      'Test::Memory::Cycle'       => '0',
+      'Devel::Cycle'              => '1.10',
+
+      # t/36datetime.t
+      # t/60core.t
+      'DateTime::Format::SQLite'  => '0',
+
+      # t/96_is_deteministic_value.t
+      'DateTime::Format::Strptime'=> '0',
+    },
   },
 
   cdbicompat => {
-    'DBIx::ContextualFetch'     => '0',
-    'Class::DBI::Plugin::DeepAbstractSearch' => '0',
-    'Class::Trigger'            => '0',
-    'Time::Piece::MySQL'        => '0',
-    'Clone'                     => '0',
-    'Date::Simple'              => '3.03',
+    req => {
+      'DBIx::ContextualFetch'     => '0',
+      'Class::DBI::Plugin::DeepAbstractSearch' => '0',
+      'Class::Trigger'            => '0',
+      'Time::Piece::MySQL'        => '0',
+      'Clone'                     => '0',
+      'Date::Simple'              => '3.03',
+    },
   },
 
   rdbms_pg => {
-    $ENV{DBICTEST_PG_DSN}
-      ? (
-        'Sys::SigAction'        => '0',
-        'DBD::Pg'               => '2.009002',
-        'DateTime::Format::Pg'  => '0',
-      ) : ()
+    req => {
+      $ENV{DBICTEST_PG_DSN}
+        ? (
+          'Sys::SigAction'        => '0',
+          'DBD::Pg'               => '2.009002',
+          'DateTime::Format::Pg'  => '0',
+        ) : ()
+    },
   },
 
   rdbms_mysql => {
-    $ENV{DBICTEST_MYSQL_DSN}
-      ? (
-        'DateTime::Format::MySQL' => '0',
-        'DBD::mysql'              => '0',
-      ) : ()
+    req => {
+      $ENV{DBICTEST_MYSQL_DSN}
+        ? (
+          'DateTime::Format::MySQL' => '0',
+          'DBD::mysql'              => '0',
+        ) : ()
+    },
   },
 
   rdbms_oracle => {
-
-    $ENV{DBICTEST_ORA_DSN}
-      ? (
-        'DateTime::Format::Oracle' => '0',
-      ) : ()
+    req => {
+      $ENV{DBICTEST_ORA_DSN}
+        ? (
+          'DateTime::Format::Oracle' => '0',
+        ) : ()
+    },
   },
 
   rdbms_ase => {
-    $ENV{DBICTEST_SYBASE_DSN}
-      ? (
-        'DateTime::Format::Sybase' => 0,
-      ) : ()
+    req => {
+      $ENV{DBICTEST_SYBASE_DSN}
+        ? (
+          'DateTime::Format::Sybase' => 0,
+        ) : ()
+    },
   },
 
   rdbms_asa => {
-    grep $_, @ENV{qw/DBICTEST_SYBASE_ASA_DSN DBICTEST_SYBASE_ASA_ODBC_DSN/}
-      ? (
-        'DateTime::Format::Strptime' => 0,
-      ) : ()
+    req => {
+      grep $_, @ENV{qw/DBICTEST_SYBASE_ASA_DSN DBICTEST_SYBASE_ASA_ODBC_DSN/}
+        ? (
+          'DateTime::Format::Strptime' => 0,
+        ) : ()
+    },
   },
 };
 
+
 sub all_optional_requirements {
-  return { map { %{ $_ || {} } } (values %$reqs) };
+  return { map { %{ $reqs->{$_}{req} || {} } } (keys %$reqs) };
 }
 
 sub req_list_for {
   my ($class, $group) = @_;
 
-  die "req_list_for() expects a requirement group name"
+  croak "req_list_for() expects a requirement group name"
     unless $group;
 
-  my $deps = $reqs->{$group}
-    or die "Requirement group '$group' does not exist";
+  my $deps = $reqs->{$group}{req}
+    or croak "Requirement group '$group' does not exist";
 
   return { %$deps };
 }
@@ -151,8 +182,7 @@ sub req_errorlist_for {
 sub _check_deps {
   my ($class, $group) = @_;
 
-  my $deps = $reqs->{$group}
-    or croak "Requirement group '$group' does not exist";
+  my $deps = $class->req_list_for ($group);
 
   my %errors;
   for my $mod (keys %$deps) {
@@ -167,10 +197,12 @@ sub _check_deps {
   }
 
   if (keys %errors) {
+    my $missing = join (', ', map { $deps->{$_} ? "$_ >= $deps->{$_}" : $_ } (sort keys %errors) );
+    $missing .= " (see $class for details)" if $reqs->{$group}{pod};
     $req_availability_cache{$group} = {
       status => 0,
       errorlist => { %errors },
-      missing => join (', ', map { $deps->{$_} ? "$_ >= $deps->{$_}" : $_ } (sort keys %errors) ),
+      missing => $missing,
     };
   }
   else {
@@ -182,4 +214,116 @@ sub _check_deps {
   }
 }
 
+sub _gen_pod {
+  my $class = shift;
+
+  my @chunks = (
+    '=head1 NAME',
+    "$class - Optional module dependency specifications",
+    '=head1 DESCRIPTION',
+    <<'EOD',
+Some of the less-frequently used features of L<DBIx::Class> have external
+module dependencies on their own. In order not to burden the average user
+with modules he will never use, these optional dependencies are not included
+in the base Makefile.PL. Instead an exception with a descriptive message is
+thrown when a specific feature is missing one or several modules required for
+its operation. This module is the central holding place for  the current list
+of such dependencies.
+EOD
+    '=head1 CURRENT REQUIREMENT GROUPS',
+    <<'EOD',
+Dependencies are organized in C<groups> and each group can list one or more
+required modules, with an optional minimum version (or 0 for any version).
+The group name can be used in the 
+EOD
+  );
+
+  for my $group (sort keys %$reqs) {
+    my $p = $reqs->{$group}{pod}
+      or next;
+
+    my $modlist = $reqs->{$group}{req}
+      or next;
+
+    next unless keys %$modlist;
+
+    push @chunks, (
+      "=head2 $p->{title}",
+      "$p->{desc}",
+      '=over',
+      ( map { "=item * $_" . ($modlist->{$_} ? " >= $modlist->{$_}" : '') } (sort keys %$modlist) ),
+      '=back',
+      "Requirement group: B<$group>",
+    );
+  }
+
+  push @chunks, (
+    '=head1 METHODS',
+    '=head2 req_list_for',
+    '=over',
+    '=item Arguments: $group_name',
+    '=item Returns: \%list_of_module_version_pairs',
+    '=back',
+    <<EOD,
+This method should be used by DBIx::Class extension authors, to determine the
+version of modules which a specific feature requires in the current version of
+DBIx::Class. For example if you write a module/extension that requires
+DBIx::Class and also requires the availability of
+L<DBIx::Class::Storage::DBI/deploy>, you can do the following in your
+C<Makefile.PL> or C<Build.PL>
+
+ require $class;
+ my \$dep_list = $class->req_list_for ('deploy');
+
+Which will give you a list of module/version pairs necessary for the particular
+feature to function with this version of DBIx::Class.
+EOD
+
+    '=head2 req_ok_for',
+    '=over',
+    '=item Arguments: $group_name',
+    '=item Returns: 1|0',
+    '=back',
+    'Returns true or false depending on whether all modules required by $group_name are present on the system and loadable',
+
+    '=head2 req_missing_for',
+    '=over',
+    '=item Arguments: $group_name',
+    '=item Returns: $error_message_string',
+    '=back',
+    <<EOD,
+Returns a single line string suitable for inclusion in larger error messages.
+This method would normally be used by DBIx::Class core-module author, to
+indicate to the user that he needs to install specific modules before he will
+be able to use a specific feature.
+
+For example if the requirements for C<replicated> are not available, the
+returned string would look like:
+
+ Moose >= 0.98, MooseX::Types >= 0.21, namespace::clean (see $class for details)
+
+The author is expected to prepend the necessary text to this message before
+returning the actual error seen by the user.
+EOD
+
+    '=head2 req_errorlist_for',
+    '=over',
+    '=item Arguments: $group_name',
+    '=item Returns: \%list_of_loaderrors_per_module',
+    '=back',
+    <<'EOD',
+Returns a hashref containing the actual errors that occured while attempting
+to load each module in the requirement group.
+EOD
+
+  );
+
+  my $fn = __FILE__;
+  $fn =~ s/\.pm$/\.pod/;
+
+  open (my $fh, '>', $fn) or croak "Unable to write to $fn: $!";
+  print $fh join ("\n\n", @chunks);
+  close ($fh);
+}
+
 1;