Hubbry Logo
Ruby syntaxRuby syntaxMain
Open search
Ruby syntax
Community hub
Ruby syntax
logo
7 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Ruby syntax
Ruby syntax
from Wikipedia

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]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
Ruby syntax refers to the formal rules and conventions that define the structure of programs written in the Ruby programming language, a dynamic, open-source scripting language created by Yukihiro "Matz" Matsumoto and first released in 1995. Designed with influences from Perl, Smalltalk, Eiffel, Ada, and Lisp, it prioritizes simplicity, readability, and programmer productivity through an elegant, expressive form that is intuitive and human-centric. This syntax enables concise code with minimal punctuation, English-like keywords, and flexible constructs that make Ruby natural to read and easy to write. At its core, Ruby syntax is object-oriented, treating everything as an object, which allows methods to be called on primitives like numbers and strings (e.g., 5.times { print "We have " + 5.to_s + " lights" }). 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). 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. Methods are defined with def and can accept blocks for iteration or callbacks, supporting and refinements for extending behavior without modifying originals. Modules and classes use module and class keywords for organization and inheritance, while employs begin, rescue, ensure, and raise. Notable modern features include , 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. Overall, Ruby's syntax balances power and brevity, with operator precedence following a defined and comments starting with # to enhance maintainability.

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. 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. 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. To begin an irb session, users invoke the irb command from or command prompt, assuming is installed and accessible in the system's PATH. 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"

More complex expressions, like File.basename(Dir.pwd), yield results such as the current directory name, printed as => "example_dir". To end the session, users type exit or press Ctrl+D (on Unix-like systems). 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. 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"]. 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.

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. Assignment to a variable implicitly creates it if it does not exist, with the type inferred from the assigned value. For example, the statement x = 5 declares and initializes a local variable x that references the integer object 5. 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. 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. 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. Global variables, starting with $, have program-wide scope and are accessible from any , such as $global_var = "global", though their use is discouraged due to potential side effects. 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. 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. These literals are written in lowercase and require no additional syntax, such as if true then puts "condition met" end for basic usage. 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. They are scoped to the class or module where defined, accessible to nested scopes, outer scopes, and superclasses, or via the :: (e.g., Math::PI). While Ruby does not enforce immutability, reassigning a constant triggers a runtime warning, promoting the convention of treating them as fixed.

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. Floats use the native double-precision representation for approximate real numbers. 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. 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. Basic arithmetic uses operators + (), - (), * (), / (division), and % (), which are implemented as methods on the classes. For integers, division truncates toward zero (e.g., 5 / 2 yields 2), while modulo follows the sign of the (e.g., 5 % 2 is 1, -5 % 2 is -1). 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).

ruby

# 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:

sql = <<-SQL SELECT * FROM users WHERE id = #{user_id} SQL

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

Here, the if branch executes unless user_input is false or nil. 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:

ruby

unless error_occurred process_data else log_error end

unless error_occurred process_data else log_error end

The body runs if error_occurred is falsy. 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 , as nesting multiple ternaries can obscure intent. An example is:

ruby

status = age >= 18 ? "adult" : "minor"

status = age >= 18 ? "adult" : "minor"

This assigns "adult" if age >= 18 is truthy, else "minor". Unlike full if expressions, the ternary cannot include multi-line blocks without parentheses. 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:

ruby

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

This matches "yes" or "y" exactly, or strings starting with "no" (case-insensitive) via regex, executing the corresponding body; the else handles non-matches. The case returns the value of the last statement in the selected branch.

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 like while 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 executes a block of 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 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 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. 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 }. 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. To handle variable numbers of arguments, Ruby supports the splat operator * within block , enabling an to be unpacked or collected, as in { |*args| args.sum }. This mirrors array splatting in method arguments and facilitates flexible over collections. Additionally, to prevent shadowing of outer variables, block-local variables can be declared after a 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. 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, 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.

Iterators and Yield

In Ruby, iterators are methods that invoke a block of code multiple times, typically to process collections or sequences, by using the yield 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:

ruby

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

This mechanism enables flexible, functional-style programming where the block defines the operation, and the method handles iteration logic. Built-in iterators leverage 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:

ruby

[1, 2, 3].each { |num| puts num } # Outputs: 1 2 3

[1, 2, 3].each { |num| puts num } # Outputs: 1 2 3

Integer-specific iterators include times, which yields the block n times for an integer n, passing indices from 0 to n-1:

ruby

5.times { |i| puts i } # Outputs: 0 1 2 3 4

5.times { |i| puts i } # Outputs: 0 1 2 3 4

Similarly, upto(limit) yields integers from the receiver up to the limit:

ruby

3.upto(5) { |i| puts i } # Outputs: 3 4 5

3.upto(5) { |i| puts i } # Outputs: 3 4 5

These methods return the receiver object after iteration, promoting method chaining. Regarding return behavior, the value returned by the block execution becomes the value of the corresponding 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:

ruby

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"

If the block contains a return statement (in a non-lambda block), it performs a non-local return, exiting the enclosing method immediately. 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:

ruby

[1, 2, 3].collect { |num| num * 2 } # Returns: [2, 4, 6]

[1, 2, 3].collect { |num| num * 2 } # Returns: [2, 4, 6]

Likewise, inject (aliased as reduce) uses the block to accumulate a computation, yielding an accumulating memo and each element:

ruby

[1, 2, 3].inject(0) { |sum, num| sum + num } # Returns: 6

[1, 2, 3].inject(0) { |sum, num| sum + num } # Returns: 6

These functional iterators emphasize immutability and composition, returning new values without modifying the original collection.

Methods

Defining Methods

In Ruby, methods are defined using the def keyword followed by the method name, an optional parameter list in parentheses, the method body, and the end keyword to close the definition. Since Ruby 3.0, a shorthand syntax is available for single-expression methods: def method_name = expression. For example:

ruby

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

The method name must be a valid identifier, typically in snake_case, and can include special characters for operators like + or []. 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. Ruby supports various 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 errors. Splat parameters capture extra positional arguments into an 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. 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. Methods can also accept a block parameter via &block, converting the passed block into a Proc object for later invocation. 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. 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. 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.

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 named greet 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. 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(). 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. 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'). Since Ruby 3.0, positional and keyword arguments are strictly separated, preventing implicit conversion of trailing hashes to keywords unless explicitly splatted. To pass multiple values dynamically, the splat operator * unpacks an into positional arguments, as in method(*[1, 2, 3]) which expands to method(1, 2, 3). For keyword arguments from a hash, the double splat ** is used, such as method(**{key: 'value'}). 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). 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. 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. 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). This is useful in metaprogramming scenarios where the method name is determined at runtime, such as array.send(:push, item).

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 . This design draws from Smalltalk influences, emphasizing that classes themselves are objects of the Class class. A class is defined using the class 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:

ruby

class MyClass # Class body end class SubClass < MyClass # Inherits from MyClass end

class MyClass # Class body end class SubClass < MyClass # Inherits from MyClass end

Subclasses inherit methods and instance variables from their superclass, allowing method overriding while invoking the parent implementation via 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. 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:

ruby

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"

The 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.

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 the module 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. 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. 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.

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. 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 class can be extended to add a custom method:

ruby

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"

This appends the reverse_words method to all instances globally. Similarly, core classes like can be extended for domain-specific utilities, such as converting integers to time durations. Common use cases include enhancing built-in classes for application-specific needs, such as adding convenience methods to for or to Numeric for unit conversions. In frameworks like , open classes are employed to extend with time-related methods, like 5.minutes returning a duration object, improving in for scheduling or timing operations. 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 , but open classes remain a powerful yet cautious tool for extensions. 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:

ruby

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!"

This creates an anonymous class for the object, enabling targeted modifications while preserving the open class paradigm for shared extensions.

Error Handling

Exceptions

In Ruby, exceptions are represented by the Exception class and its subclasses, which form a 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 . 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. Custom exception classes can be defined by subclassing existing ones, typically StandardError for application-specific errors, to integrate seamlessly into the . 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.

Rescue and Ensure

In Ruby, exception handling is managed through the rescue 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:

ruby

begin # risky code here rescue StandardError => e # handle the exception end

begin # risky code here rescue StandardError => e # handle the exception end

By default, [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. 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:

ruby

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

This ensures deterministic behavior, as ensure runs even if an exception propagates upward or the block returns normally. 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:

ruby

result = risky_operation [rescue](/page/Rescue) "default value"

result = risky_operation [rescue](/page/Rescue) "default value"

This form has higher precedence than assignment (=) 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. 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:

ruby

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

This feature promotes resilient code but requires careful condition checks to avoid repetition.

Advanced Syntax

Metaprogramming Basics

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. 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, of code in specific contexts, and method aliasing, all of which facilitate runtime and adaptation without altering source code directly. One fundamental metaprogramming tool is define_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:

ruby

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!"

This approach contrasts with static def declarations by allowing method creation based on runtime conditions, such as loops or data-driven logic. 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 , 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:

ruby

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

This technique is widely used for implementing method delegation in proxies or domain-specific languages. For injecting code dynamically into an object's context, 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:

ruby

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"

These methods support metaprogramming by permitting runtime modifications to object behavior, often used in libraries for configuration or extension. Finally, 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:

ruby

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"

This is essential for preserving backward compatibility or providing synonymous interfaces in dynamic extensions.

Dynamic Code Evaluation

Ruby's dynamic code evaluation allows the execution of arbitrary code at runtime, primarily through the Kernel#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:

ruby

eval("1 + 1") # => 2

eval("1 + 1") # => 2

This evaluates in the current context, accessing local variables and methods available there. The 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:

ruby

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

Without a binding, eval uses the caller's context, but bindings prevent unintended variable access or modifications. 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:

ruby

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

Similarly, class_exec uses class_exec(arg...) { |var...| block } for class or module contexts, where methods defined in the block become class methods:

ruby

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"

These methods avoid string parsing, making them preferable for predefined code snippets. However, eval and string-based evaluation pose significant security risks, particularly when processing untrusted input, as they can lead to , such as access or data leaks via injection attacks. For instance, evaluating user-supplied strings like system("rm -rf /") could compromise the . 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:

ruby

obj.instance_eval("def greet; 'hello'; end") obj.greet # => "hello"

obj.instance_eval("def greet; 'hello'; end") obj.greet # => "hello"

This provides isolation compared to global 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 to build domain-specific languages (DSLs) by dynamically injecting controlled code.

References

Add your contribution
Related Hubs
User Avatar
No comments yet.