diff --git a/lib/datasource/api.ts b/lib/datasource/api.ts
index 8cf870242519c5399b91a5d7bdb4f81494108453..cdcc24f13bb13cdb7ed5a5604675600993767122 100644
--- a/lib/datasource/api.ts
+++ b/lib/datasource/api.ts
@@ -24,7 +24,7 @@ import * as packagist from './packagist';
 import * as pod from './pod';
 import * as pypi from './pypi';
 import * as repology from './repology';
-import * as rubyVersion from './ruby-version';
+import { RubyVersionDatasource } from './ruby-version';
 import * as rubygems from './rubygems';
 import * as sbtPackage from './sbt-package';
 import * as sbtPlugin from './sbt-plugin';
@@ -61,7 +61,7 @@ api.set('packagist', packagist);
 api.set('pod', pod);
 api.set('pypi', pypi);
 api.set('repology', repology);
-api.set('ruby-version', rubyVersion);
+api.set('ruby-version', new RubyVersionDatasource());
 api.set('rubygems', rubygems);
 api.set('sbt-package', sbtPackage);
 api.set('sbt-plugin', sbtPlugin);
diff --git a/lib/datasource/ruby-version/index.spec.ts b/lib/datasource/ruby-version/index.spec.ts
index 3efbd83d385eef60c8e986c089573f8b133ff21b..a4e94ce0b57941b8e00c854c5f9f8db263d06d11 100644
--- a/lib/datasource/ruby-version/index.spec.ts
+++ b/lib/datasource/ruby-version/index.spec.ts
@@ -1,10 +1,12 @@
 import { getPkgReleases } from '..';
 import * as httpMock from '../../../test/http-mock';
 import { getName, loadFixture } from '../../../test/util';
-import { id as datasource } from '.';
+import { RubyVersionDatasource } from '.';
 
 const rubyReleasesHtml = loadFixture('releases.html');
 
+const datasource = RubyVersionDatasource.id;
+
 describe(getName(), () => {
   describe('getReleases', () => {
     it('parses real data', async () => {
diff --git a/lib/datasource/ruby-version/index.ts b/lib/datasource/ruby-version/index.ts
index f225b9fbca9bec49d792fabf2e0b507704742f83..9019d63c882ad0f560cdfcd53b481969c0c49aa2 100644
--- a/lib/datasource/ruby-version/index.ts
+++ b/lib/datasource/ruby-version/index.ts
@@ -1,58 +1,63 @@
 import { ExternalHostError } from '../../types/errors/external-host-error';
-import * as packageCache from '../../util/cache/package';
+import { cache } from '../../util/cache/package/decorator';
 import { parse } from '../../util/html';
-import { Http } from '../../util/http';
+import { HttpError } from '../../util/http/types';
 import { isVersion, id as rubyVersioningId } from '../../versioning/ruby';
+import { Datasource } from '../datasource';
 import type { GetReleasesConfig, ReleaseResult } from '../types';
 
-export const id = 'ruby-version';
-export const defaultRegistryUrls = ['https://www.ruby-lang.org/'];
-export const customRegistrySupport = false;
-export const defaultVersioning = rubyVersioningId;
-
-const http = new Http(id);
-
-export async function getReleases({
-  registryUrl,
-}: GetReleasesConfig): Promise<ReleaseResult | null> {
-  // First check the persistent cache
-  const cacheNamespace = 'datasource-ruby-version';
-  const cachedResult = await packageCache.get<ReleaseResult>(
-    cacheNamespace,
-    'all'
-  );
-  // istanbul ignore if
-  if (cachedResult) {
-    return cachedResult;
+export class RubyVersionDatasource extends Datasource {
+  static readonly id = 'ruby-version';
+
+  constructor() {
+    super(RubyVersionDatasource.id);
   }
-  try {
+
+  readonly defaultRegistryUrls = ['https://www.ruby-lang.org/'];
+
+  readonly customRegistrySupport = false;
+
+  readonly defaultVersioning = rubyVersioningId;
+
+  @cache({ namespace: `datasource-${RubyVersionDatasource.id}`, key: 'all' })
+  async getReleases({
+    registryUrl,
+  }: GetReleasesConfig): Promise<ReleaseResult | null> {
     const res: ReleaseResult = {
       homepage: 'https://www.ruby-lang.org',
       sourceUrl: 'https://github.com/ruby/ruby',
       releases: [],
     };
     const rubyVersionsUrl = `${registryUrl}en/downloads/releases/`;
-    const response = await http.get(rubyVersionsUrl);
-    const root = parse(response.body);
-    const rows = root.querySelector('.release-list').querySelectorAll('tr');
-    rows.forEach((row) => {
-      const tds = row.querySelectorAll('td');
-      const columns: string[] = [];
-      tds.forEach((td) => columns.push(td.innerHTML));
-      if (columns.length) {
-        const version = columns[0].replace('Ruby ', '');
-        if (isVersion(version)) {
-          const releaseTimestamp = columns[1];
-          const changelogUrl = columns[2]
-            .replace('<a href="', 'https://www.ruby-lang.org')
-            .replace('">more...</a>', '');
-          res.releases.push({ version, releaseTimestamp, changelogUrl });
+    try {
+      const response = await this.http.get(rubyVersionsUrl);
+
+      const root = parse(response.body);
+      const rows = root.querySelector('.release-list').querySelectorAll('tr');
+      rows.forEach((row) => {
+        const tds = row.querySelectorAll('td');
+        const columns: string[] = [];
+        tds.forEach((td) => columns.push(td.innerHTML));
+        if (columns.length) {
+          const version = columns[0].replace('Ruby ', '');
+          if (isVersion(version)) {
+            const releaseTimestamp = columns[1];
+            const changelogUrl = columns[2]
+              .replace('<a href="', 'https://www.ruby-lang.org')
+              .replace('">more...</a>', '');
+            res.releases.push({ version, releaseTimestamp, changelogUrl });
+          }
         }
-      }
-    });
-    await packageCache.set(cacheNamespace, 'all', res, 15);
+      });
+    } catch (err) {
+      this.handleGenericErrors(err);
+    }
+
     return res;
-  } catch (err) {
+  }
+
+  // eslint-disable-next-line class-methods-use-this
+  override handleSpecificErrors(err: HttpError): never | void {
     throw new ExternalHostError(err);
   }
 }
diff --git a/lib/manager/ruby-version/extract.ts b/lib/manager/ruby-version/extract.ts
index 57621bce2f2e16a7891d3f1d231bfca3420579b0..b71b58660ca41d24166219eef48a291067dcb2ae 100644
--- a/lib/manager/ruby-version/extract.ts
+++ b/lib/manager/ruby-version/extract.ts
@@ -1,4 +1,4 @@
-import * as datasourceRubyVersion from '../../datasource/ruby-version';
+import { RubyVersionDatasource } from '../../datasource/ruby-version';
 import { logger } from '../../logger';
 import type { PackageDependency, PackageFile } from '../types';
 
@@ -7,7 +7,7 @@ export function extractPackageFile(content: string): PackageFile {
   const dep: PackageDependency = {
     depName: 'ruby',
     currentValue: content.trim(),
-    datasource: datasourceRubyVersion.id,
+    datasource: RubyVersionDatasource.id,
   };
   return { deps: [dep] };
 }