1 min read

Ruby Globals that are not Global

Not all $-variables are globals in Ruby
Ruby Globals that are not Global
Photo by Priscilla Du Preez / Unsplash

Ruby's regular expression class sets some $ prefixed variables, but they're not global.

Everytime you perform a Regexp match, including using the match operator =~, the following variables are populated:

  • $~ is equivalent to Regexp.last_match;
  • $& contains the complete matched text;
  • $` contains string before match;
  • $' contains string after match;
  • $1, $2 and so on contain text matching first, second, etc capture group;
  • $+ contains last capture group.

While I try and avoid arcane incantations, preferring to use a MatchData object where possible, there are genuinely useful in dealing with case statements and regular expression matching.

my_string = 'The HMS Bellerophon is a fine ship'

case my_string
when /(HMS [A-z]+)/
  puts "Ahoy there, #{$1}"
when /(USS [A-z]+/
  puts "Greetings, #{$1}"
end

The regex $- variables give me access to the result of the match in the body of the branch after the matching has taken place; very useful when you don't have a MatchData to work with.

The documentation describes these variables in a misleading way, in my opinion...

Pattern matching sets some global variables

The regex special variables are not global – they are method-local and thread-local. If you try and use them outside of the method that you performed the match, they will be nil. This is important if you delegate your matching elsewhere:

my_string = 'The HMS Bellerophon is a fine ship'

# does not work
# $1 will be local to the lambdas

case my_string
when his_majestys_navy
  puts "Ahoy there, #{$1}"
when united_states_navy
  puts "Greetings, #{$1}"
end

def his_majestys_navy
  -> (name) { /(HMS [A-z]+)/ }
end

def united_states_navy
  -> (name) { /(USS [A-z]+/ }
end

# does work

case my_string
when his_majestys_navy
  puts "Ahoy there, #{$1}"
when united_states_navy
  puts "Greetings, #{$1}"
end

def his_majestys_navy
  /(HMS [A-z]+)/
end

def united_states_navy
  /(USS [A-z]+/
end