Overhaul serialization test, add optional Memcached testing
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Optional / Dependencies.pm
index 06752bb..30d6244 100644 (file)
@@ -11,9 +11,31 @@ use Carp;
 # POD is generated automatically by calling _gen_pod from the
 # Makefile.PL in $AUTHOR mode
 
+my $json_any = {
+  'JSON::Any'                     => '1.22',
+};
+
 my $moose_basic = {
-  'Moose'                      => '0.98',
-  'MooseX::Types'              => '0.21',
+  'Moose'                         => '0.98',
+  'MooseX::Types'                 => '0.21',
+};
+
+my $replicated = {
+  %$moose_basic,
+  'Hash::Merge'                   => '0.12',
+};
+
+my $admin_basic = {
+  %$moose_basic,
+  %$json_any,
+  'MooseX::Types::Path::Class'    => '0.05',
+  'MooseX::Types::JSON'           => '0.02',
+  'namespace::autoclean'          => '0.09',
+};
+
+my $datetime_basic = {
+  'DateTime'                      => '0.55',
+  'DateTime::Format::Strptime'    => '1.2',
 };
 
 my $reqs = {
@@ -22,34 +44,47 @@ my $reqs = {
   },
 
   replicated => {
-    req => {
-      %$moose_basic,
-      'namespace::clean'          => '0.11',
-      'Hash::Merge'               => '0.11',
-    },
+    req => $replicated,
     pod => {
       title => 'Storage::Replicated',
       desc => 'Modules required for L<DBIx::Class::Storage::DBI::Replicated>',
     },
   },
 
+  test_replicated => {
+    req => {
+      %$replicated,
+      'Test::Moose'               => '0',
+    },
+  },
+
+
   admin => {
     req => {
+      %$admin_basic,
+    },
+    pod => {
+      title => 'DBIx::Class::Admin',
+      desc => 'Modules required for the DBIx::Class administrative library',
+    },
+  },
+
+  admin_script => {
+    req => {
       %$moose_basic,
-      'MooseX::Types::Path::Class'=> '0.05',
-      'MooseX::Types::JSON'       => '0.02',
-      'Try::Tiny'                 => '0.02',
-      'namespace::autoclean'      => '0.09',
-      'parent'                    => '0.223',
+      %$admin_basic,
       'Getopt::Long::Descriptive' => '0.081',
-      'JSON::Any'                 => '1.22',
       'Text::CSV'                 => '1.16',
     },
+    pod => {
+      title => 'dbicadmin',
+      desc => 'Modules required for the CLI DBIx::Class interface dbicadmin',
+    },
   },
 
   deploy => {
     req => {
-      'SQL::Translator'           => '0.11002',
+      'SQL::Translator'           => '0.11006',
     },
     pod => {
       title => 'Storage::DBI::deploy()',
@@ -57,32 +92,74 @@ my $reqs = {
     },
   },
 
-  author => {
+  test_pod => {
+    req => {
+      'Test::Pod'                 => '1.41',
+    },
+  },
+
+  test_podcoverage => {
     req => {
-      'Test::Pod'                 => '1.26',
       'Test::Pod::Coverage'       => '1.08',
       'Pod::Coverage'             => '0.20',
-      #'Test::NoTabs'              => '0.9',
-      #'Test::EOL'                 => '0.6',
     },
   },
 
-  core => {
+  test_notabs => {
+    req => {
+      'Test::NoTabs'              => '0.9',
+    },
+  },
+
+  test_eol => {
+    req => {
+      'Test::EOL'                 => '0.6',
+    },
+  },
+
+  test_prettydebug => {
+    req => $json_any,
+  },
+
+  test_leaks => {
     req => {
-      # t/52cycle.t
       'Test::Memory::Cycle'       => '0',
       'Devel::Cycle'              => '1.10',
+    },
+  },
 
+  test_dt => {
+    req => $datetime_basic,
+  },
+
+  test_dt_sqlite => {
+    req => {
+      %$datetime_basic,
       # t/36datetime.t
       # t/60core.t
       'DateTime::Format::SQLite'  => '0',
+    },
+  },
 
-      # t/96_is_deteministic_value.t
-      'DateTime::Format::Strptime'=> '0',
+  test_dt_mysql => {
+    req => {
+      %$datetime_basic,
+      # t/inflate/datetime_mysql.t
+      # (doesn't need Mysql itself)
+      'DateTime::Format::MySQL'   => '0',
     },
   },
 
-  cdbicompat => {
+  test_dt_pg => {
+    req => {
+      %$datetime_basic,
+      # t/inflate/datetime_pg.t
+      # (doesn't need PG itself)
+      'DateTime::Format::Pg'      => '0.16004',
+    },
+  },
+
+  test_cdbicompat => {
     req => {
       'DBIx::ContextualFetch'     => '0',
       'Class::DBI::Plugin::DeepAbstractSearch' => '0',
@@ -93,37 +170,54 @@ my $reqs = {
     },
   },
 
-  rdbms_pg => {
+  test_rdbms_pg => {
     req => {
       $ENV{DBICTEST_PG_DSN}
         ? (
           'Sys::SigAction'        => '0',
           'DBD::Pg'               => '2.009002',
-          'DateTime::Format::Pg'  => '0',
         ) : ()
     },
   },
 
-  rdbms_mysql => {
+  test_rdbms_mssql_odbc => {
+    req => {
+      $ENV{DBICTEST_MSSQL_ODBC_DSN}
+        ? (
+          'DBD::ODBC'             => '0',
+        ) : ()
+    },
+  },
+
+  test_rdbms_mssql_sybase => {
+    req => {
+      $ENV{DBICTEST_MSSQL_DSN}
+        ? (
+          'DBD::Sybase'           => '0',
+        ) : ()
+    },
+  },
+
+  test_rdbms_mysql => {
     req => {
       $ENV{DBICTEST_MYSQL_DSN}
         ? (
-          'DateTime::Format::MySQL' => '0',
-          'DBD::mysql'              => '0',
+          'DBD::mysql'            => '0',
         ) : ()
     },
   },
 
-  rdbms_oracle => {
+  test_rdbms_oracle => {
     req => {
       $ENV{DBICTEST_ORA_DSN}
         ? (
           'DateTime::Format::Oracle' => '0',
+          'DBD::Oracle'              => '1.24',
         ) : ()
     },
   },
 
-  rdbms_ase => {
+  test_rdbms_ase => {
     req => {
       $ENV{DBICTEST_SYBASE_DSN}
         ? (
@@ -132,20 +226,26 @@ my $reqs = {
     },
   },
 
-  rdbms_asa => {
+  test_rdbms_db2 => {
     req => {
-      grep $_, @ENV{qw/DBICTEST_SYBASE_ASA_DSN DBICTEST_SYBASE_ASA_ODBC_DSN/}
+      $ENV{DBICTEST_DB2_DSN}
         ? (
-          'DateTime::Format::Strptime' => 0,
+          'DBD::DB2' => 0,
         ) : ()
     },
   },
-};
 
+  test_memcached => {
+    req => {
+      $ENV{DBICTEST_MEMCACHED}
+        ? (
+          'Cache::Memcached' => 0,
+        ) : ()
+    },
+  },
+
+};
 
-sub _all_optional_requirements {
-  return { map { %{ $reqs->{$_}{req} || {} } } (keys %$reqs) };
-}
 
 sub req_list_for {
   my ($class, $group) = @_;
@@ -167,9 +267,7 @@ sub req_ok_for {
   croak "req_ok_for() expects a requirement group name"
     unless $group;
 
-  $class->_check_deps ($group) unless $req_availability_cache{$group};
-
-  return $req_availability_cache{$group}{status};
+  return $class->_check_deps($group)->{status};
 }
 
 sub req_missing_for {
@@ -178,9 +276,7 @@ sub req_missing_for {
   croak "req_missing_for() expects a requirement group name"
     unless $group;
 
-  $class->_check_deps ($group) unless $req_availability_cache{$group};
-
-  return $req_availability_cache{$group}{missing};
+  return $class->_check_deps($group)->{missing};
 }
 
 sub req_errorlist_for {
@@ -189,52 +285,113 @@ sub req_errorlist_for {
   croak "req_errorlist_for() expects a requirement group name"
     unless $group;
 
-  $class->_check_deps ($group) unless $req_availability_cache{$group};
-
-  return $req_availability_cache{$group}{errorlist};
+  return $class->_check_deps($group)->{errorlist};
 }
 
 sub _check_deps {
   my ($class, $group) = @_;
 
-  my $deps = $class->req_list_for ($group);
+  return $req_availability_cache{$group} ||= do {
+
+    my $deps = $class->req_list_for ($group);
 
-  my %errors;
-  for my $mod (keys %$deps) {
-    if (my $ver = $deps->{$mod}) {
-      eval "use $mod $ver ()";
+    my %errors;
+    for my $mod (keys %$deps) {
+      my $req_line = "require $mod;";
+      if (my $ver = $deps->{$mod}) {
+        $req_line .= "$mod->VERSION($ver);";
+      }
+
+      eval $req_line;
+
+      $errors{$mod} = $@ if $@;
+    }
+
+    my $res;
+
+    if (keys %errors) {
+      my $missing = join (', ', map { $deps->{$_} ? "$_ >= $deps->{$_}" : $_ } (sort keys %errors) );
+      $missing .= " (see $class for details)" if $reqs->{$group}{pod};
+      $res = {
+        status => 0,
+        errorlist => \%errors,
+        missing => $missing,
+      };
     }
     else {
-      eval "require $mod";
+      $res = {
+        status => 1,
+        errorlist => {},
+        missing => '',
+      };
     }
 
-    $errors{$mod} = $@ if $@;
-  }
+    $res;
+  };
+}
 
-  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 => $missing,
-    };
-  }
-  else {
-    $req_availability_cache{$group} = {
-      status => 1,
-      errorlist => {},
-      missing => '',
-    };
-  }
+sub req_group_list {
+  return { map { $_ => { %{ $reqs->{$_}{req} || {} } } } (keys %$reqs) };
 }
 
+# This is to be called by the author only (automatically in Makefile.PL)
 sub _gen_pod {
-  my $class = shift;
+  my ($class, $distver) = @_;
+
+  my $modfn = __PACKAGE__ . '.pm';
+  $modfn =~ s/\:\:/\//g;
+
+  my $podfn = __FILE__;
+  $podfn =~ s/\.pm$/\.pod/;
+
+  $distver ||=
+    eval { require DBIx::Class; DBIx::Class->VERSION; }
+      ||
+    die
+"\n\n---------------------------------------------------------------------\n" .
+'Unable to load core DBIx::Class module to determine current version, '.
+'possibly due to missing dependencies. Author-mode autodocumentation ' .
+"halted\n\n" . $@ .
+"\n\n---------------------------------------------------------------------\n"
+  ;
+
+  my $sqltver = $class->req_list_for ('deploy')->{'SQL::Translator'}
+    or die "Hrmm? No sqlt dep?";
 
   my @chunks = (
+    <<'EOC',
+#########################################################################
+#####################  A U T O G E N E R A T E D ########################
+#########################################################################
+#
+# The contents of this POD file are auto-generated.  Any changes you make
+# will be lost. If you need to change the generated text edit _gen_pod()
+# at the end of $modfn
+#
+EOC
     '=head1 NAME',
-    "$class - Optional module dependency specifications",
+    "$class - Optional module dependency specifications (for module authors)",
+    '=head1 SYNOPSIS',
+    <<"EOS",
+Somewhere in your build-file (e.g. L<Module::Install>'s Makefile.PL):
+
+  ...
+
+  configure_requires 'DBIx::Class' => '$distver';
+
+  require $class;
+
+  my \$deploy_deps = $class->req_list_for ('deploy');
+
+  for (keys %\$deploy_deps) {
+    requires \$_ => \$deploy_deps->{\$_};
+  }
+
+  ...
+
+Note that there are some caveats regarding C<configure_requires()>, more info
+can be found at L<Module::Install/configure_requires>
+EOS
     '=head1 DESCRIPTION',
     <<'EOD',
 Some of the less-frequently used features of L<DBIx::Class> have external
@@ -243,13 +400,14 @@ 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.
+of such dependencies, for DBIx::Class core authors, and DBIx::Class extension
+authors alike.
 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 
+The group name can be used in the
 EOD
   );
 
@@ -274,24 +432,27 @@ EOD
 
   push @chunks, (
     '=head1 METHODS',
+    '=head2 req_group_list',
+    '=over',
+    '=item Arguments: none',
+    '=item Returns: \%list_of_requirement_groups',
+    '=back',
+    <<'EOD',
+This method should be used by DBIx::Class packagers, to get a hashref of all
+dependencies keyed by dependency group. Each key (group name) can be supplied
+to one of the group-specific methods below.
+EOD
+
     '=head2 req_list_for',
     '=over',
     '=item Arguments: $group_name',
     '=item Returns: \%list_of_module_version_pairs',
     '=back',
-    <<EOD,
+    <<'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.
+version of modules a specific feature requires in the B<current> version of
+DBIx::Class. See the L</SYNOPSIS> for a real-world
+example.
 EOD
 
     '=head2 req_ok_for',
@@ -299,23 +460,26 @@ EOD
     '=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',
+    <<'EOD',
+Returns true or false depending on whether all modules required by
+C<$group_name> are present on the system and loadable.
+EOD
 
     '=head2 req_missing_for',
     '=over',
     '=item Arguments: $group_name',
     '=item Returns: $error_message_string',
     '=back',
-    <<EOD,
+    <<"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:
+For example if some of the requirements for C<deploy> are not available,
+the returned string could look like:
 
- Moose >= 0.98, MooseX::Types >= 0.21, namespace::clean (see $class for details)
+ SQL::Translator >= $sqltver (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.
@@ -330,13 +494,13 @@ EOD
 Returns a hashref containing the actual errors that occured while attempting
 to load each module in the requirement group.
 EOD
-
+    '=head1 AUTHOR',
+    'See L<DBIx::Class/CONTRIBUTORS>.',
+    '=head1 LICENSE',
+    'You may distribute this code under the same terms as Perl itself',
   );
 
-  my $fn = __FILE__;
-  $fn =~ s/\.pm$/\.pod/;
-
-  open (my $fh, '>', $fn) or croak "Unable to write to $fn: $!";
+  open (my $fh, '>', $podfn) or croak "Unable to write to $podfn: $!";
   print $fh join ("\n\n", @chunks);
   close ($fh);
 }