diff --git a/lib/manager/bundler/__snapshots__/extract.spec.ts.snap b/lib/manager/bundler/__snapshots__/extract.spec.ts.snap
index 8471162943b7a1eb33e739aa6451bb9adc63777e..50ae40eb87c7e898619b6d1ecdd99274e21edea2 100644
--- a/lib/manager/bundler/__snapshots__/extract.spec.ts.snap
+++ b/lib/manager/bundler/__snapshots__/extract.spec.ts.snap
@@ -1595,7 +1595,7 @@ Object {
       "skipReason": "no-version",
     },
     Object {
-      "currentValue": ">= 3.0.5, < 3.2",
+      "currentValue": "\\">= 3.0.5\\", \\"< 3.2\\"",
       "datasource": "rubygems",
       "depName": "listen",
       "lockedVersion": "3.1.5",
diff --git a/lib/manager/bundler/extract.ts b/lib/manager/bundler/extract.ts
index 225def6ef0a24fbb18f940083da6616a4c185a2d..2d746b5e29b394c4edc90887efe81495fb903eee 100644
--- a/lib/manager/bundler/extract.ts
+++ b/lib/manager/bundler/extract.ts
@@ -53,10 +53,18 @@ export async function extractPackageFile(
         managerData: { lineNumber },
       };
       if (gemMatch[3]) {
-        dep.currentValue = gemMatch[0]
+        let currentValue = gemMatch[0]
           .substring(`gem ${gemDelimiter}${dep.depName}${gemDelimiter},`.length)
-          .replace(regEx(gemDelimiter, 'g'), '')
           .trim();
+        // strip quotes unless it's a complex constraint
+        if (
+          currentValue.startsWith(gemDelimiter) &&
+          currentValue.endsWith(gemDelimiter) &&
+          currentValue.split(gemDelimiter).length === 3
+        ) {
+          currentValue = currentValue.slice(1, -1);
+        }
+        dep.currentValue = currentValue;
       } else {
         dep.skipReason = SkipReason.NoVersion;
       }
diff --git a/lib/versioning/ruby/index.spec.ts b/lib/versioning/ruby/index.spec.ts
index 5cbe03cc4378718d7980f907fbc35542384c486a..3fadf2d0216ab9778dd2c605fad9c42b00c67589 100644
--- a/lib/versioning/ruby/index.spec.ts
+++ b/lib/versioning/ruby/index.spec.ts
@@ -366,6 +366,14 @@ describe('semverRuby', () => {
         ['1.2.3', '<= 1.0.3', 'pin', '1.0.3', '1.2.3'],
         ['1.2.3', '~> 1.0.3', 'pin', '1.0.4', '1.2.3'],
         ['4.7.8', '~> 4.7, >= 4.7.4', 'pin', '4.7.5', '4.7.8'],
+        [
+          "'>= 3.0.5', '< 3.3'",
+          "'>= 3.0.5', '< 3.2'",
+          'replace',
+          '3.1.5',
+          '3.2.1',
+        ],
+        ["'0.0.11'", "'0.0.10'", 'replace', '0.0.10', '0.0.11'],
       ].forEach(
         ([expected, currentValue, rangeStrategy, fromVersion, toVersion]) => {
           expect(
diff --git a/lib/versioning/ruby/index.ts b/lib/versioning/ruby/index.ts
index 334e27b46d0a6cb89e5e5036b7aeaafda84aa757..c0eadc29c2a0260dde7085000a252471b033ea1b 100644
--- a/lib/versioning/ruby/index.ts
+++ b/lib/versioning/ruby/index.ts
@@ -25,7 +25,7 @@ export const supportedRangeStrategies = ['bump', 'extend', 'pin', 'replace'];
 
 function vtrim<T = unknown>(version: T): string | T {
   if (typeof version === 'string') {
-    return version.replace(/^v/, '');
+    return version.replace(/^v/, '').replace(/('|")/g, '');
   }
   return version;
 }
@@ -84,28 +84,39 @@ const getNewValue = ({
   fromVersion,
   toVersion,
 }: NewValueConfig): string => {
-  let result = null;
+  let newValue = null;
   if (isVersion(currentValue)) {
-    return currentValue.startsWith('v') ? 'v' + toVersion : toVersion;
+    newValue = currentValue.startsWith('v') ? 'v' + toVersion : toVersion;
+  } else if (currentValue.replace(/^=\s*/, '') === fromVersion) {
+    newValue = currentValue.replace(fromVersion, toVersion);
+  } else {
+    switch (rangeStrategy) {
+      case 'pin':
+        newValue = pin({ to: vtrim(toVersion) });
+        break;
+      case 'bump':
+        newValue = bump({ range: vtrim(currentValue), to: vtrim(toVersion) });
+        break;
+      case 'replace':
+        newValue = replace({
+          range: vtrim(currentValue),
+          to: vtrim(toVersion),
+        });
+        break;
+      // istanbul ignore next
+      default:
+        logger.warn(`Unsupported strategy ${rangeStrategy}`);
+    }
   }
-  if (currentValue.replace(/^=\s*/, '') === fromVersion) {
-    return currentValue.replace(fromVersion, toVersion);
+  if (/^('|")/.exec(currentValue)) {
+    const delimiter = currentValue[0];
+    return newValue
+      .split(',')
+      .map((element) => element.replace(/^(\s*)/, `$1${delimiter}`))
+      .map((element) => element.replace(/(\s*)$/, `${delimiter}$1`))
+      .join(',');
   }
-  switch (rangeStrategy) {
-    case 'pin':
-      result = pin({ to: vtrim(toVersion) });
-      break;
-    case 'bump':
-      result = bump({ range: vtrim(currentValue), to: vtrim(toVersion) });
-      break;
-    case 'replace':
-      result = replace({ range: vtrim(currentValue), to: vtrim(toVersion) });
-      break;
-    // istanbul ignore next
-    default:
-      logger.warn(`Unsupported strategy ${rangeStrategy}`);
-  }
-  return result;
+  return newValue;
 };
 
 export const sortVersions = (left: string, right: string): number =>