Octoxan
Octoxan

Reputation: 2309

Remove everything between single quotes with wildcard in Ruby gsub

Trying to understand how exactly to escape everything for this. I'm trying to essentially find and replace this instance in a file, set :repo_url, '' but there could be any number of characters in between the single quotes.

One example of a line I'd need to replace is:

set :repo_url, 'git://github.com/something.com.git'

I've tried the following already:

contents.gsub!("set :repo_url, /^\'(.*)\'$/", "set :repo_url, 'test'") 
contents.gsub!("set :repo_url, /^'(.*)'$/", "set :repo_url, 'test'")
contents.gsub!("set :repo_url, '(.*)", "set :repo_url, 'test'")

None of those work, this does but only when there's nothing between the quotes:

contents.gsub!("set :repo_url, ''", "set :repo_url, 'test'")

What am I missing? Sorry I'm still learning Ruby + Regex.

Upvotes: 1

Views: 456

Answers (2)

Todd A. Jacobs
Todd A. Jacobs

Reputation: 84413

TL;DR

There's more than one way to do what you want, including fixing quoting issues with special syntax and manipulating match variables to control the text that will be replaced.

Using the following should work:

contents.gsub!(
  /^set :repo_url[^'"]+\K(.).*?\1/,
  '\1https://example.com/foo.git\1'
)

and the way it works is explained in detail below.

A Worked Example with Output

If all you want to do is avoid quoting issues, you can use Ruby's %q() syntax for your strings to avoid having to escape quotes. However, if you are having other trouble with your match, you can create a more robust regular expression like /^set :repo_url[^'"]+\K(.).*?\1/. For example:

    contents = <<~EOF
      set :repo_url, 'git://github.com/foo/bar.git'
      set :repo_url, 'git://github.com/foo/baz.git' # a comment
      set :repo_url, "git://github.com/foo/bar.git" # using doublequotes
    EOF
    
    contents.gsub!(
      /^set :repo_url[^'"]+\K(.).*?\1/,
      '\1https://example.com/foo.git\1'
    )

   puts contents

This will print:

set :repo_url, 'https://example.com/foo.git'  
set :repo_url, 'https://example.com/foo.git' # a comment  
set :repo_url, "https://example.com/foo.git" # using doublequotes  
#=> nil

Understanding the Regex Pattern

String#gsub and related methods work by replacing the match stored in $& with replacement text, so you can do a lot of interesting things by managing what's stored (or not) in the match and associated capture groups. The regular expression pattern works as follows:

  1. ^set :repo_url

    Match the fixed string "set :repo_url" only at the beginning of each line.

  2. [^'"]+

    Match all non-quote characters.

  3. \K

    Use the undocumented "keep" flag to discard the exclude the stuff to the left from $&, preventing it from being clobbered by the replacement text.

  4. (.)

    Capture the quote character used, e.g. " or '. Stores the quote in both $& and $1.

  5. .*?\1

    Non-greedy match that adds all characters, up to and including the next matching quote character (stored in $1 and referenced by \1), into $&.

Understanding the Replacement String

The replacement string leverages the capture groups from the regular expression pattern to restore portions of the string that would otherwise get replaced by the String#gsub! method. It works like this:

  1. \1

    Add the quote character from capture group 1 to the replacement string.

  2. https://example.com/foo.git

    This is the actual text you want to replace your other URL with.

  3. \1

    Add the same quote character from capture group 1 again to the end of the replacement string.

Because everything to the left of \K and to the right of the closing quote is not stored in $& by our regular expression, we don't have to use capture groups or backreferences to include those portions of the line into our replacement string. There are certainly other ways to express the match or the replacement, but this works very well for the posted examples.

Upvotes: 1

Kache
Kache

Reputation: 16727

contents.gsub!(/^set :repo_url, '.*?'$/, "set :repo_url, 'test'")

should do what you want

Upvotes: 0

Related Questions