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:
Objectis a global constant, so it’s defined as a constant insideObjectitself, soObject::Object::Objectis 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.
ARGVis an array of strings with all the arguments for the command.ARGFbehaves similar to aFileobject, 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 theARGVconstant). Some of theFile-likemethods 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 (likereadlinesthat 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
$DEBUGand$VERBOSEare present by default, requiring theEnglishmodule 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.$NThe 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
Englishmodule.
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.$0shows the name of the current script.
The longer-named variable aliases are added when requiring the
Englishmodule.
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 theARGFconstant.$FILENAMEis equivalent toARGF.filenameand shows the file name of the current file being read byARGF.$*(or$ARGV) is the same as theARGVconstant.$stdinreferences the current standard input IO object.$stdoutreferences the current standard output IO object.$stderrreferences the current standard error output IO object.$>references the standard input IO object used specifically for theKernel#printandKernel#printfmethods.$;/$-F(or$FS, or$FIELD_SEPARATOR) specifies the default separator forString#split, it can be changed at runtime but also defined using the-Fflag when running Ruby.$,(or$OFS, or$OUTPUT_FIELD_SEPARATOR) specifies the default separator used byString#joinand 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 arecordends in the input. This is thenewlinecharacter by default since the most common case is to read lines from a file and can be configured with the-0Ruby flag. This can be used to change how methods likeKernel#getswork 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#printorIO#writeinstead of when reading input.$_(or$LAST_READ_LINE) holds the last string that was read using theKernel.getsorKernel#readlinemethods.$Fis the array resulting of applyingString#splitto the value of$_when the$-aflag 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
Englishmodule.
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
Englishmodule.
$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
Englishmodule.
$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
Englishmodule.
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: