Recent from talks
Nothing was collected or created yet.
Ruby syntax
View on WikipediaThis article contains instructions or advice. (August 2021) |
This article is missing information about pattern matching syntax in Ruby. (August 2021) |
The syntax of the Ruby programming language is broadly similar to that of Perl and Python. Class and method definitions are signaled by keywords, whereas code blocks can be defined by either keywords or braces. In contrast to Perl, variables are not obligatorily prefixed with a sigil. When used, the sigil changes the semantics of scope of the variable. For practical purposes there is no distinction between expressions and statements.[1][2] Line breaks are significant and taken as the end of a statement; a semicolon may be equivalently used. Unlike Python, indentation is not significant.
One of the differences from Python and Perl is that Ruby keeps all of its instance variables completely private to the class and only exposes them through accessor methods (attr_writer, attr_reader, etc.). Unlike the "getter" and "setter" methods of other languages like C++ or Java, accessor methods in Ruby can be created with a single line of code via metaprogramming; however, accessor methods can also be created in the traditional fashion of C++ and Java. As invocation of these methods does not require the use of parentheses, it is trivial to change an instance variable into a full function without modifying a single line of calling code or having to do any refactoring achieving similar functionality to C# and VB.NET property members.
Python's property descriptors are similar, but come with a trade-off in the development process. If one begins in Python by using a publicly exposed instance variable, and later changes the implementation to use a private instance variable exposed through a property descriptor, code internal to the class may need to be adjusted to use the private variable rather than the public property. Ruby's design forces all instance variables to be private, but also provides a simple way to declare set and get methods. This is in keeping with the idea that in Ruby one never directly accesses the internal members of a class from outside the class; rather, one passes a message to the class and receives a response.
Interactive sessions
[edit]The following examples can be run in a Ruby shell such as Interactive Ruby Shell, or saved in a file and run from the command line by typing ruby <filename>.
Classic Hello world example:
puts 'Hello World!'
Some basic Ruby code:
>> # Everything, including a literal, is an object, so this works:
>> -199.abs
=> 199
>> 'ice is nice'.length
=> 11
>> 'ruby is cool.'.index('u')
=> 1
>> "Nice Day Isn't It?".downcase.split('').uniq.sort.join
=> " '?acdeinsty"
Input:
print 'Please type name >'
name = gets.chomp
puts "Hello #{name}."
Conversions:
puts 'Give me a number'
number = gets.chomp
puts number.to_i
output_number = number.to_i + 1
puts output_number.to_s + ' is a bigger number.'
Strings
[edit]There are a variety of ways to define strings in Ruby.
The following assignments are equivalent:
a = "\nThis is a double-quoted string\n"
a = %Q{\nThis is a double-quoted string\n}
a = %{\nThis is a double-quoted string\n}
a = %/\nThis is a double-quoted string\n/
a = <<-BLOCK
This is a double-quoted string
BLOCK
Strings support variable interpolation:
var = 3.14159
"pi is #{var}"
=> "pi is 3.14159"
The following assignments are equivalent and produce raw strings:
a = 'This is a single-quoted string'
a = %q{This is a single-quoted string}
Collections
[edit]Constructing and using an array:
>> a = [3, 'hello', 14.5, 1, 2, [6, 15]]
=> [3, "hello", 14.5, 1, 2, [6, 15]]
>> a[2]
=> 14.5
>> a.[](2)
=> 14.5
>> a.reverse
=> [[6, 15], 2, 1, 14.5, "hello", 3]
>> a.flatten.uniq
=> [3, "hello", 14.5, 1, 2, 6, 15]
Constructing and using an associative array (in Ruby, called a hash):
>> hash = Hash.new # equivalent to hash = {}
>> hash = { water: 'wet', fire: 'hot' } # makes the previous line redundant as we are now
>> # assigning hash to a new, separate hash object
>> puts hash[:fire]
hot
=> nil
>>
?> hash.each_pair do |key, value| # or: hash.each do |key, value|
?> puts "#{key} is #{value}"
>> end
water is wet
fire is hot
=> {water: "wet", fire: "hot"}
>>
>> hash.delete :water # deletes the pair :water => 'wet'
=> "wet"
>> hash.delete_if {|key,value| value == 'hot'} # deletes the pair :fire => 'hot'
=> {}
Control structures
[edit]If statement:
# Generate a random number and print whether it's even or odd.
if rand(100).even?
puts "It's even"
else
puts "It's odd"
end
Blocks and iterators
[edit]The two syntaxes for creating a code block:
{ puts 'Hello, World!' } # note the braces
# or:
do
puts 'Hello, World!'
end
A code block can be passed to a method as an optional block argument. Many built-in methods have such arguments:
File.open('file.txt', 'w') do |file| # 'w' denotes "write mode"
file.puts 'Wrote some text.'
end # file is automatically closed here
File.readlines('file.txt').each do |line|
puts line
end
# => Wrote some text.
Parameter-passing a block to be a closure:
>> # In an object instance variable (denoted with '@'), remember a block.
?> def remember(&a_block)
?> @block = a_block
>> end
>>
=> :remember
>>
>> # Invoke the preceding method, giving it a block that takes a name.
>> remember {|name| puts "Hello, #{name}!"}
=> #<Proc:0x00007ff0b9823460 (irb):8>
>>
>> # Call the closure (note that this happens not to close over any free variables):
>> @block.call('Jon')
Hello, Jon!
=> nil
Creating an anonymous function:
proc {|arg| puts arg}
Proc.new {|arg| puts arg}
lambda {|arg| puts arg}
->(arg) {puts arg} # introduced in Ruby 1.9
Returning closures from a method:
?> def create_set_and_get(initial_value=0) # note the default value of 0
?> closure_value = initial_value
?> [ Proc.new {|x| closure_value = x}, Proc.new { closure_value } ]
>> end
>>
>> setter, getter = create_set_and_get # returns two values
>> setter.call(21)
>> getter.call
=> 21
# Parameter variables can also be used as a binding for the closure,
# so the preceding can be rewritten as:
def create_set_and_get(closure_value=0)
[ proc {|x| closure_value = x } , proc { closure_value } ]
end
Yielding the flow of program control to a block that was provided at calling time:
?> def use_hello
?> yield "hello"
>> end
>>
>> # Invoke the preceding method, passing it a block.
>> use_hello {|string| puts string}
hello
=> nil
Iterating over enumerations and arrays using blocks:
>> array = [1, 'hi', 3.14]
>> array.each {|item| puts item }
1
hi
3.14
=> [1, "hi", 3.14]
>>
>> array.each_index {|index| puts "#{index}: #{array[index]}" }
0: 1
1: hi
2: 3.14
=> [1, "hi", 3.14]
>>
>> # The following uses a (a..b) Range
>> (3..6).each {|num| puts num }
3
4
5
6
A method such as inject can accept both a parameter and a block. The inject method iterates over each member of a list, performing some function on it while retaining an aggregate. This is analogous to the foldl function in functional programming languages. For example:
>> [1,3,5].inject(10) {|sum, element| sum + element}
=> 19
On the first pass, the block receives 10 (the argument to inject) as sum, and 1 (the first element of the array) as element. This returns 11, which then becomes sum on the next pass. It is added to 3 to get 14, which is then added to 5 on the third pass, to finally return 19.
Using an enumeration and a block to square the numbers 1 to 10 (using a range):
>> (1..10).collect {|x| x*x}
=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Or invoke a method on each item (map is a synonym for collect):
>> (1..5).map(&:to_f)
=> [1.0, 2.0, 3.0, 4.0, 5.0]
Classes
[edit]The following code defines a class named Person. In addition to initialize, the usual constructor to create new objects, it has two methods: one to override the <=> comparison operator (so Array#sort can sort by age) and the other to override the to_s method (so Kernel#puts can format its output). Here, attr_reader is an example of metaprogramming in Ruby: attr_accessor defines getter and setter methods of instance variables, but attr_reader only getter methods. The last evaluated statement in a method is its return value, allowing the omission of an explicit return statement.
class Person
attr_reader :name, :age
def initialize(name, age)
@name, @age = name, age
end
def <=>(person) # the comparison operator for sorting
@age <=> person.age
end
def to_s
"#{@name} (#{@age})"
end
end
group = [
Person.new("Bob", 33),
Person.new("Chris", 16),
Person.new("Ash", 23)
]
puts group.sort.reverse
The preceding code prints three names in reverse age order:
Bob (33)
Ash (23)
Chris (16)
Person is a constant and is a reference to a Class object.
Open classes
[edit]In Ruby, classes are never closed: methods can always be added to an existing class. This applies to all classes, including the standard, built-in classes. All that is needed to do is open up a class definition for an existing class, and the new contents specified will be added to the existing contents. A simple example of adding a new method to the standard library's Time class:
>> # re-open Ruby's Time class
?> class Time
?> def yesterday
?> self - 86400
?> end
>> end
=> :yesterday
>>
>> today = Time.now
=> 2025-08-20 10:34:33.595383429 +0900
>> yesterday = today.yesterday
=> 2025-08-19 10:34:33.595383429 +0900
Adding methods to previously defined classes is often called monkey-patching. If performed recklessly, the practice can lead to both behavior collisions with subsequent unexpected results and code scalability problems.
Since Ruby 2.0 it has been possible to use refinements to reduce the potentially negative consequences of monkey-patching, by limiting the scope of the patch to particular areas of the code base.
# re-open Ruby's Time class
module RelativeTimeExtensions
refine Time do
def half_a_day_ago
self - 43200
end
end
end
module MyModule
class MyClass
# Allow the refinement to be used
using RelativeTimeExtensions
def window
Time.now.half_a_day_ago
end
end
end
Exceptions
[edit]An exception is raised with a raise call:
raise
An optional message can be added to the exception:
raise "This is a message"
Exceptions can also be specified by the programmer:
raise ArgumentError, "Illegal arguments!"
Alternatively, an exception instance can be passed to the raise method:
raise ArgumentError.new("Illegal arguments!")
This last construct is useful when raising an instance of a custom exception class featuring a constructor that takes more than one argument:
class ParseError < Exception
def initialize(input, line, pos)
super "Could not parse '#{input}' at line #{line}, position #{pos}"
end
end
raise ParseError.new("Foo", 3, 9)
Exceptions are handled by the rescue clause. Such a clause can catch exceptions that inherit from StandardError. Other flow control keywords that can be used when handling exceptions are else and ensure:
begin
# do something
rescue
# handle exception
else
# do this if no exception was raised
ensure
# do this whether or not an exception was raised
end
It is a common mistake to attempt to catch all exceptions with a simple rescue clause. To catch all exceptions one must write:
begin
# do something
rescue Exception
# Exception handling code here.
# Don't write only "rescue"; that only catches StandardError, a subclass of Exception.
end
Or catch particular exceptions:
begin
# do something
rescue RuntimeError
# handle only RuntimeError and its subclasses
end
It is also possible to specify that the exception object be made available to the handler clause:
begin
# do something
rescue RuntimeError => e
# handling, possibly involving e, such as "puts e.to_s"
end
Alternatively, the most recent exception is stored in the magic global $!.
Several exceptions can also be caught:
begin
# do something
rescue RuntimeError, Timeout::Error => e
# handling, possibly involving e
end
Metaprogramming
[edit]Ruby code can programmatically modify, at runtime, aspects of its own structure that would be fixed in more rigid languages, such as class and method definitions. This sort of metaprogramming can be used to write more concise code and effectively extend the language.
For example, the following Ruby code generates new methods for the built-in String class, based on a list of colors. The methods wrap the contents of the string with an HTML tag styled with the respective color.
COLORS = { black: "000",
red: "f00",
green: "0f0",
yellow: "ff0",
blue: "00f",
magenta: "f0f",
cyan: "0ff",
white: "fff" }
class String
COLORS.each do |color,code|
define_method "in_#{color}" do
"<span style=\"color: ##{code}\">#{self}</span>"
end
end
end
The generated methods could then be used like this:
>> "Hello, World!".in_blue
=> "<span style=\"color: #00f\">Hello, World!</span>"
To implement the equivalent in many other languages, the programmer would have to write each method (in_black, in_red, in_green, etc.) separately.
Some other possible uses for Ruby metaprogramming include:
- intercepting and modifying method calls
- implementing new inheritance models
- dynamically generating classes from parameters
- automatic object serialization
- interactive help and debugging
References
[edit]- ^ "[ruby-talk:01120] Re: The value of while..."
In Ruby's syntax, statement is just a special case of an expression that cannot appear as an argument (e.g. multiple assignment).
- ^ "[ruby-talk:02460] Re: Precedence question".
statement [...] can not be part of expression unless grouped within parentheses.
Ruby syntax
View on Grokipedia5.times { print "We have " + 5.to_s + " lights" }).[1] Variable scoping uses sigils for clarity—local variables without prefixes, instance variables with @, class variables with @@, and globals with $—while supporting single inheritance augmented by mixins for modularity (e.g., include Enumerable).[1] The language's structure is divided into key components, including literals for representing data types such as integers, floats, strings, arrays (e.g., [1, 2, 3] or %w{a b c}), and hashes (e.g., {key: 'value'}); assignment statements for binding values to variables; and control expressions like if, unless, while, until, and for loops to manage program flow.[3]
Methods are defined with def and can accept blocks for iteration or callbacks, supporting operator overloading and refinements for extending behavior without modifying originals.[3] Modules and classes use module and class keywords for organization and inheritance, while exception handling employs begin, rescue, ensure, and raise.[3] Notable modern features include pattern matching, introduced experimentally in Ruby 2.7 (2019) and stabilized in 3.0 (2020), which allows concise destructuring and matching of complex data structures via case/in or rightward assignment (=>); Ruby syntax continues to evolve, with the latest stable release 3.4.7 (as of November 2025) adding features like the default block parameter it for cleaner short blocks.[4][5] Overall, Ruby's syntax balances power and brevity, with operator precedence following a defined hierarchy and comments starting with # to enhance code maintainability.[3]
Getting Started
Interactive Ruby
Interactive Ruby, commonly known as irb, serves as the primary interactive shell for the Ruby programming language, allowing users to execute code snippets in real-time without the need for script files.[6] It functions as a read-eval-print loop (REPL), where the interpreter reads user input, evaluates it, prints the result, and loops back for further input, facilitating immediate feedback on Ruby expressions.[6] This environment is particularly valuable for exploring Ruby syntax through trial and error, as it enables quick testing of individual statements or blocks of code.[6] To begin an irb session, users invoke theirb command from the terminal or command prompt, assuming Ruby is installed and accessible in the system's PATH.[6] Once started, the shell displays a prompt such as irb(main):001:0>, indicating the session's main context and line number. Users can then enter Ruby expressions, such as basic literals like 42 or "hello", which are immediately evaluated and their output displayed with an arrow indicator, for example:
irb(main):001> 42
=> 42
irb(main):002> "hello"
=> "hello"
irb(main):001> 42
=> 42
irb(main):002> "hello"
=> "hello"
File.basename(Dir.pwd), yield results such as the current directory name, printed as => "example_dir".[6] To end the session, users type exit or press Ctrl+D (on Unix-like systems).[6]
irb supports multiline input for constructs requiring blocks, such as iterators, where the prompt changes to * to signal continuation until the block is completed with end.[6] For instance, entering Dir.entries('.').select do |entry| followed by indented lines like entry.start_with?('R') and then end will evaluate the full block and return matching entries, such as ["README.md", "Rakefile"].[6] This feature enhances learning by allowing experimentation with Ruby's syntax in an iterative, low-stakes manner, building intuition for how elements interact without compiling or running full programs.[6]
Variables and Literals
In Ruby, variables are dynamically typed, meaning they do not require explicit type declarations and can hold any object type during runtime.[7] Assignment to a variable implicitly creates it if it does not exist, with the type inferred from the assigned value.[7] For example, the statementx = 5 declares and initializes a local variable x that references the integer object 5.[7]
Ruby employs four primary types of variables, each with distinct scoping rules to manage accessibility. Local variables, lacking any prefix, are confined to the scope in which they are defined, such as the top-level script, a class or module body, or a method body; for instance, def method; y = 10; end creates a local y accessible only within method.[7] Instance variables, prefixed with @, are scoped to a specific instance of a class and persist for the object's lifetime, like @instance_var = "value" which can be accessed in instance methods.[7] Class variables, denoted by @@, are shared across all instances of a class and its subclasses, initialized once per class hierarchy, as in @@class_var = 20.[7] Global variables, starting with $, have program-wide scope and are accessible from any context, such as $global_var = "global", though their use is discouraged due to potential side effects.[7]
Literal values in Ruby include the boolean constants true and false, which represent truth and falsehood, respectively, and are used in conditional expressions and logical operations.[8] The keyword nil serves as a literal singleton object indicating the absence of a value, often returned by methods with no explicit return or for uninitialized variables, and it evaluates to false in boolean contexts alongside false.[8] These literals are written in lowercase and require no additional syntax, such as if true then puts "condition met" end for basic usage.[8]
Constants in Ruby are conventionally defined using uppercase names and assignment, like PI = 3.14, and are intended to hold immutable values throughout the program.[7] They are scoped to the class or module where defined, accessible to nested scopes, outer scopes, and superclasses, or via the scope resolution operator :: (e.g., Math::PI).[7] While Ruby does not enforce immutability, reassigning a constant triggers a runtime warning, promoting the convention of treating them as fixed.[7]
Basic Data Types
Numbers
In Ruby, numbers are represented primarily by two built-in classes:Integer for whole numbers and Float for floating-point numbers. The Integer class, introduced in Ruby 2.4 to unify the previous Fixnum (for small integers fitting in a machine word) and Bignum (for arbitrary-precision integers), handles all integer values without overflow concerns, automatically managing precision as needed.[9] Floats use the native double-precision representation for approximate real numbers.[10]
Integer literals are written as plain decimal digits, optionally prefixed for other bases: decimal (e.g., 42 or 1_000 with underscores for readability), binary (0b101), octal (0o52), or hexadecimal (0x2A). Negative integers include a leading minus sign (e.g., -10), and zero is simply 0. These literals directly instantiate Integer objects.[11]
Float literals consist of a decimal point with optional integer and fractional parts, and may include scientific notation with e or E (e.g., 3.14, 0.001, or 1e-3). Underscores are permitted for clarity (e.g., 1_234.56). Operations involving floats yield Float results.[11]
Basic arithmetic uses operators + (addition), - (subtraction), * (multiplication), / (division), and % (modulo), which are implemented as methods on the operand classes. For integers, division truncates toward zero (e.g., 5 / 2 yields 2), while modulo follows the sign of the dividend (e.g., 5 % 2 is 1, -5 % 2 is -1).[12] When mixing types, such as an integer and a float, Ruby coerces the integer to float via the coerce mechanism in the Numeric superclass, producing a float result (e.g., 5 + 2.0 equals 7.0).[13]
# Integer examples
42.class # => [Integer](/page/Integer)
0b101 # => 5 (binary)
-10 % 3 # => -1
# Float examples
3.14.class # => Float
1e3 # => 1000.0
# Arithmetic with coercion
5 / 2 # => 2 (integer division)
5.0 / 2 # => 2.5
5 + 2.0 # => 7.0
```[](https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html)[](https://docs.ruby-lang.org/en/master/syntax/operators_rdoc.html)
### Strings
In Ruby, strings are sequences of bytes representing text or binary data, created using literals or the `String.new` method. Single-quoted strings, delimited by `'`, treat content literally and do not process escape sequences except for `\'` and `\\`, making them suitable for simple, unprocessed text. For example, `'hello\nworld'` outputs as `hello\nworld` without interpreting the newline.[](https://docs.ruby-lang.org/en/master/String.html)
Double-quoted strings, delimited by `"`, support escape sequences for special characters such as `\n` for [newline](/page/Newline) and `\t` for tab, allowing formatted output like `"hello\nworld"` which renders as two lines. They also enable [string interpolation](/page/String_interpolation), where expressions within `#{}` are evaluated and inserted, such as `"age: #{age}"` assuming `age = 25` yields `age: 25`. This feature facilitates embedding variables or computations directly into strings for dynamic construction.[](https://docs.ruby-lang.org/en/master/String.html)
For multiline strings, Ruby provides heredoc syntax using `<<-IDENTIFIER` (or `<<IDENTIFIER` without indentation), where the string spans until the closing `IDENTIFIER` on a new line. This is useful for embedding blocks like SQL queries without concatenation:
# Integer examples
42.class # => [Integer](/page/Integer)
0b101 # => 5 (binary)
-10 % 3 # => -1
# Float examples
3.14.class # => Float
1e3 # => 1000.0
# Arithmetic with coercion
5 / 2 # => 2 (integer division)
5.0 / 2 # => 2.5
5 + 2.0 # => 7.0
```[](https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html)[](https://docs.ruby-lang.org/en/master/syntax/operators_rdoc.html)
### Strings
In Ruby, strings are sequences of bytes representing text or binary data, created using literals or the `String.new` method. Single-quoted strings, delimited by `'`, treat content literally and do not process escape sequences except for `\'` and `\\`, making them suitable for simple, unprocessed text. For example, `'hello\nworld'` outputs as `hello\nworld` without interpreting the newline.[](https://docs.ruby-lang.org/en/master/String.html)
Double-quoted strings, delimited by `"`, support escape sequences for special characters such as `\n` for [newline](/page/Newline) and `\t` for tab, allowing formatted output like `"hello\nworld"` which renders as two lines. They also enable [string interpolation](/page/String_interpolation), where expressions within `#{}` are evaluated and inserted, such as `"age: #{age}"` assuming `age = 25` yields `age: 25`. This feature facilitates embedding variables or computations directly into strings for dynamic construction.[](https://docs.ruby-lang.org/en/master/String.html)
For multiline strings, Ruby provides heredoc syntax using `<<-IDENTIFIER` (or `<<IDENTIFIER` without indentation), where the string spans until the closing `IDENTIFIER` on a new line. This is useful for embedding blocks like SQL queries without concatenation:
The `<<-` variant allows indentation of the closing identifier for cleaner code alignment.[](https://docs.ruby-lang.org/en/master/String.html)
Common string operations include accessing the length with `str.length`, which returns the number of characters, such as `"hello".length` yielding `5`. Concatenation uses the `+` operator to join strings, producing a new instance like `"hello" + " world"` resulting in `"hello world"`, though repeated use can be inefficient for large builds due to immutability. For pattern-based substitution, `gsub` replaces all occurrences matching a pattern, e.g., `"hello".gsub(/e/, "a")` returns `"hallo"`. These methods support Ruby's mutable strings, allowing in-place modifications where applicable.[](https://docs.ruby-lang.org/en/master/String.html#method-i-length)[](https://docs.ruby-lang.org/en/master/String.html#method-i-2B)[](https://docs.ruby-lang.org/en/master/String.html#method-i-gsub)
### Symbols
In Ruby, symbols are immutable objects that serve as lightweight identifiers, representing names or keys within the interpreter. Unlike strings, which are mutable sequences of characters, symbols are designed for efficiency in scenarios where the same identifier is reused frequently, such as in method names or hash keys. They are created as unique instances, meaning that identical symbols always refer to the same object in memory throughout a program's execution, which conserves resources by avoiding redundant allocations.[](https://docs.ruby-lang.org/en/master/Symbol.html)
The primary syntax for creating symbols is the colon prefix followed by a name, such as `:name` for simple identifiers composed of alphanumeric characters and underscores. For dynamic or interpolated symbols, double quotes allow embedding expressions, as in `:"dynamic#{variable}"`, while single quotes disable interpolation to treat content literally, resulting in `:'dynamic\#{variable}'`. Alternative literal forms include the `%s` construct, like `%s[identifier]` for a single symbol or `%i[foo bar]` for an array of symbols without interpolation, and `%I[foo bar]` with interpolation enabled. These literals generate symbols that are immediately available for use.[](https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html)
Internally, symbols are stored as a single, immutable object with a fixed `object_id`, ensuring that `:example.object_id` yields the same value every time, regardless of creation context—this constancy promotes performance by enabling fast lookups and comparisons via object identity rather than content evaluation. For instance, multiple references to `:to_s` share the identical object, reducing memory overhead compared to equivalent strings. Symbols are particularly common in hashes as keys due to their hash-friendly uniqueness and immutability, though full hash details are covered elsewhere; they also facilitate method references, as in `method(:to_s)`, which retrieves the `to_s` method as a `Method` object.[](https://docs.ruby-lang.org/en/master/Symbol.html)
Conversion between symbols and strings is straightforward and bidirectional. The `to_s` method on a symbol returns its string representation, such as `:name.to_s` yielding `"name"`, allowing seamless integration with string-based APIs. Conversely, strings can be converted to symbols using `to_sym` or its alias `intern`, where `"name".to_sym` produces `:name`; if the receiver is already a symbol, `intern` returns `self` unchanged. These methods enable flexible data interchange while preserving the efficiency benefits of symbols where appropriate.[](https://docs.ruby-lang.org/en/master/Symbol.html#method-i-to_s)[](https://docs.ruby-lang.org/en/master/Symbol.html#method-i-intern)
## Collections and Data Structures
### Arrays
In Ruby, arrays are ordered collections of objects that can hold elements of any type, including mixed data such as numbers, strings, and symbols.[](https://docs.ruby-lang.org/en/master/Array.html) They are created using literal syntax enclosed in square brackets, where elements are separated by commas; for example, `[1, 'a', :b]` defines an array with an integer, a string, and a symbol as elements.[](https://docs.ruby-lang.org/en/master/Array.html) An empty array is represented simply as `[]`.[](https://docs.ruby-lang.org/en/master/Array.html) Arrays can also be constructed using `Array.new(size)` to preallocate space, optionally with a block to initialize elements, such as `Array.new(3) { |i| i * 2 }` which yields `[0, 2, 4]`.[](https://docs.ruby-lang.org/en/master/Array.html)
Elements in an array are accessed via zero-based integer indexing using the `[]` method; for instance, given `arr = [1, 2, 3, 4]`, `arr[0]` returns `1` and `arr[-1]` retrieves the last element `4` using negative indices that count from the end.[](https://docs.ruby-lang.org/en/master/Array.html) Slices allow extraction of subarrays with ranges or start-length pairs: `arr[1..3]` produces `[2, 3, 4]`, while `arr[1, 2]` yields `[2, 3]`.[](https://docs.ruby-lang.org/en/master/Array.html) Assignment to indices or slices is also supported, enabling modification like `arr[1] = 'x'` to change the second element.[](https://docs.ruby-lang.org/en/master/Array.html)
Arrays support various manipulation methods for adding and removing elements. The `push` method appends one or more elements to the end, as in `arr.push(5)` updating `arr` to `[1, 2, 3, 4, 5]`, while `pop` removes and returns the last element.[](https://docs.ruby-lang.org/en/master/Array.html) Conversely, `unshift` adds elements to the beginning, and `shift` removes the first one; for example, `arr.unshift(0)` prepends `0`.[](https://docs.ruby-lang.org/en/master/Array.html) The `length` or `size` method returns the number of elements, such as `arr.length` yielding `5`.[](https://docs.ruby-lang.org/en/master/Array.html) For iteration, methods like `each` and `map` provide previews of traversing arrays, allowing element-by-element processing without delving into block definitions here.[](https://docs.ruby-lang.org/en/master/Array.html)
Multidimensional arrays are formed by nesting arrays as elements, creating structures like matrices; for example, `[[1, 2], [3, 4]]` represents a 2x2 array accessible via `matrix[0][1]` to get `2`.[](https://docs.ruby-lang.org/en/master/Array.html) Care must be taken with initialization to avoid shared references, using blocks like `Array.new(3) { Array.new(3) }` for distinct inner arrays.[](https://docs.ruby-lang.org/en/master/Array.html)
### Hashes
In Ruby, a hash is an ordered collection of key-value pairs that preserves insertion order, where each unique key maps to a specific value, providing a dictionary-like data structure for associating data. Hashes are created using literal syntax enclosed in curly braces, allowing for immediate initialization with key-value pairs separated by commas and using the rocket operator (=>) or, for symbol keys, a colon (:). For example, the following creates a hash with symbol keys: `{ :a => 1, :b => 2 }`, which is equivalent to the more concise form `{ a: 1, b: 2 }`. String keys can also be used, as in `{ 'a' => 1, 'b' => 2 }`, and mixed key types are permitted within the same hash, though consistency is recommended for clarity.[](https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Hash+Data+Syntax)
Accessing values in a hash is typically done using square bracket notation with the key, such as `hash[:key]`, which returns the associated value or `nil` if the key is absent. For safer retrieval that raises a `KeyError` on missing keys, the `fetch` method is employed: `hash.fetch(:key)`. Hashes support default values to handle missing keys gracefully; for instance, `Hash.new(0)` creates a hash that returns 0 for any undefined key, allowing operations like `h[:missing] += 1` without explicit checks.[](https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Hash+Value+Basics)[](https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Any-Key+Default)
To add or update entries, assignment via square brackets is used: `hash[:new_key] = value`, which inserts the pair if the key does not exist or overwrites the value if it does. For combining hashes, the `merge` method creates a new hash incorporating pairs from another: `hash.merge(other_hash)`, with later keys overriding earlier ones in conflicts; a destructive variant `merge!` modifies the original. Deletion of a key-value pair is achieved with `hash.delete(:key)`, which removes the entry and returns the deleted value or `nil` if the key was not present.[](https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Methods+for+Assigning)[](https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Methods+for+Deleting)
Hashes provide methods to retrieve collections of keys or values as arrays, facilitating inspection without iteration. The `keys` method returns an array of all keys: for `h = { a: 1, b: 2 }`, `h.keys` yields `[:a, :b]`. Similarly, `values` returns the array of values: `h.values` yields `[1, 2]`. Symbols are preferred as hash keys in Ruby due to their immutable nature and efficient hashing, promoting consistent and performant code.[](https://docs.ruby-lang.org/en/master/Hash.html#method-i-keys)[](https://docs.ruby-lang.org/en/master/Hash.html#method-i-values)
### Ranges and Enumerables
In [Ruby](/page/Ruby), ranges represent intervals of values between a beginning and an end point, serving as a concise way to express sequences or spans. They can be created using literal [syntax](/page/Hungarian_noun_phrase) with two or three dots: an inclusive range includes the end value, denoted by `start..end`, while an exclusive range excludes it, denoted by `start...end`. For example, `(1..5)` encompasses the integers 1 through 5, whereas `(1...5)` includes only 1 through 4.[](https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html)[](https://docs.ruby-lang.org/en/master/Range.html)
Ranges support conversion to arrays via the `to_a` method, which generates an [array](/page/ARRAY) of all elements within the range. For instance, `(1..3).to_a` yields `[1, 2, 3]`. Basic operations include `include?`, which checks membership—`(1..5).include?(3)` returns `true`—and `size`, which computes the number of elements, returning `3` for `(1..3)`.[](https://docs.ruby-lang.org/en/master/Range.html#method-i-to_a)[](https://docs.ruby-lang.org/en/master/Range.html#method-i-include-3F)[](https://docs.ruby-lang.org/en/master/Range.html#method-i-size)
The `step` method enables custom increments, producing a stepped sequence from the range. For example, `(1..10).step(2)` advances by 2, covering odd or even numbers as appropriate.[](https://docs.ruby-lang.org/en/master/Range.html#method-i-step)
The Enumerable module is a [mixin](/page/Mixin) that equips collection classes, including [Array](/page/Array), Hash, and Range, with higher-level methods for querying, searching, and transforming elements, provided the class implements an `each` method to yield successive members.[](https://docs.ruby-lang.org/en/master/Enumerable.html) This allows uniform behavior across collections; for instance, ranges can use Enumerable methods like `map` to apply a transformation to each element, returning an array of results—`(1..3).map { |x| x * 2 }` produces `[2, 4, 6]`—or `select` to filter elements based on a condition, such as `(1..5).select { |x| x.even? }` yielding `[2, 4]`.[](https://docs.ruby-lang.org/en/master/Enumerable.html#method-i-map)[](https://docs.ruby-lang.org/en/master/Enumerable.html#method-i-select)
## Control Flow
### Conditionals
In Ruby, conditional statements allow programs to execute different code paths based on the evaluation of expressions. The primary constructs are the `if` expression, its inverse `unless`, the ternary operator, and the `case` expression for multi-way branching. These are control expressions that return the value of the last evaluated statement in the chosen branch, enabling them to be used in assignments or as part of larger expressions.[](https://docs.ruby-lang.org/en/master/syntax/control_expressions_rdoc.html)
The `if` expression evaluates a test condition and executes the associated body if the condition is truthy. Its basic syntax is `if test then body end`, where the `then` keyword is optional and separates the condition from the body; without it, a newline or semicolon acts as the delimiter. For multi-line bodies, the `end` keyword is required to close the block. An `if` can include an `else` clause for the falsy case, and multiple `elsif` clauses for additional conditions, forming a chain of checks. As a statement modifier, `if` can be appended to a single statement, such as `puts "Hello" if true`, which executes the statement only if the condition is truthy. One-liner forms with `then` allow compact writing, like `puts "Hello" if true then puts "World" end`, though the modifier form is more common for brevity.[](https://docs.ruby-lang.org/en/master/syntax/control_expressions_rdoc.html#label-if+Expression)
Ruby's notion of [truthiness](/page/Truthiness) is central to conditionals: only `false` and `nil` are considered falsy, while all other values—including `0`, empty strings, and empty collections—are [truthy](/page/Truthiness). This design promotes idiomatic code where conditions often test for the presence of a value rather than its exact content. For example:
```ruby
if user_input
puts "Input provided"
else
puts "No input"
end
The `<<-` variant allows indentation of the closing identifier for cleaner code alignment.[](https://docs.ruby-lang.org/en/master/String.html)
Common string operations include accessing the length with `str.length`, which returns the number of characters, such as `"hello".length` yielding `5`. Concatenation uses the `+` operator to join strings, producing a new instance like `"hello" + " world"` resulting in `"hello world"`, though repeated use can be inefficient for large builds due to immutability. For pattern-based substitution, `gsub` replaces all occurrences matching a pattern, e.g., `"hello".gsub(/e/, "a")` returns `"hallo"`. These methods support Ruby's mutable strings, allowing in-place modifications where applicable.[](https://docs.ruby-lang.org/en/master/String.html#method-i-length)[](https://docs.ruby-lang.org/en/master/String.html#method-i-2B)[](https://docs.ruby-lang.org/en/master/String.html#method-i-gsub)
### Symbols
In Ruby, symbols are immutable objects that serve as lightweight identifiers, representing names or keys within the interpreter. Unlike strings, which are mutable sequences of characters, symbols are designed for efficiency in scenarios where the same identifier is reused frequently, such as in method names or hash keys. They are created as unique instances, meaning that identical symbols always refer to the same object in memory throughout a program's execution, which conserves resources by avoiding redundant allocations.[](https://docs.ruby-lang.org/en/master/Symbol.html)
The primary syntax for creating symbols is the colon prefix followed by a name, such as `:name` for simple identifiers composed of alphanumeric characters and underscores. For dynamic or interpolated symbols, double quotes allow embedding expressions, as in `:"dynamic#{variable}"`, while single quotes disable interpolation to treat content literally, resulting in `:'dynamic\#{variable}'`. Alternative literal forms include the `%s` construct, like `%s[identifier]` for a single symbol or `%i[foo bar]` for an array of symbols without interpolation, and `%I[foo bar]` with interpolation enabled. These literals generate symbols that are immediately available for use.[](https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html)
Internally, symbols are stored as a single, immutable object with a fixed `object_id`, ensuring that `:example.object_id` yields the same value every time, regardless of creation context—this constancy promotes performance by enabling fast lookups and comparisons via object identity rather than content evaluation. For instance, multiple references to `:to_s` share the identical object, reducing memory overhead compared to equivalent strings. Symbols are particularly common in hashes as keys due to their hash-friendly uniqueness and immutability, though full hash details are covered elsewhere; they also facilitate method references, as in `method(:to_s)`, which retrieves the `to_s` method as a `Method` object.[](https://docs.ruby-lang.org/en/master/Symbol.html)
Conversion between symbols and strings is straightforward and bidirectional. The `to_s` method on a symbol returns its string representation, such as `:name.to_s` yielding `"name"`, allowing seamless integration with string-based APIs. Conversely, strings can be converted to symbols using `to_sym` or its alias `intern`, where `"name".to_sym` produces `:name`; if the receiver is already a symbol, `intern` returns `self` unchanged. These methods enable flexible data interchange while preserving the efficiency benefits of symbols where appropriate.[](https://docs.ruby-lang.org/en/master/Symbol.html#method-i-to_s)[](https://docs.ruby-lang.org/en/master/Symbol.html#method-i-intern)
## Collections and Data Structures
### Arrays
In Ruby, arrays are ordered collections of objects that can hold elements of any type, including mixed data such as numbers, strings, and symbols.[](https://docs.ruby-lang.org/en/master/Array.html) They are created using literal syntax enclosed in square brackets, where elements are separated by commas; for example, `[1, 'a', :b]` defines an array with an integer, a string, and a symbol as elements.[](https://docs.ruby-lang.org/en/master/Array.html) An empty array is represented simply as `[]`.[](https://docs.ruby-lang.org/en/master/Array.html) Arrays can also be constructed using `Array.new(size)` to preallocate space, optionally with a block to initialize elements, such as `Array.new(3) { |i| i * 2 }` which yields `[0, 2, 4]`.[](https://docs.ruby-lang.org/en/master/Array.html)
Elements in an array are accessed via zero-based integer indexing using the `[]` method; for instance, given `arr = [1, 2, 3, 4]`, `arr[0]` returns `1` and `arr[-1]` retrieves the last element `4` using negative indices that count from the end.[](https://docs.ruby-lang.org/en/master/Array.html) Slices allow extraction of subarrays with ranges or start-length pairs: `arr[1..3]` produces `[2, 3, 4]`, while `arr[1, 2]` yields `[2, 3]`.[](https://docs.ruby-lang.org/en/master/Array.html) Assignment to indices or slices is also supported, enabling modification like `arr[1] = 'x'` to change the second element.[](https://docs.ruby-lang.org/en/master/Array.html)
Arrays support various manipulation methods for adding and removing elements. The `push` method appends one or more elements to the end, as in `arr.push(5)` updating `arr` to `[1, 2, 3, 4, 5]`, while `pop` removes and returns the last element.[](https://docs.ruby-lang.org/en/master/Array.html) Conversely, `unshift` adds elements to the beginning, and `shift` removes the first one; for example, `arr.unshift(0)` prepends `0`.[](https://docs.ruby-lang.org/en/master/Array.html) The `length` or `size` method returns the number of elements, such as `arr.length` yielding `5`.[](https://docs.ruby-lang.org/en/master/Array.html) For iteration, methods like `each` and `map` provide previews of traversing arrays, allowing element-by-element processing without delving into block definitions here.[](https://docs.ruby-lang.org/en/master/Array.html)
Multidimensional arrays are formed by nesting arrays as elements, creating structures like matrices; for example, `[[1, 2], [3, 4]]` represents a 2x2 array accessible via `matrix[0][1]` to get `2`.[](https://docs.ruby-lang.org/en/master/Array.html) Care must be taken with initialization to avoid shared references, using blocks like `Array.new(3) { Array.new(3) }` for distinct inner arrays.[](https://docs.ruby-lang.org/en/master/Array.html)
### Hashes
In Ruby, a hash is an ordered collection of key-value pairs that preserves insertion order, where each unique key maps to a specific value, providing a dictionary-like data structure for associating data. Hashes are created using literal syntax enclosed in curly braces, allowing for immediate initialization with key-value pairs separated by commas and using the rocket operator (=>) or, for symbol keys, a colon (:). For example, the following creates a hash with symbol keys: `{ :a => 1, :b => 2 }`, which is equivalent to the more concise form `{ a: 1, b: 2 }`. String keys can also be used, as in `{ 'a' => 1, 'b' => 2 }`, and mixed key types are permitted within the same hash, though consistency is recommended for clarity.[](https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Hash+Data+Syntax)
Accessing values in a hash is typically done using square bracket notation with the key, such as `hash[:key]`, which returns the associated value or `nil` if the key is absent. For safer retrieval that raises a `KeyError` on missing keys, the `fetch` method is employed: `hash.fetch(:key)`. Hashes support default values to handle missing keys gracefully; for instance, `Hash.new(0)` creates a hash that returns 0 for any undefined key, allowing operations like `h[:missing] += 1` without explicit checks.[](https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Hash+Value+Basics)[](https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Any-Key+Default)
To add or update entries, assignment via square brackets is used: `hash[:new_key] = value`, which inserts the pair if the key does not exist or overwrites the value if it does. For combining hashes, the `merge` method creates a new hash incorporating pairs from another: `hash.merge(other_hash)`, with later keys overriding earlier ones in conflicts; a destructive variant `merge!` modifies the original. Deletion of a key-value pair is achieved with `hash.delete(:key)`, which removes the entry and returns the deleted value or `nil` if the key was not present.[](https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Methods+for+Assigning)[](https://docs.ruby-lang.org/en/master/Hash.html#class-Hash-label-Methods+for+Deleting)
Hashes provide methods to retrieve collections of keys or values as arrays, facilitating inspection without iteration. The `keys` method returns an array of all keys: for `h = { a: 1, b: 2 }`, `h.keys` yields `[:a, :b]`. Similarly, `values` returns the array of values: `h.values` yields `[1, 2]`. Symbols are preferred as hash keys in Ruby due to their immutable nature and efficient hashing, promoting consistent and performant code.[](https://docs.ruby-lang.org/en/master/Hash.html#method-i-keys)[](https://docs.ruby-lang.org/en/master/Hash.html#method-i-values)
### Ranges and Enumerables
In [Ruby](/page/Ruby), ranges represent intervals of values between a beginning and an end point, serving as a concise way to express sequences or spans. They can be created using literal [syntax](/page/Hungarian_noun_phrase) with two or three dots: an inclusive range includes the end value, denoted by `start..end`, while an exclusive range excludes it, denoted by `start...end`. For example, `(1..5)` encompasses the integers 1 through 5, whereas `(1...5)` includes only 1 through 4.[](https://docs.ruby-lang.org/en/master/syntax/literals_rdoc.html)[](https://docs.ruby-lang.org/en/master/Range.html)
Ranges support conversion to arrays via the `to_a` method, which generates an [array](/page/ARRAY) of all elements within the range. For instance, `(1..3).to_a` yields `[1, 2, 3]`. Basic operations include `include?`, which checks membership—`(1..5).include?(3)` returns `true`—and `size`, which computes the number of elements, returning `3` for `(1..3)`.[](https://docs.ruby-lang.org/en/master/Range.html#method-i-to_a)[](https://docs.ruby-lang.org/en/master/Range.html#method-i-include-3F)[](https://docs.ruby-lang.org/en/master/Range.html#method-i-size)
The `step` method enables custom increments, producing a stepped sequence from the range. For example, `(1..10).step(2)` advances by 2, covering odd or even numbers as appropriate.[](https://docs.ruby-lang.org/en/master/Range.html#method-i-step)
The Enumerable module is a [mixin](/page/Mixin) that equips collection classes, including [Array](/page/Array), Hash, and Range, with higher-level methods for querying, searching, and transforming elements, provided the class implements an `each` method to yield successive members.[](https://docs.ruby-lang.org/en/master/Enumerable.html) This allows uniform behavior across collections; for instance, ranges can use Enumerable methods like `map` to apply a transformation to each element, returning an array of results—`(1..3).map { |x| x * 2 }` produces `[2, 4, 6]`—or `select` to filter elements based on a condition, such as `(1..5).select { |x| x.even? }` yielding `[2, 4]`.[](https://docs.ruby-lang.org/en/master/Enumerable.html#method-i-map)[](https://docs.ruby-lang.org/en/master/Enumerable.html#method-i-select)
## Control Flow
### Conditionals
In Ruby, conditional statements allow programs to execute different code paths based on the evaluation of expressions. The primary constructs are the `if` expression, its inverse `unless`, the ternary operator, and the `case` expression for multi-way branching. These are control expressions that return the value of the last evaluated statement in the chosen branch, enabling them to be used in assignments or as part of larger expressions.[](https://docs.ruby-lang.org/en/master/syntax/control_expressions_rdoc.html)
The `if` expression evaluates a test condition and executes the associated body if the condition is truthy. Its basic syntax is `if test then body end`, where the `then` keyword is optional and separates the condition from the body; without it, a newline or semicolon acts as the delimiter. For multi-line bodies, the `end` keyword is required to close the block. An `if` can include an `else` clause for the falsy case, and multiple `elsif` clauses for additional conditions, forming a chain of checks. As a statement modifier, `if` can be appended to a single statement, such as `puts "Hello" if true`, which executes the statement only if the condition is truthy. One-liner forms with `then` allow compact writing, like `puts "Hello" if true then puts "World" end`, though the modifier form is more common for brevity.[](https://docs.ruby-lang.org/en/master/syntax/control_expressions_rdoc.html#label-if+Expression)
Ruby's notion of [truthiness](/page/Truthiness) is central to conditionals: only `false` and `nil` are considered falsy, while all other values—including `0`, empty strings, and empty collections—are [truthy](/page/Truthiness). This design promotes idiomatic code where conditions often test for the presence of a value rather than its exact content. For example:
```ruby
if user_input
puts "Input provided"
else
puts "No input"
end
if branch executes unless user_input is false or nil.[14]
The unless expression inverts the logic of if, executing its body when the condition is falsy. Its syntax mirrors if: unless test then body end, supporting else but not elsif. As a modifier, it appears as body unless test, such as puts "Not empty" unless array.empty?. This construct is useful for expressing negative conditions directly, avoiding double negatives in if statements. For instance:
unless error_occurred
process_data
else
log_error
end
unless error_occurred
process_data
else
log_error
end
error_occurred is falsy.[15]
For simple binary choices, Ruby provides the ternary operator as a concise alternative to if-else: condition ? true_expression : false_expression. It evaluates the condition and returns the true_expression if truthy, otherwise the false_expression. This operator is recommended only for straightforward cases to maintain readability, as nesting multiple ternaries can obscure intent. An example is:
status = age >= 18 ? "adult" : "minor"
status = age >= 18 ? "adult" : "minor"
age >= 18 is truthy, else "minor". Unlike full if expressions, the ternary cannot include multi-line blocks without parentheses.[16]
The case expression handles multi-way branching more elegantly than chained if-elsif statements. It begins with case expression (or just case for condition-based matching) followed by when clauses and an optional else. Each when specifies patterns or conditions, and the first matching clause executes its body. Matching uses the === operator, which supports equality (==), range inclusion, regex matching, and class checks. The then keyword can separate single-line bodies, and since Ruby 2.7, in enables pattern matching for destructuring. Without an initial expression, when clauses act like conditions. For example:
case input
when "yes", "y"
puts "Affirmative"
when /^no/i
puts "Negative"
else
puts "Unclear"
end
case input
when "yes", "y"
puts "Affirmative"
when /^no/i
puts "Negative"
else
puts "Unclear"
end
else handles non-matches. The case returns the value of the last statement in the selected branch.[17]
Loops and Iteration
Ruby provides several constructs for loops and iteration, enabling repetitive execution of code blocks based on conditions or over collections. These include conditional loops likewhile and until, as well as the for loop for iterating over enumerables. Unlike many languages, Ruby lacks pre- and post-increment/decrement operators (e.g., ++ or --), which can lead to common pitfalls in loop counters where developers must manually update variables, potentially causing off-by-one errors or infinite loops if forgotten.
The while loop executes a block of code as long as a given condition evaluates to true. Its basic syntax is while condition do ... end, where the condition is checked before each iteration; if false initially, the loop body is skipped entirely. A do-while variant, begin ... end while condition, evaluates the condition after the body, ensuring at least one execution regardless of the initial condition. Additionally, Ruby supports postfix modifiers, allowing a single statement to be followed by while condition, such as x += 1 while x < 10, which increments x repeatedly until the condition fails. Infinite loops can be created explicitly with while true do ... end, often used in event-driven programming but requiring manual termination via break to avoid hangs.
The until loop functions as the inverse of while, repeating the body until a condition becomes true (i.e., while the condition is false). It shares the same syntax variations: until condition do ... end for pre-check, begin ... end until condition for post-check, and postfix modifiers like x += 1 until x >= 10. This construct is useful for scenarios where continuation depends on a falsy state, such as waiting for a resource to become available. Like while, until can form infinite loops with until false, though this is less idiomatic than while true.
The for loop provides a concise way to iterate over an enumerable object, assigning each element to a variable in sequence. Its syntax is for variable in enumerable do ... end, which is largely syntactic sugar for enumerable.each do |variable| ... end, offering identical functionality but without introducing an explicit block. For instance, for i in 1..5 do puts i end iterates over the range, printing numbers 1 through 5; ranges or arrays can serve as the enumerable. The loop respects the enumerable's order and stops at exhaustion, with no built-in index tracking unless the variable is structured accordingly (e.g., using with_index).
Loop control is managed through keywords like break, next, and redo. The break keyword immediately exits the innermost loop, optionally with a value that propagates to the caller if in a method or block; for example, while condition do break if escape_cond end halts on escape_cond. next skips the remainder of the current iteration and advances to the next, useful for filtering without full body execution, as in for item in collection do next unless valid?(item); process(item) end. redo restarts the current iteration from the beginning, re-evaluating the condition but without incrementing counters, which can create tight loops for retries but risks infinity if not conditioned properly. These keywords apply uniformly to while, until, and for loops, enhancing flexibility without altering the core repetition logic.
Blocks and Closures
Defining Blocks
In Ruby, blocks are anonymous chunks of code that can be passed to methods or executed in a specific context, serving as closures that capture the surrounding lexical environment. They are defined using either curly braces{} for single-line or compact expressions or the do...end keywords for multi-line blocks, with the latter having lower precedence in method calls.[18]
The syntax for a basic block begins with an optional parameter list enclosed in vertical bars | | immediately after the opening brace or do, followed by the code body. For instance, a simple block without parameters might appear as { puts "Hello" } or do puts "Hello"; end, though the former is conventional for brevity. When parameters are needed, they are specified within the bars, such as { |x| x * 2 }, allowing the block to receive values yielded from the invoking method. Multiple parameters are separated by commas, like { |x, y| x + y }.[18]
Since Ruby 3.4, an implicit block parameter named it is available for single-argument blocks, providing a concise way to refer to the yielded value without explicit naming. For example, [1, 2, 3].map { it * 2 } returns [2, 4, 6], equivalent to { |x| x * 2 }. This feature enhances readability for simple transformations but requires explicit parameters for multiple arguments.[19]
To handle variable numbers of arguments, Ruby supports the splat operator * within block parameters, enabling an array to be unpacked or collected, as in { |*args| args.sum }. This mirrors array splatting in method arguments and facilitates flexible iteration over collections. Additionally, to prevent shadowing of outer variables, block-local variables can be declared after a semicolon in the parameter list, for example { |obj; local| local = obj.upcase }, where local remains confined to the block's scope even if it shares a name with an external variable.[18]
Blocks can be explicitly converted into reusable Proc objects using Proc.new or the shorthand proc, both of which encapsulate the block for later invocation via call. For example, double = proc { |x| x * 2 } creates a proc that doubles its input. In contrast, lambdas are created with lambda or the arrow syntax ->, such as double_lambda = ->(x) { x * 2 }, and enforce stricter argument checking: they raise an ArgumentError if the number of provided arguments does not match the declared parameters, whereas regular procs are more lenient, assigning nil to missing arguments and ignoring extras. This distinction makes lambdas behave more like functions with rigid signatures, while procs offer greater flexibility akin to true blocks.[20]
Iterators and Yield
In Ruby, iterators are methods that invoke a block of code multiple times, typically to process collections or sequences, by using theyield keyword to pass values to the block. The yield keyword, when called within a method, executes the block passed to that method and can optionally pass arguments to it, with the syntax yield arg1, *args allowing zero or more values to be supplied. For instance, a custom method might use yield to provide data to the block:
def process_data(data)
yield data
end
process_data("example") { |d| puts d } # Outputs: example
def process_data(data)
yield data
end
process_data("example") { |d| puts d } # Outputs: example
yield to simplify common looping patterns without explicit loops. The each method, defined in the Enumerable module and implemented by classes like Array or Hash, yields each element to the block sequentially. For example, on an array:
[1, 2, 3].each { |num| puts num } # Outputs: 1 2 3
[1, 2, 3].each { |num| puts num } # Outputs: 1 2 3
times, which yields the block n times for an integer n, passing indices from 0 to n-1:
5.times { |i| puts i } # Outputs: 0 1 2 3 4
5.times { |i| puts i } # Outputs: 0 1 2 3 4
upto(limit) yields integers from the receiver up to the limit:
3.upto(5) { |i| puts i } # Outputs: 3 4 5
3.upto(5) { |i| puts i } # Outputs: 3 4 5
yield expression, allowing the method to capture and potentially use it, but block completion does not inherently exit the method—execution resumes after yield unless an explicit return statement is used within the method body. For example:
def example
result = yield 42
"Method continues: #{result}"
end
example { |x| x * 2 } # Returns: "Method continues: 84"
def example
result = yield 42
"Method continues: #{result}"
end
example { |x| x * 2 } # Returns: "Method continues: 84"
return statement (in a non-lambda block), it performs a non-local return, exiting the enclosing method immediately.[25][20]
The Enumerable module provides higher-level iterators like collect (aliased as map), which yields each element to the block and collects the block's return values into a new array, enabling transformations:
[1, 2, 3].collect { |num| num * 2 } # Returns: [2, 4, 6]
[1, 2, 3].collect { |num| num * 2 } # Returns: [2, 4, 6]
inject (aliased as reduce) uses the block to accumulate a computation, yielding an accumulating memo and each element:
[1, 2, 3].inject(0) { |sum, num| sum + num } # Returns: 6
[1, 2, 3].inject(0) { |sum, num| sum + num } # Returns: 6
Methods
Defining Methods
In Ruby, methods are defined using thedef keyword followed by the method name, an optional parameter list in parentheses, the method body, and the end keyword to close the definition.[21] Since Ruby 3.0, a shorthand syntax is available for single-expression methods: def method_name = expression. For example:
def add(a, b)
a + b
end
def add_shorthand(a, b) = a + b
def add(a, b)
a + b
end
def add_shorthand(a, b) = a + b
+ or [].[21] For example, a simple method to add two numbers is written as shown above. This defines an instance method when placed inside a class or module, or a singleton method when prefixed with a receiver like def obj.method.[21]
Ruby supports various parameter types to handle different argument passing needs. Required positional parameters are listed first without defaults, such as def greet(name, age). Optional parameters follow with default values, like def greet(name, age = 18), where the default applies if the argument is omitted; defaults must be grouped after required ones to avoid syntax errors.[21] Splat parameters capture extra positional arguments into an array using *args, enabling variable-length inputs, as in def sum(*numbers); numbers.sum; end. Argument forwarding using ... delegates all remaining positional, keyword, and block arguments to a super method or another call; it has been available since Ruby 2.7 and since Ruby 3.0 allows leading arguments before ... in definitions and invocations, e.g., def wrapper(pre, ...); super(pre, ...); end.[21] Keyword parameters allow named arguments with defaults, such as def configure(host: 'localhost', port: 8080), and can be required without defaults like user:. Double splat parameters **opts collect remaining keyword arguments into a hash.[21] Methods can also accept a block parameter via &block, converting the passed block into a Proc object for later invocation.[21]
Return values in Ruby methods are determined implicitly by the value of the last expression evaluated in the body, or explicitly using the return keyword for early exits or multiple values. For instance, in def factorial(n); n == 0 ? 1 : n * factorial(n - 1); end, the recursive call provides the implicit return. Assignment methods, named with an equals sign like def name=(value); @name = value; end, automatically return the assigned value.[21]
Method visibility controls accessibility and is managed within class or module definitions using public, private, and protected directives, which affect subsequently defined methods or specific ones when passed as symbols. Public is the default, allowing calls from any object without restrictions. Private methods are callable only without an explicit receiver or with [self](/page/Self), restricting them to the defining class's internal use, as in private def helper; end. Protected methods permit calls within the class and its subclasses, but only when the receiver is [self](/page/Self) or an instance of the same or subclass type, useful for comparisons like in the Comparable module. These modifiers can be invoked as private :method_name to apply to existing methods.[28]
Method Invocation and Arguments
In Ruby, methods are invoked by sending a message to an object, typically using dot notation to specify the receiver. For instance, to call a method namedgreet on an object person, the syntax is person.greet, where person acts as the receiver and self serves as the implicit receiver if no explicit object is provided.[18] Parentheses around arguments are optional for simple calls but are recommended for clarity, especially in chained expressions; for example, array.length is equivalent to array.length().[18]
Arguments to methods can be passed positionally, by keyword, or via splatting mechanisms. Positional arguments are supplied in a comma-separated list matching the method's expected order, such as add(2, 3) for a method that sums two numbers.[18] Keyword arguments, introduced for more readable and flexible calls, use the syntax method(key: value), allowing arguments in any order and providing defaults if omitted; for example, draw(shape: 'circle', color: 'red').[18] Since Ruby 3.0, positional and keyword arguments are strictly separated, preventing implicit conversion of trailing hashes to keywords unless explicitly splatted.[29]
To pass multiple values dynamically, the splat operator * unpacks an array into positional arguments, as in method(*[1, 2, 3]) which expands to method(1, 2, 3).[18] For keyword arguments from a hash, the double splat ** is used, such as method(**{key: 'value'}).[18] Blocks, which are anonymous code closures, can be passed to methods using do ... end or the more concise { ... } syntax; to pass a Proc object as a block, prefix it with &, like method(&my_proc).[18] This enables methods to yield control to the block during execution.
Method chaining combines multiple invocations using dot notation, relying on methods that return objects to facilitate fluent interfaces, for example, string.strip.downcase.reverse.[18] The safe navigation operator &., introduced in Ruby 2.3, prevents errors when chaining on potentially nil receivers by returning nil if the receiver is nil without invoking the method, as in user&.name&.upcase.[18][30]
For dynamic invocation, the send method on Object allows calling a method by name as a symbol, bypassing some visibility restrictions, with syntax obj.send(:method_name, arg1, key: value, &block).[31] This is useful in metaprogramming scenarios where the method name is determined at runtime, such as array.send(:push, item).[31]
Object-Oriented Features
Classes and Objects
Ruby's object-oriented paradigm is built around classes, which define the structure and behavior for objects, with every value in the language being an instance of a class. Classes encapsulate instance variables for state and methods for operations, promoting data abstraction and modularity. This design draws from Smalltalk influences, emphasizing that classes themselves are objects of the Class class.[28][32] A class is defined using theclass keyword followed by the class name, with the body enclosed in an end block; by default, classes inherit from Object, but explicit inheritance from a superclass is denoted with the < operator. For example:
class MyClass
# Class body
end
class SubClass < MyClass
# Inherits from MyClass
end
class MyClass
# Class body
end
class SubClass < MyClass
# Inherits from MyClass
end
super. Objects are created by calling the new method on the class, which returns an instance of that class. Within class definitions, the self keyword refers to the class itself, whereas in instance methods, it refers to the receiving object, enabling access to instance state or class-level operations.[28][21]
Instance variables, denoted by a leading @ symbol (e.g., @name), store per-object data and are automatically available across methods within the instance scope. To expose these variables safely, Ruby provides built-in accessor methods: attr_reader generates a read-only getter, attr_writer creates a setter, and attr_accessor combines both. For instance:
class Person
attr_accessor :name # Generates name and name= methods
def initialize(full_name)
@name = full_name # Instance variable assignment
end
end
person = Person.new("Alice")
person.name = "Bob" # Uses setter
puts person.name # Uses getter, outputs "Bob"
class Person
attr_accessor :name # Generates name and name= methods
def initialize(full_name)
@name = full_name # Instance variable assignment
end
end
person = Person.new("Alice")
person.name = "Bob" # Uses setter
puts person.name # Uses getter, outputs "Bob"
initialize method serves as the constructor, automatically invoked upon object creation with new and receiving any passed arguments to set initial state. It is a conventional instance method without a return value, ensuring objects are properly initialized before use.[28]
Modules and Mixins
In Ruby, modules serve as a mechanism for organizing code by grouping methods, constants, and classes, providing both namespacing and the ability to share functionality across classes without relying on single inheritance. They are defined using themodule keyword followed by the module name and a body enclosed in end, such as module ExampleModule; end. Unlike classes, modules cannot be instantiated directly but can contain constants, instance methods, and module methods.[33]
Modules enable namespacing to prevent naming conflicts by encapsulating related elements under a hierarchical scope, allowing access via the scope resolution operator ::, for example, ExampleModule::CONSTANT. This is particularly useful in large codebases to organize constants and classes logically, such as defining a nested class module Outer; class Inner; end; end and referencing it as Outer::Inner. Module methods, which behave like class methods, are defined using self. prefix within the module, such as def self.module_method; end, and invoked directly on the module, e.g., ExampleModule.module_method.[33][33]
A key feature of modules is their role as mixins, allowing code reuse by incorporating module methods into classes or other modules. The include method mixes in instance methods from the module to the including class's instances, enabling shared behavior without inheritance; for instance, class MyClass; include ExampleModule; end makes ExampleModule's instance methods available on MyClass objects. In contrast, extend adds the module's instance methods as singleton methods to the target object or class, applicable to individual instances like obj.extend(ExampleModule). This mixin approach provides a flexible alternative to multiple inheritance, as seen in core modules like Enumerable, which supplies iteration methods when included in collection classes. Classes can effectively inherit behavior from modules via include, simulating mixin-based extension.[33][33][33]
Open Classes
Ruby's open classes feature allows developers to reopen and extend existing classes at any time after their initial definition, enabling the addition or modification of methods and attributes without altering the original source code. This dynamic nature stems from Ruby's design philosophy, where classes are not sealed and can be redefined repeatedly, with each subsequent definition appending to or overriding the previous one.[34][35] To reopen a class, the same class keyword is used followed by the class name, as in the initial definition. For instance, the built-in String class can be extended to add a custom method:class [String](/page/String)
def reverse_words
split(' ').map(&:reverse).join(' ')
end
end
"hello world".reverse_words # => "olleh dlrow"
class [String](/page/String)
def reverse_words
split(' ').map(&:reverse).join(' ')
end
end
"hello world".reverse_words # => "olleh dlrow"
reverse_words method to all String instances globally. Similarly, core classes like Integer can be extended for domain-specific utilities, such as converting integers to time durations.[36][35]
Common use cases include enhancing built-in classes for application-specific needs, such as adding convenience methods to Array for data processing or to Numeric for unit conversions. In frameworks like Ruby on Rails, open classes are employed to extend Integer with time-related methods, like 5.minutes returning a duration object, improving readability in code for scheduling or timing operations.[35]
However, this flexibility introduces risks, particularly namespace pollution where added methods can collide with future library updates or other codebases, leading to unexpected behavior. Global modifications from monkey patching—reopening classes to alter core functionality—can cause version conflicts in multi-library environments, as changes affect all instances and propagate across the application, potentially breaking compatibility. To mitigate such issues, Ruby introduced refinements in version 2.0, but open classes remain a powerful yet cautious tool for extensions.[37]
A related concept is the eigenclass, also known as the singleton class, which provides instance-specific extensions without affecting the broader class. Defined using class << object, it allows adding methods unique to a single object:
porsche = Car.new
class << porsche
def honk
puts "Beep beep!"
end
end
porsche.honk # => "Beep beep!"
porsche = Car.new
class << porsche
def honk
puts "Beep beep!"
end
end
porsche.honk # => "Beep beep!"
Error Handling
Exceptions
In Ruby, exceptions are represented by theException class and its subclasses, which form a hierarchy to categorize different types of errors or exceptional conditions that may require handling. The root of this hierarchy is the Exception class itself, with major branches including StandardError, which serves as the base for most runtime errors that programmers are expected to handle, and other categories like ScriptError for compilation-time issues and SignalException for interrupt signals. RuntimeError is a direct subclass of StandardError and acts as the default exception type when raising an error with a simple message string. Common subclasses of StandardError include ArgumentError, raised for invalid arguments passed to methods, TypeError for type mismatches, and ZeroDivisionError for arithmetic operations involving division by zero.[38]
Exceptions are raised explicitly using the raise method (also aliased as fail), which is a kernel method available globally. The basic syntax is raise [exception[, message[, backtrace]]], where exception can be an exception class, an instance of an exception class, or omitted (defaulting to RuntimeError). For example, raise 'An error occurred' raises a RuntimeError with the provided string as its message. To specify a particular class, one can use raise ArgumentError, 'Invalid argument provided', which instantiates and raises an ArgumentError instance with the given message. If a backtrace array is provided as the third argument, it overrides the default call stack trace. This mechanism allows developers to signal errors from within method calls when conditions are not met, propagating the exception up the call stack unless handled.[39]
Custom exception classes can be defined by subclassing existing ones, typically StandardError for application-specific errors, to integrate seamlessly into the hierarchy. The syntax is straightforward: class MyCustomError < StandardError; end, after which instances can be raised as raise MyCustomError, 'Custom message'. This approach enables precise error categorization without altering Ruby's built-in behavior. For debugging raised exceptions, the global variable $! (or Exception) holds the most recent exception object, while $@ (or the exception's backtrace method) provides an array of strings representing the call stack at the point of raising, including file names, line numbers, and method names. The backtrace instance method on an Exception object returns this array directly, facilitating detailed error analysis.[40][41]
Rescue and Ensure
In Ruby, exception handling is managed through therescue and ensure clauses, which allow code to respond to errors and guarantee cleanup regardless of outcomes. The rescue clause catches specified exceptions raised within a preceding block, enabling recovery or alternative execution paths. It is typically used within a begin...end structure, where the begin block contains potentially error-prone code. For instance:
begin
# risky code here
rescue StandardError => e
# handle the exception
end
begin
# risky code here
rescue StandardError => e
# handle the exception
end
[rescue](/page/Rescue) without an exception type catches StandardError and its subclasses, but specific classes can be targeted for precise matching, such as rescue ZeroDivisionError => e to handle division-by-zero cases. Multiple [rescue](/page/Rescue) clauses can chain together to address different exception types in sequence, with the first matching clause executing. The exception object can be assigned to a variable (e.g., => e) for inspection, like logging e.message or e.backtrace.[42]
The ensure clause complements rescue by executing code unconditionally after the begin block, whether an exception occurred or not, making it ideal for resource cleanup such as closing files or connections. It follows any rescue or else clauses but does not alter the block's return value. A full structure might appear as:
begin
# main code
rescue SomeError => e
# error handling
ensure
# always-run cleanup
end
begin
# main code
rescue SomeError => e
# error handling
ensure
# always-run cleanup
end
ensure runs even if an exception propagates upward or the block returns normally.[42]
Ruby also supports an inline rescue modifier for concise, expression-level handling, where a default value or action follows a risky expression if it raises an exception. The syntax is expression [rescue](/page/Rescue) fallback, which evaluates to the fallback if the expression fails (defaulting to nil if unspecified). For example:
result = risky_operation [rescue](/page/Rescue) "default value"
result = risky_operation [rescue](/page/Rescue) "default value"
=) but functions as a modifier, so it requires parentheses for use in certain contexts like method arguments: puts((1 / 0 [rescue](/page/Rescue) 0)). It implicitly rescues StandardError subclasses and cannot target specific types directly.[43]
Within a [rescue](/page/Rescue) clause, the [retry](/page/2022–23_Florida_Atlantic_Owls_men's_basketball_team) keyword restarts execution of the enclosing begin block from the top, potentially allowing recovery from transient errors but risking infinite loops if the condition persists. It is valid only inside rescue blocks; elsewhere, it raises a SyntaxError. An example use might retry a network call after a timeout exception:
begin
# code that might fail
rescue TimeoutError
retry # restarts the begin block
end
begin
# code that might fail
rescue TimeoutError
retry # restarts the begin block
end
Advanced Syntax
Metaprogramming Basics
Metaprogramming in Ruby allows code to generate or modify other code at runtime, leveraging the language's dynamic nature to enable flexible and expressive programming techniques.[44] This capability builds on Ruby's open classes, which permit modifications to existing classes after their initial definition. Core techniques include dynamic method definition, interception of undefined method calls, evaluation of code in specific contexts, and method aliasing, all of which facilitate runtime introspection and adaptation without altering source code directly.[44] One fundamental metaprogramming tool isdefine_method, a method on Module that creates instance methods dynamically at runtime. Its syntax is define_method(symbol, method = nil, &block) → symbol, where symbol names the method, method can be a Proc, Method, or UnboundMethod, and a block provides the implementation if no method is given. For example:
class Example
define_method(:greet) { |name| "Hello, #{name}!" }
end
Example.new.greet("World") # => "Hello, World!"
class Example
define_method(:greet) { |name| "Hello, #{name}!" }
end
Example.new.greet("World") # => "Hello, World!"
def declarations by allowing method creation based on runtime conditions, such as loops or data-driven logic.[45]
The method_missing hook, defined on BasicObject and inherited by all objects, intercepts calls to undefined methods, enabling dynamic delegation or fallback behavior. Its signature is method_missing(name, *args, &block) → result, where name is the missing method's symbol, args are arguments, and block is an optional block. By default, it raises a NoMethodError, but overriding it allows custom handling, such as proxying to another object:
class Delegator
def initialize(target)
@target = target
end
def method_missing(name, *args, &block)
return super unless @target.respond_to?(name)
@target.send(name, *args, &block)
end
end
class Delegator
def initialize(target)
@target = target
end
def method_missing(name, *args, &block)
return super unless @target.respond_to?(name)
@target.send(name, *args, &block)
end
end
instance_eval on BasicObject evaluates a string or block as if executed within the receiver's instance scope, allowing access to instance variables and private methods. Syntax includes instance_eval(string [, filename [, lineno]]) → obj or instance_eval {|obj| block} → obj. Complementing this, class_eval on Module performs similar evaluation in the class or module context, enabling additions like new methods:
class Example
end
Example.class_eval do
def dynamic_method
"Added at runtime"
end
end
Example.new.dynamic_method # => "Added at runtime"
class Example
end
Example.class_eval do
def dynamic_method
"Added at runtime"
end
end
Example.new.dynamic_method # => "Added at runtime"
alias_method on Module renames an existing method, creating an alias without duplicating code. Its syntax is alias_method(new_name, old_name) → self, where both names are symbols. For instance:
class Example
def original
"original"
end
alias_method :aliased, :original
end
Example.new.aliased # => "original"
class Example
def original
"original"
end
alias_method :aliased, :original
end
Example.new.aliased # => "original"
Dynamic Code Evaluation
Ruby's dynamic code evaluation allows the execution of arbitrary code at runtime, primarily through theKernel#eval method, which interprets a string as Ruby source code within a specified context. The syntax is eval(string [, binding [, filename [, lineno]]), where string contains the code to evaluate, binding is an optional Binding object defining the local scope and self, filename sets the reported file name for errors (defaulting to '(eval)'), and lineno specifies the starting line number (defaulting to 1). For example:
eval("1 + 1") # => 2
eval("1 + 1") # => 2
Binding class encapsulates the execution context, including local variables, self, and the method call stack, enabling controlled evaluation scopes. A Binding object is created using Kernel#binding, which captures the context at the call site. Passing this to eval restricts execution to that environment:
def outer
x = 10
binding # Returns a Binding object
end
b = outer
eval("x + 5", b) # => 15, uses outer's scope
def outer
x = 10
binding # Returns a Binding object
end
b = outer
eval("x + 5", b) # => 15, uses outer's scope
eval uses the caller's context, but bindings prevent unintended variable access or modifications.[51]
For object-specific execution without strings, BasicObject#instance_exec and Module#class_exec invoke blocks as if defined on the receiver, passing arguments to the block parameters. The syntax for instance_exec is instance_exec(arg...) { |var...| block }, evaluating the block with self as the instance:
class Example
def initialize(value)
@value = value
end
end
obj = Example.new(42)
obj.instance_exec(10) { |add| @value + add } # => 52
class Example
def initialize(value)
@value = value
end
end
obj = Example.new(42)
obj.instance_exec(10) { |add| @value + add } # => 52
class_exec uses class_exec(arg...) { |var...| block } for class or module contexts, where methods defined in the block become class methods:
class Example
end
Example.class_exec { def self.static_method; "class level"; end }
Example.static_method # => "class level"
class Example
end
Example.class_exec { def self.static_method; "class level"; end }
Example.static_method # => "class level"
eval and string-based evaluation pose significant security risks, particularly when processing untrusted input, as they can lead to arbitrary code execution, such as file system access or data leaks via injection attacks. For instance, evaluating user-supplied strings like system("rm -rf /") could compromise the system. Official guidance advises against passing untrusted data to eval, noting that input sanitization is unreliable and often insufficient to prevent exploits. Safer alternatives include Object#instance_eval with strings for scoped execution, which limits the context to a specific object while still evaluating code:
obj.instance_eval("def greet; 'hello'; end")
obj.greet # => "hello"
obj.instance_eval("def greet; 'hello'; end")
obj.greet # => "hello"
eval, though strings remain risky for untrusted sources; block-based instance_eval or instance_exec is recommended for trusted, static code. Bindings further enhance safety by confining scope. These features are often used in metaprogramming to build domain-specific languages (DSLs) by dynamically injecting controlled code.[54][55]