Overview
Ruby reserves specific words that cannot be used as variable names, method names, or other identifiers. These reserved keywords form the core syntax of the language and include control structures, literals, and operators. Ruby's identifier naming system follows strict rules for variables, methods, constants, and classes, with specific conventions that affect scope and behavior.
Ruby defines 41 reserved keywords that serve different purposes: control flow (if
, else
, case
, when
), iteration (for
, while
, until
), method definition (def
, class
, module
), and special values (nil
, true
, false
). The language also distinguishes between different identifier types through naming conventions - local variables start with lowercase letters, constants with uppercase letters, instance variables with @
, class variables with @@
, and global variables with $
.
# Reserved keyword usage
def calculate_score
return nil if invalid_data?
case competition_type
when 'standard'
base_score * 1.0
else
base_score * 1.5
end
end
# Identifier types
LOCAL_CONSTANT = 100
@instance_var = "data"
@@class_var = "shared"
$global_var = "system_wide"
The identifier system directly impacts variable scope, method visibility, and constant lookup. Ruby's parser uses these naming patterns to determine how identifiers behave at runtime, making the conventions more than stylistic choices.
Basic Usage
Ruby's reserved keywords cannot be redefined or used as identifiers. Attempting to use reserved words as variable names, method names, or other identifiers results in syntax errors. The complete list includes logical operators (and
, or
, not
), control structures (if
, unless
, case
), loop constructs (while
, until
, for
), and special literals (nil
, true
, false
, self
, super
).
# This raises SyntaxError
def class
"invalid method name"
end
# => SyntaxError: unexpected keyword_class
# This also raises SyntaxError
if = 5
# => SyntaxError: unexpected '='
Local variables and method names follow identical naming rules: they begin with lowercase letters or underscores, followed by letters, digits, or underscores. Ruby allows method names to end with ?
or !
for predicate and mutating methods respectively.
# Valid local variables and method names
user_name = "alice"
is_valid = true
total_count = 42
def user_logged_in?
@session_active
end
def reset_password!
@password = generate_secure_password
save_to_database
end
Constants must start with uppercase letters and typically use SCREAMING_SNAKE_CASE
for multiple words. Ruby allows constant reassignment but issues warnings. Constants defined in classes or modules are scoped to those containers.
# Module-scoped constants
module Configuration
DATABASE_URL = "postgresql://localhost/myapp"
MAX_CONNECTIONS = 50
class Settings
DEFAULT_TIMEOUT = 30
end
end
# Accessing scoped constants
puts Configuration::DATABASE_URL
puts Configuration::Settings::DEFAULT_TIMEOUT
Instance variables begin with @
and exist within object instances. Class variables start with @@
and are shared across all instances of a class and its subclasses. Global variables use $
prefix and are accessible throughout the program.
class UserAccount
@@total_accounts = 0
def initialize(username)
@username = username
@created_at = Time.now
@@total_accounts += 1
end
def self.account_count
@@total_accounts
end
end
$application_name = "MyApp"
Advanced Usage
Ruby's identifier scoping rules create complex interactions when combined with metaprogramming. Constants undergo lexical scoping, meaning Ruby searches for constants starting from the current lexical scope, then moving outward through nesting levels before checking the inheritance chain.
module Outer
CONSTANT = "outer"
module Inner
CONSTANT = "inner"
class Example
def self.demonstrate_lookup
puts CONSTANT # Finds Inner::CONSTANT
puts ::CONSTANT rescue "Not found at top level"
puts Outer::CONSTANT # Explicit scope resolution
end
end
end
end
Outer::Inner::Example.demonstrate_lookup
# => inner
# => Not found at top level
# => outer
Dynamic identifier creation through metaprogramming requires careful handling of reserved keywords. Methods like define_method
and const_set
can create identifiers dynamically, but reserved keywords still cannot be used.
class DynamicMethodGenerator
RESERVED_WORDS = %w[class module def if else case when]
def self.create_method(name, &block)
if RESERVED_WORDS.include?(name.to_s)
raise ArgumentError, "Cannot use reserved word '#{name}' as method name"
end
define_method(name, &block)
end
def self.safe_const_set(name, value)
if name.to_s !~ /\A[A-Z][A-Za-z0-9_]*\z/
raise ArgumentError, "Invalid constant name: #{name}"
end
const_set(name, value)
end
end
generator = Class.new(DynamicMethodGenerator)
generator.create_method(:calculate_total) { |items| items.sum }
generator.safe_const_set("MAX_ITEMS", 100)
Method aliasing and alias_method
can create alternative names for methods, including potentially confusing scenarios where methods have names similar to reserved keywords but with slight variations.
class MethodAliasing
def original_method
"original implementation"
end
# Create aliases that might be confusing
alias_method :klass, :original_method # Similar to 'class'
alias_method :modul, :original_method # Similar to 'module'
# Dynamic aliasing with validation
def self.safe_alias(new_name, original_name)
reserved = %w[class module def if else case when while until for]
if reserved.include?(new_name.to_s)
raise ArgumentError, "Cannot alias to reserved word: #{new_name}"
end
alias_method new_name, original_name
end
end
Constant autoloading in Ruby creates complex scenarios where constant resolution might trigger file loading, potentially defining new constants that affect the lookup chain.
module AutoloadExample
# Simulate autoloading behavior
def self.const_missing(name)
case name
when :DatabaseConfig
const_set(name, Class.new do
TIMEOUT = 30
HOST = "localhost"
end)
when :CacheConfig
const_set(name, Module.new do
TTL = 3600
SIZE_LIMIT = 1000
end)
else
super
end
end
end
# These constants are created on first access
puts AutoloadExample::DatabaseConfig::TIMEOUT # => 30
puts AutoloadExample::CacheConfig::TTL # => 3600
Common Pitfalls
One of the most frequent mistakes involves using reserved keywords as hash keys without proper quoting. While symbols and strings can represent reserved words, they require careful syntax to avoid parser confusion.
# Problematic hash syntax
hash = { class: "User", module: "Authentication" } # Syntax error
# Correct approaches
hash = { "class" => "User", "module" => "Authentication" }
hash = { :class => "User", :module => "Authentication" }
hash = { "class": "User", "module": "Authentication" } # Symbol keys
Constant reassignment warnings are often ignored but can indicate serious design problems. Ruby allows constant modification but warns about it, and the behavior can be surprising in inheritance scenarios.
class Parent
SHARED_CONFIG = { timeout: 30, retries: 3 }
end
class Child < Parent
# This modifies the parent's constant
SHARED_CONFIG[:timeout] = 60 # No warning, but dangerous
# This creates a new constant with warning
SHARED_CONFIG = { timeout: 60, retries: 5 } # Warning issued
end
puts Parent::SHARED_CONFIG # May not be what you expect
Variable shadowing occurs when local variables hide method calls of the same name, leading to unexpected behavior that's difficult to debug.
class ShadowingExample
def name
"instance method"
end
def demonstrate_shadowing
puts name # Calls method: "instance method"
name = "local variable"
puts name # Local variable shadows method: "local variable"
# To call method when local variable exists
puts self.name # Explicit receiver: "instance method"
end
end
Global variable pollution represents a serious pitfall where libraries or application code accidentally creates global variables, leading to conflicts and unpredictable behavior.
# Dangerous: accidental global variable creation
def process_data(items)
$processed_count = 0 # Accidentally global
items.each do |item|
# Process item
$processed_count += 1
end
$processed_count
end
# Better: explicit local scope
def process_data_safely(items)
processed_count = 0
items.each do |item|
# Process item
processed_count += 1
end
processed_count
end
Class variable inheritance creates unexpected sharing between parent and child classes, where modifications in child classes affect parent classes and siblings.
class Vehicle
@@count = 0
def initialize
@@count += 1
end
def self.count
@@count
end
end
class Car < Vehicle
end
class Truck < Vehicle
end
# All classes share the same @@count variable
Vehicle.new
Car.new
Truck.new
puts Vehicle.count # => 3 (shared across all classes)
puts Car.count # => 3 (same variable)
puts Truck.count # => 3 (same variable)
Reference
Reserved Keywords
Category | Keywords | Purpose |
---|---|---|
Control Flow | if , unless , else , elsif , case , when , then |
Conditional execution |
Loops | while , until , for , break , next , redo |
Iteration control |
Method Definition | def , undef , alias , return , yield |
Method creation and control |
Class/Module | class , module , super , self |
Object-oriented constructs |
Exception Handling | begin , rescue , ensure , raise , retry |
Error management |
Logical Operators | and , or , not |
Boolean logic |
Literals | nil , true , false |
Special values |
Variable Scope | defined? , END , BEGIN |
Runtime introspection |
Identifier Naming Rules
Type | Pattern | Example | Scope |
---|---|---|---|
Local Variable | [a-z_][a-zA-Z0-9_]* |
user_name , _temp |
Method/block local |
Constant | [A-Z][A-Z0-9_]* |
MAX_SIZE , CONFIG |
Class/module scoped |
Instance Variable | @[a-zA-Z_][a-zA-Z0-9_]* |
@name , @user_id |
Object instance |
Class Variable | @@[a-zA-Z_][a-zA-Z0-9_]* |
@@count , @@total |
Class hierarchy |
Global Variable | $[a-zA-Z_][a-zA-Z0-9_]* |
$debug , $app_name |
Program global |
Method Name | [a-z_][a-zA-Z0-9_]*[?!]? |
valid? , save! |
Object scope |
Special Global Variables
Variable | Purpose | Example |
---|---|---|
$! |
Current exception | rescue; puts $!.message; end |
$$ |
Process ID | puts "PID: #{$$}" |
$? |
Child process status | system('ls'); puts $?.exitstatus |
$0 |
Script name | puts "Running: #{$0}" |
$* |
Command line arguments | puts "Args: #{$*}" |
$/ |
Input record separator | $/ = "\n\n" |
$\ |
Output record separator | $\ = "\n" |
Constant Lookup Order
- Lexical scope - Current class/module nesting
- Inheritance chain - Superclasses of current class
- Included modules - Mixed-in modules
- Top-level constants - Main object constants
- Object class - Ruby's root object class
Method Naming Conventions
Suffix | Purpose | Example | Behavior |
---|---|---|---|
? |
Predicate method | empty? , valid? |
Returns boolean |
! |
Mutating method | sort! , upcase! |
Modifies receiver |
= |
Setter method | name= , value= |
Assignment syntax |
No suffix | Standard method | size , first |
Returns value |
Scope Resolution Operators
Operator | Purpose | Example |
---|---|---|
:: |
Absolute scope | ::Math::PI |
:: |
Relative scope | Module::CONSTANT |
:: |
Method call | object::method (rare) |
Reserved Word Alternatives
Reserved | Alternative | Usage |
---|---|---|
class |
klass , type |
Hash keys, method parameters |
module |
mod , namespace |
Configuration, APIs |
def |
define , definition |
Metaprogramming contexts |
if |
condition , when_true |
Configuration keys |
else |
otherwise , default |
Conditional logic |