-
Notifications
You must be signed in to change notification settings - Fork 1
Variables, Constants and Namespaces
Variables (including constants) in Ruby have the following key characteristics:
- They are not "typed" - all variables can be assigned any Ruby object
- They are not declared - they just pop into existence when they are first assigned to
- The only characteristics of variables are their name, what they are attached to, and their value at any point in time - except for constants, which are marked either public or private
- Constant accesses may be resolved through a
const_missingmethod, similar tomethod_missing - The type of a variable (as in local/instance/class/global/constant) is determined by the first 1 or 2 characters in its name, so there are no name conflicts. However, constants and local variables can have the same name as methods.
In general, a variable name is any combination of alphanumeric, _, or non-ASCII characters.
Specific rules for the beginning of variable names:
-
Constants must begin with an uppercase ASCII character
-
Local variables must begin with a legal name character other than an ASCII uppercase or digit character
-
Instance variables must begin with
@followed by a legal name character other than an ASCII digit -
Class variables must begin with
@@followed by a legal name character other than an ASCII digit -
Global variables must have one of the following forms:
-
begin with
$followed by a legal name character other than an ASCII digit -
be
$followed by an ASCII digit other than 0 followed by any number of ASCII digits (i.e. a regular expression match backreference) -
be
$-followed by a single legal name character -
be one of the following forms:
$~ $* $$ $? $! $@ $/ $\ $; $, $. $= $: $< $> $" $& $` $' $+ $0
Local variables are, as with other languages, transient variables that are created when a unit of code runs and discarded afterwards (unless a closure is formed over them).
Local variables are pretty much the only thing in all of Ruby (apart from syntax rules and similar reasonable odds and ends) that is statically resolved at compile time. Failures to resolve cause compile-time errors.
Local variable gets are resolved by searching up the dynamic variable context stack, then the local variable context, for the first context where the local variable exists. If the local variable does not exist, and Ruby syntax permits, the reference is treated as a variable-call-style method call instead and resolved at runtime.
Local variable sets (assignments) are resolved by the same search - if not found, the local variable is created in the innermost dynamic variable context, or if not available, the local variable context.
Instance variables, as with other languages, are variables attached to an object and accessible from methods executing on that object. Instance variables can never be accessed directly "outside" of an object, although APIs do exist that allow you to access them.
Instance variable gets and sets are resolved to self. If an instance variable get is performed and the variable doesn't exist, it will return the value nil and Ruby will issue a warning.
Class variables, as with other languages, are variables attached to a class (or module) and generally accessible from:
- Code defining that class, in a
classormodulestatement, including (if they were defined within such a statement) - Methods run on instances of the class
- "Static methods" run on that class
Class variable gets, both direct and API, are resolved in the following odd way:
- All possible resolutions of the class variable are identified with the following search:
- For normal, direct class variable gets, begin searching with the class variable context class
- For class variable gets on a specific class through API (i.e. the
class_variable_getmethod), begin searching with that class - If the class is a metaclass (not possible with direct gets, as class variable context is never a singleton class), move to the class that the metaclass is attached to and search that
- Move to each of the superclasses in turn and search them
- The last (i.e. highest in the inheritance tree) class variable found is returned
- If there was more than one resolution, the first class variable found is deleted from its class (because it has been overshadowed) with a warning
- If no class variable was found, an exception is raised
Class variable sets are resolved in the exact same way, except that if no class variable was found, it will be created on the first class that was searched.
Global variables, as with other languages, are global to a running Ruby program.
Global variable gets and sets are resolved straightforwardly. If a global variable set does not resolve, the global variable is created. If a global variable get does not resolve, the it will be created with value nil.
It is worth knowing that internally, to the Ruby C API, global variables are not really variables - they are implemented as callbacks to arbitrary getter and setter functions which can be implemented however the C code sees fit. Internally, the following basic types of global variables can be created:
- Value global variables: these function like regular variables. When Ruby code creates a global variable (as opposed to accessing one or setting an existing one), this is the type created.
- Reference global variables: these are also like regular variables except that instead of the value being maintained in a location controlled by the global variable mechanism, it is controlled by some other component of C code. This means that the C code can alter the value of the global variable without going through the global variable API, and such alterations can't be traced (see tracing below)
- Virtual global variables: these have arbitrary getter and setter functions, with the same tracing issue as reference global variables. A common example is where the setter function always raises an exception, thus making it a read-only global variable.
Once global variables have been created, they can't be removed except by redefining them as an alias of another global variable.
You can "trace" changes to global variables by using the trace_var method, providing callback code (a string, block or Proc) to be invoked whenever the global variable is changed. Multiple trace callbacks can be set, and they'll all be invoked when the global variable is changed, in the reverse of the order that they were set.
While a global variable's trace callbacks are in the process of being executed:
- If the global variable is changed again, trace callbacks are not reinvoked
- If any trace callbacks on the global variable are added or removed, this is deferred until all callbacks have been invoked in the current trace cycle
If you create a trace on a global variable that doesn't exist, it will be created with value nil.
Global variables can be aliased. Aliases of a single global variable share same structures, including the list of tracers.
While a global variable is being traced, you can't redefine it as an alias.
If you create an alias on a global variable that doesn't exist, it will be created with value nil.
When direct gets in Ruby code are done on the global virtual variables
Also note that backreferences such as $1 are not in any sense true global variables, they're more of a syntax shortcut. You can't create aliases of them, and the global_variables method can't report on whether they exist since there is in concept an infinite number of them (although global_variables will "pretend" by reporting the existence of $1 through to $9)
Constants have some differences to other languages. These are variables generally intended to be set once and then not changed, but this is not really enforced. They are attached to a class or module and, if public, accessible to any code that can access that class or module.
Classes and modules themselves are objects usually stored in constants, which are stored in other classes and modules. Thus arises Ruby's one and only concept of a namespace hierarchy. A reference such as ::Ont::Person::FEMALE means the constant FEMALE in the Person class/module which is a constant inside the Ont class/module which as constant inside the Object class.
There are 3 ways a constant can be referred to:
-
MYCONST- an implicitly namespaced constant reference -
<value>::MYCONST- an explicitly namespaced constant reference on the value provided. The value can be any Ruby expression but must resolve to a class or module. - Note that the
::operator used in this way is actually very similar to the.operator used to reference methods, and::can be used to call methods as well. The only difference is that.can only reference methods, whereas::will be interpreted as referencing a constant instead of a method if it's referencing an identifier that begins with an uppercase letter and is otherwise a valid constant name (but could still be a constant and/or a method), and it isn't obviously a method call (i.e. it isn't followed by arguments or a block) - ::MYCONST - an explicitly namespaced constant reference on the "root namespace" i.e. the Object class. Which Object class in what namespace? The "one true" Object class which is the common ancestor class of (almost) all other classes in Ruby.
As mentioned above, the root of the namespace hierarchy is effectively the Object class. This is a simplification because the rules for resolving constant references are a fairly complex, and Object itself has superclasses (e.g. the BasicObject class and the mixed-in Kernel module), however this simplification is generally effective because:
- The
::MYCONSTroot namespace reference, which operates on the Object class - The most fundamental classes in Ruby,
BasicObject,Object,ClassandModuleare all stored as constants on the Object class (althoughBasicObjectis also stored as a constant on itself) - Almost all other core Ruby classes are stored as constants on the Object class. When we talk about Ruby classes just by name e.g.
Array, we are generally talking about the Array class stored in Object (i.e.::Array) unless explicitly stated otherwise. - Constant references in a top context (i.e. not nested inside a class or module statement) generally resolve to the Object class
- The constant reference resolving algorithms will generally search on superclasses too, so if the Object class is searched its superclasses will be searched as well
Ruby makes some attempt to ensure that constants are really constant, and never altered. However, it doesn't really work, because the Ruby language does not have enough static declaration for proper strict constant management to be possible.
The following will produce compile time errors:
- Assigning to an explicitly namespaced constant via a complex assignment operator (e.g.
+=, or even||=) because by definition this alters an existing constant - Assigning to a constant in code inside a
defmethod definition statement (presumably because methods are expected to run multiple times and therefore constant assignments are likely to alter existing constants), unless: - That constant assignment is further nested inside a
classstatement defining a singleton class (presumably to support methods which create singleton classes on objects and assign constants to them)
Therefore you can still alter a constant:
- with a complex assignment operator as long as the constant is implicitly namespaced (this could be considered a bug)
- in a top, class statement or module statement context as long as you're not using a complex assignment operator
- inside a
defstatement as long as you further nest it in aclassstatement, or perhaps use one of theevalAPIs - with APIs that set constants
At runtime, altering a constant will cause Ruby to issue a warning.
Similar to methods, if a constant is marked private, it can't be referenced by an explicitly namespaced constant reference - it must be implicit or through APIs.
All constants are created as public, but they can be set to private afterwards.
Autoloading is a feature whereby Ruby can be instructed that if something tries to get the value of a some particular constant which doesn't exist, on some particular class/module, Ruby is to load and run some particular file (either a Ruby source file or a Ruby C extension) in the expectation that this file will set the value of the constant.
When a constant resolving algorithm locates on some class/module an eligible constant which doesn't currently exist, but is marked for autoloading, the following will happen:
- It will determine that the constant in fact doesn't exist on this class, and continue searching, if:
- The associated file was already loaded successfully, according to C APIs associated with
requireand$LOADED_FEATURES(i.e. the file did not raise up an exception while loading) - if the file was loaded successfully, and the constant still doesn't exist, the autoload obviously didn't succeed in assigning the constant - The associated file is currently in the process of being loaded, and the code which has attempted to access the constant is in the same Ruby sourcefile as the one being loaded - so the file has a circular reference to the constant it is meant to be creating
- Otherwise, it will attempt to load the file via the C API equivalent of the
requiremethod. Note that this means if the file is already in the process of being loaded (e.g. on another thread), it will wait for the file to complete loading before proceeding - It will then check again to see if the constant now exists. If it still doesn't exist (and is still therefore marked for autoloading), it will give up and continue searching for the constant elsewhere
Once an autoload flag has been set, it is only removed if the constant is assigned to, or the constant is removed (via API).
TODO - clarify this
Similar to method_missing, constant gets, if they can't be resolved directly (including via autoloading), will invoke the const_missing method on the target class. const_missing is provided 1 argument, which is the constant's name in symbol form, and is expected to return the constant's value.
The default implementation of const_missing is to raise a NameError exception.
Note that for const_missing there is no analog to the respond_to? mechanisms associated with method_missing, which a class is expected to implement in order to declare whether it implements methods via method_missing. Also, const_missing has no bearing on constant sets.
For a constant set, Ruby will set the constant on the target (explicitly provided) class/module.
For a constant get, Ruby will search the target class/module and then each of its superclasses in turn for the constant requested.
- If it is found but marked private, an exception is raised
- If it is found but marked for autoload, autoloading will be tried
- If the constant is not found anywhere, Ruby will call
const_missingon the target class/module and return its value.
For a constant set, Ruby will set the constant on the constant base context class/module.
For a constant get, Ruby will search for the constant in the following classes/modules (in order):
- Each class/module in the class reference stack which is eligible to be a constant base, except for the top class on the stack (which is Object), without searching superclasses (from the current constant base context upwards)
- A target class/module is selected for further searching - whichever is the innermost class/module on the class reference stack of the following categories.
- The constant base, or
- If
nilis pushed on by runninginstance_evalon a string (as opposed to a block), which happens wheninstance_evalis executed with an immediate object as self, then the class ofself - The target class/module will be searched, including each of its superclasses in turn
- If it is a module, it will be treated as having an implicit superclass of Object
- If the constant is still not found, Ruby will call
const_missingon the target class/module and return its value
If at any point in this search a constant is found which is marked for autoload, autoloading will be tried.
When defining using a class or module statement, the class/module is specified using the same constant reference syntax as with all other constant references. However, the resolving rules are different - almost the same as a constant set except for a provision allowing constants on superclasses of Objects to be treated as though they are defined on Object.
If the class/module is specified using <value>::MyClass syntax, the portion is resolved using normal rules before proceeding.
Explicitly-namespaced and implicitly-namespaced references are resolved the same way, with the exception that:
- Explicit references can only access public constants
- For explicit references the target class/module is the value provided, and for implicit references it is the constant base context
Ruby will search as follows:
- It will test whether the class/module is already "defined" (see defined logic below), as either public or private (including for explicitly named references), on the following locations, without searching in superclasses/class reference stack/etc:
- The target class
- If not found, and the target class is the Object class, each of its superclasses in turn
- If found, it's private, and the class was explicitly namespaced, it will raise an exception
- If found and its marked for autoload, autoload will be tried. If autoload fails to set the constant, it will invoke
const_missingmethod and use its returned value (if an exception is raised here it will not be caught). - If found and the object is not of appropriate type (i.e. class or module), it will raise an exception
- If not found, it will create a new class/module object and set it as a constant on the target class/module.
Note that if a superclass is declared in a class statement, it is a normal value and resolved using normal rules.
There is a mechanism for querying whether a constant is defined or not, which works slightly differently from a constant get. This is used by, for example:
- The
class/modulestatement rules above - The
defined?statement - The
const_defined?API method
The query returns true if the constant is defined and false if it is not. The search procedure is the same as for a constant get except:
- If querying for public only, and a private constant is found, it will return
false - If a constant is eligible for autoload, it will return
trueunless its associated file has already been successfully loaded, in which case it'll returnfalse. It will not attempt the autoload. - It will not invoke
const_missing
Kernel#instance_variables
Returns an array of instance variable names in Symbol form that are defined on self.
Kernel#instance_variable_get <name>
Returns the value of the named instance variable (represented in String or Symbol form, e.g. :@ivar) from self.
Kernel#instance_variable_set <name>, <value>
Assigns the provided value to the named instance variable (represented in String or Symbol form, e.g. :@ivar) on self and returns the assigned value.
Kernel#instance_variable_defined? <name>
Returns true if the named instance variable (represented in String or Symbol form, e.g. :@ivar) is defined on self, otherwise false.
private Kernel#remove_instance_variable <name>
Remove the named instance variable (represented in String or Symbol form, e.g. :@ivar) from self, returning its value.
Module#class_variables
Returns an array of class variable names in Symbol form that are defined on self.
Module#class_variable_get <name>
Returns the value of the named class variable (represented in String or Symbol form, e.g. :@@cvar) from self or one of its associated classes, as per the class variable get protocol.
Module#class_variable_set <name>, <value>
Assigns the provided value to the named class variable (represented in String or Symbol form, e.g. :@@cvar) on self or one of its associated classes, as per the class variable set protocol, and returns the assigned value.
Module#class_variable_defined? <name>
Returns true if the named class variable (represented in String or Symbol form, e.g. :@@cvar) is defined on self or one of its associated classes, as per the class variable get protocol, otherwise false.
Note that unlike class variable gets, this will not remove any class variables from any intermediary classes.
Module#remove_class_variable <name>
Remove the named class variable (represented in String or Symbol form, e.g. :@@cvar) from self, returning its value.
Kernel::global_variables
private Kernel#global_variables
Returns an array of defined global variable names in Symbol form, in addition to the symbols :$1 through :$9.
Kernel::trace_var
private Kernel#trace_var
rb_f_trace_var
Kernel::untrace_var
private Kernel#untrace_var
rb_f_untrace_var
Module#constants [ <include_super> ]
<include_super> is a true/false value specifying whether to include constants present in superclasses, and defaults to true if not provided.
Returns an array of public constant names in Symbol form defined on self (if <include_super> is false) or defined on self or one of its superclasses, stopping the search at the Object class unless Object is self (if <include_super> is true).
Module::constants
If this is invoked with arguments, or not invoked on the Module class, then it performs the same function as Module#constants.
Otherwise, it returns (more or less) an array of public constants accessible by implicitly namespaced reference from the current context - specifically, all the constants defined on the constant base context class or its superclasses, in addition to all constants defined directly on other classes that are part of the class reference stack and eligible to be constant base contexts.
Module#const_get
rb_mod_const_get
Module#const_set
rb_mod_const_set
Module#const_defined?
rb_mod_const_defined
private Module#remove_const
rb_mod_remove_const
Module#const_missing
rb_mod_const_missing
Module#public_constant
rb_mod_public_constant
Module#private_constant
rb_mod_private_constant
A Ruby Language Reference
Copyright © by Michael Hore, 2016.
Introduction to This Document
Ruby Elements
- Classes and Modules
- Methods
- Blocks, Procs and Lambdas
- Execution Context and Closures
- Variables, Constants and Namespaces
- Types and Literals
- Ruby Expressions
- Operators
Syntax Grammar
Exceptions and Throw
Ruby Sourcefiles and Libraries
Multi Threading
Execution and Lifecycle