Search code examples
rubyregexperllanguage-comparisons

What's the Ruby equivalent of Perl's s{}{}e?


I'm trying to rewrite a command to replace output files with the same files in a different directory, building up a translation map. In Perl I could do something like this:

s{(-o|--out)\s((?:\S+\/)?(\S+))}{ "$1 " . ($m{$2}="foo/$3") }eg

I'm not clear how to do the same under Ruby, because:

"-out AAA -out yeah/BBB".
gsub(/((?:\s|^)-out)\s+((?:\S+\/)?(\S+))/) { |f|
  "#{f[1]} #{m[f[2]] = "temp/#{f[3]}"}"
}

results in

"o temp/t- temp/u"

because 'm' is the matched string, not a match object, so #{m[1]} is just the second character of the matched string.

I don't want to use $1, $2 because Rubocop says they're evil, and I'd prefer not to use 'Regexp.last_match' because it's quite remarkably verbose and turns this one-liner into a do-block.

Is there no built-in that gives me the match object as the parameter?


Solution

  • Once you do not want to use $1 as you did in perl, you might use named matches:

    "-out AAA -out BBB".gsub(/(?<leading>\s|^)(?<out>-out)\s+(?<rest>\S+)/) do
      "#{$~[:leading]}#{$~[:out]} BLOCK"
    end
    #⇒ "-out BLOCK -out BLOCK"
    

    Another option would be to shut rubocop up, since you know what you are doing:

    #rubocop:disable Style/PerlBackrefs
    "-out AAA -out BBB".gsub(/(\s|^)(-out)\s+(\S+)/) do
      "#{$1}#{$2} BLOCK"
    end
    #rubocop:enable Style/PerlBackrefs