Exploring Ruby's Global Constants and Variables
By default, Ruby defines many constants and global variables that can be used in the code to get information about the current state of the application and the runtime. In this article we’ll go over most of them to understand what they are and what information we can set and get to simplify our scripts or to debug problems.
Global Constants
In Ruby, a constant encompasses many concepts: it can be a class, a module, or a primitive value. Global constants are actually defined as constants of the Object
class, so RUBY_PLATFORM
and Object::RUBY_PLATFORM
are equivalent.
If we execute Object.constants
we can get a list of all the global constants. This includes classes (like the Exception
class), modules (like the JSON
module), and others like RUBY_PLATFORM
or ARGV
that are primitives (a string and an array, respectively).
Useless fact:
Object
is a global constant, so it’s defined as a constant insideObject
itself, soObject::Object::Object
is equivalent toObject
.
Let’s explore some of them.
RUBY_PLATFORM, RUBY_VERSION, RUBY_*
There are many constants that we can use to know which version of the engine and which version of Ruby is executing our code. We can also check the Operating System and the architecture.
These are the values when I check them in my system:
RUBY_COPYRIGHT # "ruby - Copyright (C) 1993-2023 Yukihiro Matsumoto"
RUBY_DESCRIPTION # "ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]"
RUBY_ENGINE # "ruby"
RUBY_ENGINE_VERSION # "3.2.2"
RUBY_PATCHLEVEL # 53
RUBY_PLATFORM # "x86_64-linux"
RUBY_RELEASE_DATE # "2023-03-30"
RUBY_REVISION # "e51014f9c05aa65cbf203442d37fef7c12390015"
RUBY_VERSION # "3.2.2"
Here we can see that I’m running MRI 3.2.2 (RUBY_ENGINE
would be jruby
if using JRuby for example). In the case of MRI, RUBY_ENGINE_VERSION
and RUBY_VERSION
always match but, when using another implementation, the RUBY_VERSION
will always show the equivalent MRI version while RUBY_ENGINE_VERSION
shows the version number of that particular Ruby engine. We can also see it’s a linux OS with an x86_64 architecture for the CPU.
STDIN, STDOUT, STDERR
These constants are the defaults for the standard input, standard output, and the standard error output. These are used as the default values for the $stdin
, $stdout
, and $stderr
global variables. Trying to change them will display a warning (it will still change it though):
STDERR = my_io_object
(irb):18: warning: already initialized constant STDERR
While trying to change the global variable is the expected method, if needed:
$stderr = my_io_object
These constants are not meant to be changed to always keep a reference to the original IO.
ARGV, ARGF
These 2 constants are related to the input passed to the process.
ARGV
is an array of strings with all the arguments for the command.ARGF
behaves similar to aFile
object, but instead of referencing one file in the file system, it is a wrapper around all the files passed as arguments to the current process (or any file listed in theARGV
constant). Some of theFile-like
methods are invoked on the current file (likeclose
, that will move the reading position to the beginning of the next file), but some are invoked for the complete list of files (likereadlines
that will read the lines of all the files at once). This is the documentation for the ARGF class to see all the methods.
ENV
This constant is commonly used because of the Twelve Factor App methodology. It lists all environment variables.
DATA
The DATA
constant is only defined when executing a script (it’s not defined for IRB for example), and only if the special __END__
content is present.
This is a File
object pointing to the current script file, but positioned in the line right after __END__
.
$ cat t.rb
puts DATA.gets
__END__
hello world!
$ ruby t.rb
hello world!
In that example, DATA.gets
reads the content of the file after __END__
, which is the string "hello world!"
, and then it’s printed out.
Global Variables
Global variables always start with the $
character. New global variables can also be defined at runtime by prefixing them with $
. Ruby includes many global variables by default, some of them are read-only or meant to be modified by Ruby itself.
When working with Ractors and Threads, some of these variables are “global” but only in the context of that Ractor/Thread.
These default variables are always named with the pattern of $
followed by 1 or 2 characters, but some aliased variables are defined when requiring the English
module.
puts $$ # => current process id
puts $PID # => nil
require 'English' => # true
puts $PID # => current process id
Ruby Options
These variables are used to know if certain options were passed to the program.
The $-a
, $-p
, $-l
, $-i
, $-K
, $-F
, $-d
(alias of $DEBUG
), $-w
/$-v
(aliases of $VERBOSE
) variables are all mapped to flags that can be passed to the ruby
command listed when running ruby -h
. Most of them are either true
or false
for boolean flags, except for $-i
that is either a string with the extension name or nil
, and $-w
/$-v
that can be a boolean (for warning levels 1 and 2) or nil
(when warnings are disabled).
The longer-named variables
$DEBUG
and$VERBOSE
are present by default, requiring theEnglish
module is not needed.
Regexp Options
When working with regular expressions, after a successful match many global variables are populated with information about it.
$~
($LAST_MATCH_INFO
) The information about the last match in the current scope (thread-local and frame-local).$&
($MATCH
) The string matched by the last successful match.$`
($PREMATCH
) The string to the left of the last successful match.$'
($POSTMATCH
) The string to the right of the last successful match.$+
($LAST_PAREN_MATCH
) The highest group matched by the last successful match.$N
The Nth group of the last successful match, with N being an integer greater than 1.
The longer-named variable aliases are added when requiring the
English
module.
So, given this regexp match "Hello World!" =~ /e(llo) (\w+)rl/
, the variables are populated with this values:
puts $~ # => #<MatchData "ello Worl" 1:"llo" 2:"Wo"> (the match and each group)
puts $& # => "ello Worl" (the match)
puts $` # => "H" (the string to the left of the match)
puts $' # => "d!" (the string to the right of the match)
puts $+ # => "Wo" (the last group)
puts $1 # => "llo"
puts $2 # => "Wo
puts $3 # => nil
Process Information
These variables are useful to find information about the current process or child processes.
$$
(or$PID
, or$PROCESS_ID
) shows the current process id.$?
(or$CHILD_STATUS
) shows the exit code of the last child process.$0
shows the name of the current script.
The longer-named variable aliases are added when requiring the
English
module.
Input and Output
There are many variables related to the input and output of the process. These should be used carefully because some of this variables affect methods like Kernel#print
or String#split
.
$<
(orDEFAULT_INPUT
) is the same as theARGF
constant.$FILENAME
is equivalent toARGF.filename
and shows the file name of the current file being read byARGF
.$*
(or$ARGV
) is the same as theARGV
constant.$stdin
references the current standard input IO object.$stdout
references the current standard output IO object.$stderr
references the current standard error output IO object.$>
references the standard input IO object used specifically for theKernel#print
andKernel#printf
methods.$;
/$-F
(or$FS
, or$FIELD_SEPARATOR
) specifies the default separator forString#split
, it can be changed at runtime but also defined using the-F
flag when running Ruby.$,
(or$OFS
, or$OUTPUT_FIELD_SEPARATOR
) specifies the default separator used byString#join
and when printing an array withKernel#print
. Note that this should not be used in new code since it will be deprecated.$/
/$-0
(or$RS
, or$INPUT_RECORD_SEPARATOR
) specifies the default separator that is used to determine when arecord
ends in the input. This is thenewline
character by default since the most common case is to read lines from a file and can be configured with the-0
Ruby flag. This can be used to change how methods likeKernel#gets
work for different input formats.$\
(or$ORS
, or$OUTPUT_RECORD_SEPARATOR
), similar to the$/
variable but specifies the separator used when outputting data with methods likeKernel#print
orIO#write
instead of when reading input.$_
(or$LAST_READ_LINE
) holds the last string that was read using theKernel.gets
orKernel#readline
methods.$F
is the array resulting of applyingString#split
to the value of$_
when the$-a
flag istrue
.$.
(or$NR
, or$INPUT_LINE_NUMBER
) holds the line number of the line that was last read in$_
.
The longer-named variable aliases are added when requiring the
English
module.
Exceptions
When dealing with exceptions, there’s no need to store them in a local variable or pass them around as arguments. Ruby will populate two variables with the information of the last exception that was raised:
$!
(or$ERROR_INFO
) is a reference to the last Exception that was raised.$@
(or$ERROR_POSITION
) is the stack trace of the last exception (so it’s equivalent to$!.backtrace
).
These variables are thread-local, so an exception inside a thread will not modify the same variables in a different thread.
The longer-named variable aliases are added when requiring the
English
module.
$LOAD_PATH
This variable is really important. It specifies an array with the different paths where Ruby will look for files when using the Kernel#load
and Kernel#require
methods.
It can be modified to point Ruby in the right direction when we want to be sure it will load the script we want to. This can be also modified to add custom non-standard directories where we have important scripts.
Bundler uses this variable to ensure that we always use the same gem versions.
The variable is aliased as $:
and $-I
, and the value can me changed at runtime but also with the -I
Ruby flag.
The long-name version is defined by Ruby by default, there’s no need to include the
English
module.
$LOADED_FEATURES
Last but not least, the $LOADED_FEATURES
is an array that lists all the paths for rb
and so
files that were already loaded by Ruby.
This variable can be inspected to debug problems to check if a file we expect to be loaded is actually loaded or not, and to find out if files we don’t expect to be loaded actually are.
It’s aliased as $"
.
The long-name version is defined by Ruby by default, there’s no need to include the
English
module.
Conclusion
Ruby’s global constants and variables provide a lot of information about the process and the current state of the app. They can come in handy when creating new scripts and working with inputs, also when dealing with exceptions, and for debugging problems. The Ruby and OS information is also important when dealing with code that should work cross-platform.
Finally, the $LOADED_FEATURES
variable can help you find code that is loaded that you don’t need, to optimize the performance of the app.
Do you need help with your Ruby app? Let’s see how we can help! !
Resources: