This document describes various Ruby language behaviors, compiled from observations in code analysis and testing.
- Namespace Qualification
- Lexical Scoping and Constant Resolution
- Definitions vs Declarations
- Method Parameters
- Method Aliasing
- Attribute Methods
- Variable Scoping
- Mixins
- Visibility
- Constant References
- Constant Aliases
- Singleton Classes
- Anonymous Classes And Modules
Ruby uses :: as the namespace separator and supports various ways of referencing namespaced entities.
The same unqualified name refers to different entities depending on context:
class Bar # Top-level Bar
end
module Foo
class Bar # Foo::Bar
end
puts Bar # Inside Foo, "Bar" means "Foo::Bar"
end
puts Bar # Outside Foo, "Bar" means "Bar" (top-level)Unqualified names are simple identifiers:
FooBarString
Qualified names include namespace separators:
Foo::BarActiveRecord::BaseRails::Application::Configuration
Top-level references start with :::
::Foo(explicitly top-level)::Array(explicitly top-level)
Rooted qualified names combine both (starting with :: and including namespace separators):
::ActiveRecord::Base(explicitly top-level ActiveRecord, then Base within it)::Rails::Application::Configuration(navigates from top level through the namespace chain)
Singleton methods (class methods) can use the same name as instance methods:
class Foo
def bar; end # Instance method Foo#bar
def self.bar; end # Singleton method Foo.bar (or Foo::bar)
endBoth methods can coexist because they operate on different receivers:
Foo.new.barcalls the instance methodFoo.barcalls the singleton method
Ruby resolves names according to the current scope:
class Bar; end
class Qux; end
module Foo
class Bar; end # Defines Foo::Bar
# "Bar" => "Foo::Bar"
# "::Bar" => "Bar" (top-level)
class Baz
class Qux; end # Defines Foo::Baz::Qux
# "Bar" => "Foo::Bar"
# "Qux" => "Foo::Baz::Qux"
# "::Qux" => "Qux" (top-level)
end
endRules:
- If the name starts with
::, it refers to a top-level constant - Otherwise, the name is qualified relative to the current namespace
- At the top level, unqualified names remain unqualified
Ruby's top-level constant resolution operator :: resets the namespace to the root, but lexical nesting is still preserved for constant lookup. This is a critical and subtle behavior.
Example:
module Foo
CONST = 42
class ::Bar
CONST # resolves to Foo::CONST because we're inside the `Foo` namespace
end
end
class Bar
CONST # NameError because this `Bar` is not lexically inside `Foo`
endKey Points:
- The class
::Bardefined insideFoohas the fully qualified nameBar(notFoo::Bar) - However, it is still lexically connected to
Foofor constant resolution - The second
Bardefinition (outsideFoo) has no lexical connection toFoo, therefore needs to accessCONSTviaFoo::CONST
Ruby supports compact namespace notation like class Foo::Bar::Baz, which creates multiple nesting levels in one declaration.
Example:
class Foo::Bar
class Baz::Qux; end
class ::Quuux; end
endThis creates:
Foo::BarFoo::Bar::Baz::Qux(nested underFoo::Bar)Quuux(top-level, but lexically insideFoo::Bar)
Important: Foo::Bar::Baz::Qux::Quuux does not exist because ::Quuux resets to top level.
A fundamental concept in Ruby is that classes and modules can be reopened and defined multiple times.
Example:
module Foo
class Bar; end
end
class Foo::Bar; endThis code creates:
3 separate definitions:
- Module definition for
Foo - Class definition for
Foo::Bar(insideFoo) - Class definition for
Foo::Bar(at top level)
2 distinct declarations:
- The module
Foo - The class
Foo::Bar
Key Points:
- A definition is a single definition occurrence of a class, module, method, etc. in a specific file at a specific location
- A declaration represents the named entity that may have multiple definitions
Ruby has a rich and expressive parameter system with multiple parameter types.
def method_name(
a, # Required positional parameter
b = 42, # Optional positional parameter (with default)
*c, # Rest positional parameter (splat)
d, # Post parameter (required positional after rest)
e:, # Required keyword parameter
f: 42, # Optional keyword parameter (with default)
**g, # Rest keyword parameter (double splat)
&h # Block parameter
)
endComplete Example:
def foo(a, b = 42, *c, d, e:, g: 42, **i, &j); endThis demonstrates:
a: required positional parameterb = 42: optional positional parameter with default value*c: rest positional (captures remaining positional args as an array)d: post parameter (required positional after rest)e:: required keyword parameterg: 42: optional keyword parameter with default value**i: rest keyword (captures remaining keyword args as a hash)&j: block parameter
def foo(...) # Forwards all arguments to bar
bar(...)
endThis captures and forwards all positional, keyword, and block arguments.
Methods can be defined on individual objects or as class methods:
class Foo
def bar; end # Instance method
def self.baz; end # Singleton method (class method)
endResults in:
Foo#bar- instance methodFoo.bazorFoo::baz- singleton method
Ruby provides two ways to create method aliases: the alias keyword and the alias_method method.
The alias keyword creates a new name for an existing method:
class Foo
def bar
"bar"
end
alias baz bar # baz is now an alias for bar
end
Foo.new.baz # => "bar"Aliases can use symbols or bare method names:
class Foo
def original; end
alias :new_name :original # symbol syntax
alias another_name original # bare name syntax
endalias_method is a method from Module that creates aliases at runtime:
class Foo
def bar; end
alias_method :baz, :bar # symbol syntax
alias_method "qux", "bar" # string syntax
endUnlike alias, alias_method requires symbols or strings, not bare method names.
Aliases can be defined at the top level but it only works with the alias keyword:
def foo; end
alias bar foo # Creates top-level method alias
alias_method :bar, :foo # NoMethodErrorThe alias keyword can also create aliases for global variables:
$foo = 123
alias $bar $foo # $bar is now an alias for $foo
$bar # => 123
$bar = 456
$foo # => 456 (they share the same value)This is different from simply assigning $bar = $foo, which would copy the value rather than create an alias.
Ruby provides special methods for creating getter and setter methods automatically.
Important: Attribute methods (attr, attr_accessor, attr_reader, attr_writer) can only be used inside class or module definitions. They are not allowed at the top level.
class Foo
attr_accessor :foo # Creates both `foo` and `foo=` methods
attr_reader :bar # Creates only `bar` method (getter)
attr_writer :baz # Creates only `baz=` method (setter)
end
# NOT allowed:
# attr_accessor :top_level # Error: undefined method `attr_accessor'All attribute methods accept multiple symbols:
class Foo
attr_accessor :bar, :baz
endCreates: bar, bar=, baz, baz=
Attribute methods only work when called on self (implicit or explicit):
class Foo
attr_accessor :foo # Works (implicit self)
self.attr_accessor :qux # Works (explicit self)
foo.attr_accessor :ignored # Does NOT work (different receiver)
endThe attr method has special behavior based on its arguments.
Rules:
-
No second parameter: Creates only reader
class Foo attr :foo # Creates only foo (getter) end
-
Second parameter is
false: Creates only readerclass Foo attr :foo, false # Creates only foo (getter) end
-
Second parameter is
true: Creates both reader and writerclass Foo attr :foo, true # Creates both foo and foo= (getter and setter) end
-
Multiple symbols/strings without second parameter: Creates only readers for all arguments
class Foo attr :foo, :bar, :baz # Creates only: foo, bar, baz (readers only) attr :a, "b", "c" # Creates only: a, b, c (readers only) end
-
Invalid second parameter: Raises runtime error
class Foo attr :foo, 123 # Raises `123 is not a symbol nor a string (TypeError)` end
Ruby has three types of variables distinguished by sigils: global ($), instance (@), and class (@@).
Global variables are not namespaced and exist globally regardless of where they're defined:
$foo = 1
class Foo
$bar = 2
endBoth $foo and $bar are accessible globally. The class definition doesn't affect the scope of $bar.
Instance variables are scoped to instances of classes. When defined at the class level, they belong to the class itself (as the class is an object):
@foo = 1 # Top-level instance variable (belongs to <main>)
class Foo
@bar = 2 # Class instance variable (belongs to Foo object)
def initialize
@baz = 3 # Instance variable (belongs to instances of Foo)
end
endClass variables are shared across a class and all its instances:
class Foo
@@bar = 2 # Class variable for Foo
def self.bar
@@bar
end
def bar
@@bar # Same @@bar accessible in instance methods
end
end
# NOT allowed:
# @@foo = 1 # Error: class variable access from toplevelAll variable types support multi-assignment:
# Constants
FOO, BAR::BAZ = 1, 2
# Global variables
$foo, $bar, $baz = 1, 2, 3
# Instance variables
@foo, @bar = 1, 2
# Class variables
@@foo, @@bar = 1, 2
# Mixed with top-level references
FOO, BAR::BAZ, ::BAZ = 3, 4, 5A critical difference between instance variables (@) and class variables (@@) is how they determine ownership when defined inside a method with an explicit receiver:
- Instance variables follow the receiver (
selfat runtime) - Class variables follow lexical scope (where the code is written)
class Foo; end
class Bar
# This defines a singleton method on Foo, but lexically inside Bar
def Foo.demo
@ivar = "instance var" # Belongs to <Foo> (the receiver)
@@cvar = "class var" # Belongs to Bar (lexical scope)
end
end
Foo.demo
Foo.instance_variables # => [:@ivar]
Bar.instance_variables # => []
Foo.class_variables # => []
Bar.class_variables # => [:@@cvar]This behavior has important implications for static analysis:
| Variable Type | Scoping Rule | In def Foo.demo inside class Bar |
|---|---|---|
@ivar |
Receiver | Belongs to Foo |
@@cvar |
Lexical | Belongs to Bar |
The same applies to class << Foo blocks defined inside another class:
class Foo; end
class Bar
class << Foo
def another_demo
@ivar2 = 1 # Belongs to Foo (receiver is Foo's singleton)
@@cvar2 = 2 # Belongs to Bar (lexical scope)
end
end
end
Foo.another_demo
Foo.instance_variables # => [:@ivar2]
Bar.class_variables # => [:@@cvar2]Ruby provides three ways to mix modules into classes or other modules: include, prepend, and extend.
include inserts the module into the ancestor chain after the class, making the module's constants, methods, and module variables available:
module Foo; end
class Bar
include Foo
end
Bar.ancestors # => [Bar, Foo, Object, Kernel, BasicObject]prepend inserts the module into the ancestor chain before the class:
module Foo; end
class Bar
prepend Foo
end
Bar.ancestors # => [Foo, Bar, Object, Kernel, BasicObject]extend adds module methods as singleton methods (class methods when used in a class):
module Foo
def bar; end
end
class Baz
extend Foo
end
Baz.bar # Works (class method)
Baz.new.bar # NoMethodError (not an instance method)A common pattern is extend self to make module methods callable on the module itself:
module Foo
extend self
def bar
"bar"
end
end
Foo.bar # => "bar"extend Foo is functionally equivalent to class << self; include Foo; end - both add methods to the singleton class. However, they trigger different hooks:
module Foo
def self.included(base)
puts "included hook called on #{base}"
end
def self.extended(base)
puts "extended hook called on #{base}"
end
end
class UsingExtend
extend Foo # Triggers: "extended hook called on UsingExtend"
end
class UsingSingletonInclude
class << self
include Foo # Triggers: "included hook called on #<Class:UsingSingletonInclude>"
end
endBoth classes get the same methods, but:
extendcalls theextendedhookincludein singleton class calls theincludedhook
Multiple modules can be mixed in a single call:
class Foo
include Bar, Baz # Baz is included first, then Bar
prepend Qux, Quux # Quux is prepended first, then Qux
endRuby removes duplicate modules from the ancestor chain:
module A; end
module B
include A
end
class Foo
include A
include B
end
Foo.ancestors # => [Foo, B, A, Object, ...]
# A appears only once, not twiceIndirect duplicates are also removed:
module A; end
module B
include A
end
module C
include A
end
module Foo
include B
include C
end
Foo.ancestors # => [Foo, C, B, A]
# A appears only once despite being in both B and CWhen both parent and child include the same module, Ruby deduplicates it - the module appears once in the ancestor chain:
module A; end
module B
include A
end
class Parent
include B
end
class Child < Parent
include B
end
Child.ancestors # => [Child, Parent, B, A, Object, ...]
# B and A appear once (child's include is deduplicated against parent)Mixins at the top level affect Object:
include Foo # Makes Foo's methods available everywhereNote: include self, prepend self, or extend self at the top level is invalid and will produce an error:
include self # => TypeError: wrong argument type Object (expected Module)
prepend self # => NoMethodError: undefined method 'prepend' for main
extend self # => TypeError: wrong argument type Object (expected Module)include self and prepend self inside a module are rejected as cyclic:
module Foo
include self # => ArgumentError: cyclic include detected
end
module Bar
prepend self # => ArgumentError: cyclic prepend detected
endextend self inside a module is valid (see Extend Self Pattern above).
Ruby provides three visibility levels for methods: public, private, and protected.
Methods defined at the top level default to private:
def foo; end # private by default at top level
public
def bar; end # now public (after public modifier)Methods defined inside a class or module default to public:
class Foo
def bar; end # public by default
endVisibility can be changed using public, private, or protected inside class and module bodies. At the top level, only public and private are available; calling protected on main raises NameError.
class Foo
def m1; end # public (default)
private
def m2; end # private (after modifier)
protected def m3; end # protected (inline modifier)
endpublic: No restrictions — callable from anywhere with any receiver.private: A private method defined anywhere in the ancestor chain can be called onself— either implicitly or explicitly asself.method. Calling it on any other object, even an instance of the same class, raisesNoMethodError.protected: Likeprivate, but also allows calls on other objects as long as bothselfand the receiver have the defining class or module in their ancestor chain.
The key distinction between private and protected is cross-instance access:
class Account
def initialize(balance)
@balance = balance
end
def >(other)
balance > other.balance # works — other is also an Account
end
protected
def balance; @balance; end
end
a = Account.new(100)
b = Account.new(50)
a > b # => true (cross-instance protected call succeeds)With private, the same pattern fails because other.balance uses an explicit receiver that isn't self:
class Wallet
def initialize(amount)
@amount = amount
end
def >(other)
amount > other.amount # NoMethodError — private method called on other
end
private
def amount; @amount; end
endProtected access extends to subclasses:
class SavingsAccount < Account
def compare(other)
balance > other.balance # works — both are in the Account hierarchy
end
endAn unrelated class cannot call protected methods, even with a reference to the object:
class Auditor
def inspect_balance(account)
account.balance # NoMethodError — Auditor is not in Account's hierarchy
end
endProtected access also works between unrelated classes that share a module in their ancestors:
module HasBalance
protected
def balance; @balance; end
end
class Account
include HasBalance
def initialize(balance) = @balance = balance
def >(other) = balance > other.balance
end
class Wallet
include HasBalance
def initialize(balance) = @balance = balance
def >(other) = balance > other.balance
end
Account.new(100) > Account.new(50) # => true (same class)
Wallet.new(100) > Account.new(50) # => true (unrelated classes, but both include HasBalance)Account and Wallet share no inheritance relationship, but both include HasBalance. Since the defining module is in both ancestor chains, protected cross-instance calls work between them.
Important: Visibility modifiers do NOT propagate into nested class or module definitions:
private
class Foo
def m1; end # public (resets in new scope)
private
module Bar
def m2; end # public (resets again)
end
def m3; end # private (still in Foo's private scope)
endVisibility modifiers have no effect on singleton methods defined with def self.method:
class Foo
private
def self.bar; end # STILL PUBLIC (visibility ignored)
# private_class_method :bar can turn `bar` private
end
Foo.bar # Works fineImportant: This does NOT apply inside class << self blocks - visibility works normally there:
class Foo
class << self
private
def bar; end # Actually private
end
end
Foo.bar # private method 'bar' called for class Foo (NoMethodError)Visibility can be changed retroactively by passing a method name as a symbol to private, protected, or public:
class Foo
def foo; end
def bar; end
private :foo # retroactively makes foo private
end
Foo.public_instance_methods(false) # => [:bar]
Foo.private_instance_methods(false) # => [:foo]- Accepts multiple symbols:
private :a, :b - Returns the symbol(s):
private :fooreturns:foo,private :a, :breturns[:a, :b] protected :fooandpublic :foowork the same way
private is a private method on Module:
Foo.private(:foo) # => NoMethodError: private method 'private' called for class Foo
Foo.send(:private, :foo) # works (bypasses visibility check)Unlike private_constant and private_class_method which are public methods, private/protected/public cannot be called with an explicit receiver from outside.
Works across reopened classes:
class Foo; def foo; end; end
class Foo; private :foo; end # works — retroactive across reopen
Foo.private_instance_methods(false) # => [:foo]private :inherited_method creates an implicit copy:
class Parent
def inherited_method; "parent"; end
end
class Child < Parent
private :inherited_method
end
Parent.new.respond_to?(:inherited_method) # => true (parent unaffected)
Child.new.respond_to?(:inherited_method) # => false (private on child)
Child.instance_method(:inherited_method).owner # => Child (NOT Parent!)Ruby creates an implicit method entry on the child class. The owner is Child, not Parent. This means private :inherited_method effectively creates a definition, not just modifying one. The parent is unaffected.
private_class_method makes singleton methods (class methods) private:
class Foo
def self.hidden; end
private_class_method :hidden
end
Foo.hidden # => NoMethodError: private method 'hidden' called for class FooInline form:
class Foo
private_class_method def self.secret; "secret"; end
end
Foo.secret # => NoMethodErrorCommon pattern — private_class_method :new:
class Singleton
private_class_method :new
def self.create; new; end # internal access works
end
Singleton.new # => NoMethodError
Singleton.create # => #<Singleton:0x...>public_class_method reverses private_class_method. Both are public methods on Module, callable from anywhere with a receiver.
Note: private_singleton_method does not exist in Ruby. The correct API is private_class_method.
module_function can be called with a method name to retroactively apply the module_function behavior, but only inside module bodies (or via send on a module). It is not available at the top level or inside class bodies:
module Foo
def foo; "foo"; end
module_function :foo
end
Foo.foo # => "foo" (public singleton)
Foo.public_instance_methods(false) # => []
Foo.private_instance_methods(false) # => [:foo] (instance becomes private)
Foo.singleton_methods(false) # => [:foo]Creates a copy, not a reference:
module Foo
def foo; "v1"; end
module_function :foo
def foo; "v2"; end # redefines instance method
end
Foo.foo # => "v1" (singleton is an independent copy of v1)Ruby provides private_constant and public_constant to control constant visibility.
class Foo
CONST = 1
private_constant :CONST
def use_const; CONST; end # works — internal access OK
end
Foo::CONST # => NameError: private constant Foo::CONST referenced
Foo.constants # => [] (private constants hidden from .constants)- Accepts symbols (
:CONST) and strings ("CONST") - Accepts multiple args:
private_constant :A, :B - Works on classes AND modules, including nested classes:
private_constant :Inner Foo.constantsexcludes private constantsFoo.const_defined?(:PRIV)returns true even for private constants — visibility doesn't affectconst_defined?Foo.const_get(:PRIV)bypasses private constant visibility and returns the value — only the::operator enforcesprivate_constant
Reverses private_constant:
class Foo
CONST = 1
private_constant :CONST
public_constant :CONST
end
Foo::CONST # => 1 (accessible again)private_constant is a public method on Module — callable from anywhere with a receiver. It targets the receiver, not the lexical scope:
module Bar
CONST = 1
class Foo
CONST = 2
private_constant(:CONST) # targets Foo::CONST (self is Foo)
Bar.private_constant(:CONST) # targets Bar::CONST (explicit receiver)
end
end
Bar::CONST # => NameError (blocked by explicit receiver call)
Bar::Foo::CONST # => NameError (blocked by bare call inside Foo)private_constant :FOO # => NoMethodError: undefined method 'private_constant' for main
public_constant :FOO # => NoMethodError: undefined method 'public_constant' for mainself at top level is main (an Object instance), not a Module, so neither API is available there.
Private constants block external access but NOT internal or inherited access:
class Parent
CONST = 1
private_constant :CONST
def use_const; CONST; end
end
class Child < Parent
def try_const; CONST; end
end
Parent.new.use_const # => 1 (internal access works)
Child.new.try_const # => 1 (inherited access through method works)
Child::CONST # => NameError (direct external access blocked)Private constants from included modules are also blocked externally:
module Mod
CONST = 1
private_constant :CONST
end
class Foo; include Mod; end
Foo::CONST # => NameErrorThe private/protected visibility modifiers have no effect on constants. Only private_constant controls constant visibility:
class Foo
private
CONST = 42
end
Foo::CONST # => 42 (still public)
Foo.constants # => [:CONST]Both alias and alias_method copy the source method's visibility at the time of aliasing, regardless of the current visibility stack:
class Foo
def pub; end # public
private
alias copy pub # copy is PUBLIC (copies source, ignores stack)
end
Foo.public_instance_methods(false) # => [:copy, :pub]
Foo.private_instance_methods(false) # => []class Foo
def foo; end
private
alias_method :bar, :foo # bar is PUBLIC (same behavior as alias)
end
Foo.public_instance_methods(false) # => [:bar, :foo]
Foo.private_instance_methods(false) # => []Aliases don't track later visibility changes:
class Foo
def foo; end
alias bar foo # bar is public (same as foo at this point)
private :foo # foo becomes private
end
Foo.public_instance_methods(false) # => [:bar]
Foo.private_instance_methods(false) # => [:foo]Constants in Ruby can be referenced before they're defined, and resolution depends on lexical scope.
A constant reference may not immediately resolve to a definition:
module Foo
BAR # Could be Foo::BAR or top-level BAR - depends on what exists
endResolution depends on:
- What constants are defined
- The lexical scope chain
- Whether it's a top-level reference (
::BAR)
Constants prefixed with :: are resolved by searching the ancestors of Object. This means they can find constants defined in Kernel or other modules included into Object, not just constants owned by Object directly:
module Kernel
FOO = 1
end
module Foo
::FOO # Found through Object's ancestors (Kernel)
::Bar # Always defined through the ancestors of `Object`
Baz # Could be defined in `Foo` or in the ancestors of `Object`
endThis works even inside classes that don't inherit from Object:
module Kernel
FOO = 1
end
class Bar < BasicObject
::FOO # Resolves through Object's ancestors, not through Bar's
endmodule Foo
class Bar
::Bar # Resolves to top-level Bar (if it exists)
::Baz # Resolves to top-level Baz (if it exists)
String # Searches: Foo::Bar::String, Foo::String, ::String
::Object # Resolves to top-level Object
end
end
class Bar
::Foo::Bar # Explicit path to Foo::Bar
endLexical scope resolution for unqualified constants (like String above):
- Current namespace (Foo::Bar::String)
- Each enclosing lexical scope (Foo::String)
After lexical scopes are exhausted, Ruby searches the ancestor chain. See Constant Resolution Through Inheritance for the full algorithm.
Ruby builds ancestors through the following steps recursively:
- Prepended modules (in reverse order of prepending)
- Current class
- Included modules (in reverse order of inclusion)
- Parent class
And then it continues up the inheritance chain.
A class without an explicit superclass inherits from Object. Exceptions are:
Objectitself inherits fromBasicObjectBasicObjectdoes NOT have any superclass.
class Foo; end
Foo.superclass # => Object
Object.superclass # => BasicObject
BasicObject.superclass # => nil
Foo.ancestors # => [Foo, Object, Kernel, BasicObject]
Object.ancestors # => [Object, Kernel, BasicObject]
BasicObject.ancestors # => [BasicObject]Singleton classes use a separate root rule: BasicObject.singleton_class.superclass is Class.
module PrependedInPrepended
end
module Prepended
prepend PrependedInPrepended
end
module PrependedInIncluded
prepend PrependedInPrepended
end
module IncludedInIncluded
end
module Included
include IncludedInIncluded
end
class Foo
end
class Bar < Foo
prepend Prepended
include Included
end
puts Bar.ancestors.to_s
#=> [PrependedInPrepended, Prepended, Bar, Included, IncludedInIncluded, Foo, Object, Kernel, BasicObject]Ruby also searches the ancestor chain when resolving constants:
class Parent
CONST = "from parent"
end
class Child < Parent
def show
CONST # Resolves to Parent::CONST through inheritance
end
end
Child.new.show # => "from parent"Resolution order:
- Lexical scopes (current namespace, then each enclosing scope)
- Ancestor chain of the innermost enclosing class or module
- For modules only: ancestors of
Object
There is no separate "top-level fallback" for classes. Top-level constants like String are owned by Object, which appears in the ancestor chain of most classes. This can be confirmed because a class inheriting from BasicObject (which does not have Object in its ancestors) cannot resolve top-level constants:
class Foo < BasicObject
String # NameError: Foo doesn't have Object in its ancestor chain
endWhen using constants at the top level of a script, they resolve because <main> is an instance of Object:
String # Works because <main> is Object. Resolved through inheritanceThe module fallback searches the full ancestor chain of Object, not just Object directly:
module Kernel
FOO = 1
end
module Bar
FOO # Resolves through Object's ancestors (Kernel)
endExample with modules:
module M
CONST = "from module"
end
class Parent
CONST = "from parent"
end
class Child < Parent
include M
def show
CONST # Resolves to M::CONST (included module takes precedence)
end
endRuby supports qualified constant paths:
module Foo
class Bar
Object::String # Explicit path: Object must exist, then String within it
end
endConstant paths are resolved left-to-right: Object is resolved first, then String is looked up within Object.
Constants can be assigned to reference other constants, creating aliases:
module Foo
CONST = 123
end
ALIAS = Foo
ALIAS::CONST # Resolves to Foo::CONST (123)Aliases can reference other aliases, forming chains:
module Foo
CONST = 123
end
ALIAS1 = Foo
ALIAS2 = ALIAS1
ALIAS2::CONST # Resolves through ALIAS2 -> ALIAS1 -> Foo -> Foo::CONSTAliases can be defined within namespaces:
module Foo
CONST = 1
end
module Bar
MyFoo = Foo # Bar::MyFoo is an alias for Foo
end
Bar::MyFoo::CONST # Resolves to Foo::CONSTAliases can also be assigned using qualified paths:
module Foo; end
module Bar; end
Bar::ALIAS = Foo # Creates Bar::ALIAS pointing to FooThe ||= operator can create conditional aliases:
ALIAS ||= Foo # Only assigns if ALIAS is not already definedRuby allows defining classes or modules under aliases. The defined class/module will have its fully qualified name based on the alias target, not the alias itself. For example:
class Foo; end
ALIAS = Foo
class ALIAS::Bar; end
ALIAS::Bar.name # Foo::Bar, not ALIAS::BarRuby can create a singleton class for most objects, but it usually materializes that class lazily the first time singleton-specific syntax/methods are used.
- Defining singleton behavior (
def self.*,class << Foo,extend, etc.) forces Ruby to create the singleton class if it was not created already. - Calling
Foo.singleton_classalso forces creation and returns the singleton class object. - Plain objects behave the same way:
obj.singleton_classordef obj.specialallocates one lazily for that object. - Classes and modules that never reference their singleton class may run indefinitely without allocating it.
- Certain objects can never have singleton classes: integers, floats, symbols, and frozen strings. Calling
singleton_classon them raisesTypeError. - Ruby boots with singleton classes for
true,false, andnilalready created.
Calling Foo.new does not force Foo's singleton class to exist. The new method is defined on Class
itself, so every class object inherits it automatically. Unless you override Foo.new via
def self.new (which would then create the singleton class) Ruby can instantiate Foo instances without ever
materializing Foo's singleton class.
Class.instance_method(:new).owner
# => ClassThe singleton class is the receiver for def self.*, class << self, and it is where modules mixed in via extend
actually insert their methods.
class Foo
class << self # reopens (or creates) Foo's singleton class
A = 1
def bar; A; end
end
def self.baz; end # adds the method to Foo's singleton class
@ivar = 1 # assigns a class instance variable on Foo's singleton class (NOT a class variable)
end- Inside the class body the current receiver (
self) isFoo, so@ivarwrites to the singleton class anddef self.bazdefines a singleton method. - Lexical constant lookup still follows the surrounding constant scope, so constants created inside
class << selfdo not becomeFoo::CONST.
class Foo
@counter = 0
class << self
A = 1
def bar
@counter += 1 # valid: singleton ivar
puts A # valid: constant defined in this singleton scope
end
end
def self.baz
puts @counter # valid: reads the singleton's @counter
puts A # NameError: Foo::A is not defined
end
endIf a constant must be visible as Foo::CONST, define it in the regular class body instead of inside a class << self
block.
The singleton class can be reopened anywhere with class << Foo as long as the constant already exists:
# file: foo.rb
class Foo; end
# file: foo_singleton.rb
class << Foo
def bar!
puts "bar"
end
end
# file: my_class.rb
class MyClass
# This also reopens Foo's singleton class
class << Foo
def baz!
puts "baz"
end
end
end
Foo.bar!
Foo.baz!Class variables (@@var) bypass singleton classes entirely.
They always belong to the base class or module, regardless of where they are defined:
class Foo
@@in_class_body = 1
class << self
@@in_singleton_scope = 2
def set_var
@@in_method = 3
end
end
end
Foo.set_var
# All class variables belong to Foo, not its singleton class
Foo.class_variables
# => [:@@in_class_body, :@@in_singleton_scope, :@@in_method]
Foo.singleton_class.class_variables
# => [:@@in_class_body, :@@in_singleton_scope, :@@in_method] # inherited view
# The singleton class itself owns no class variables
Foo.singleton_class.class_variables(false)
# => []This is fundamentally different from instance variables (@var), which do belong to the singleton class when
defined in class scope:
class Foo
@class_ivar = 1 # belongs to Foo's singleton class
@@class_var = 2 # belongs to Foo itself
endTop-level class variable access (@@foo = 1 outside any class) raises RuntimeError: class variable access from toplevel.
class << self can be nested. Each nesting opens the singleton class of the current self:
class Foo
class << self
# self is now Foo.singleton_class
def on_foo_singleton; end
class << self
# self is now Foo.singleton_class.singleton_class
def on_singleton_singleton; end
end
end
end
# on_foo_singleton is callable on Foo
Foo.on_foo_singleton
# on_singleton_singleton is NOT callable on Foo
Foo.on_singleton_singleton
# => NoMethodError
# It's callable on Foo's singleton class
Foo.singleton_class.on_singleton_singletonThe ownership chain is: Foo → Foo.singleton_class → Foo.singleton_class.singleton_class.
Class.new and Module.new create anonymous class/module objects. When assigned to a constant, that constant names the new namespace.
Inside the block, self is the newly created class/module.
Foo = Class.new do
def self.foo; end # class method on Foo
def bar; end # instance method on Foo instances
alias_method :baz, :bar
endYou can still access those methods even when the anonymous class isn't assigned to a constant:
c = Class.new do
def self.foo; end
def bar; end
end
c.foo
c.new.barInstance variables behave the same as in regular class definitions:
Foo = Class.new do
@class_ivar = 1 # instance variable of the Foo class
def initialize
@ivar = 2 # instance variable of Foo instances
end
endClass variables and constants follow lexical scope (Module.nesting). Class.new/Module.new blocks do not change lexical scope, so unqualified constants and class variables are defined in the outer scope, not on the new class/module.
class Outer
Foo = Class.new do
@@cvar = 1 # this belongs to Outer, not Foo
end
endDefining class variables in a top-level anonymous class/module results in an error:
Foo = Class.new do
@@cvar = 1 # RuntimeError: class variable access from toplevel
endSimilar to class variables, constants are attached to the closest lexical scope, not the anonymous class/module:
Foo = Class.new do
CONST = 2
end
defined?(Foo::CONST) # => nil
defined?(::CONST) # => "constant"If you want constants under the new class/module, use self::CONST or define them after the assignment.
Foo = Class.new do
self::CONST = 2
end
Foo::CONSTThe block does not introduce lexical nesting for class/module definitions:
class Foo
Bar = Class.new do
class Baz; end
end
end
# Baz is Foo::Baz, not Foo::Bar::BazYou cannot reliably reference the assigned constant inside the block because the assignment happens after the block runs. Bar::Baz inside the block refers to an outer Bar (if any) or raises NameError. Using self::Bar as a superclass also fails because self is the anonymous class, not the outer scope:
class Foo
Bar = Class.new do
class Baz < Bar; end # NameError: uninitialized constant Foo::Bar
class Baz < self::Bar; end # NameError: uninitialized constant #<Class:...>::Bar
end
endTo define classes or constants under the new class, use class self::Baz or self::Baz = Class.new (or const_set) inside the block, or define them after the assignment:
class Foo
Bar = Class.new do
class self::Baz; end # defines Foo::Bar::Baz
self::Qux = Class.new # defines Foo::Bar::Qux
end
end
# or
class Foo
Bar = Class.new
Bar::Baz = Class.new
endThe rules above apply to nested anonymous class/module definitions as well.
For example, constants defined in nested anonymous classes are still attached to the closest lexical scope:
Foo = Class.new do
Class.new do
CONST = 1
end
end
# CONST is defined on top-level, the closest lexical scope, instead of Foo
defined?(CONST) #=> "constant"
defined?(Foo::CONST) #=> nilAlthough Class.new/Module.new blocks do not create lexical scopes, class << self blocks inside them still do. Constants and class variables defined inside such a singleton class block are attached to the singleton class:
Foo = Class.new do
class << self
CONST = 1
end
end
Foo.singleton_class.constants # => [:CONST]
Foo.constants # => []This also applies to nested anonymous classes within the singleton class block:
Foo = Class.new do
class << self
Class.new do
CONST = 1
end
end
end
Foo.singleton_class.constants # => [:CONST]