---
title: 'Ruby Fundamentals'
source: 'https://academia.sh/en/courses/ruby-fundamentals'
language: en
updated: '2026-10-21T05:03:42+00:00'
license: 'CC BY-SA 4.0'
lessons: 12
outcomes:
    - 'Explains which message an operation corresponds to in a model where every value is an object'
    - 'Distinguishes, by measurement, whether a message is answered by a class, a module, a block or the syntax'
    - 'Writes basic programs with variables, control flow, methods, symbols and strings'
    - 'Processes arrays and hashes with blocks, Enumerable methods, procs and lambdas'
    - 'Catches exceptions, retries, cleans up resources and works with files'
---

# Ruby Fundamentals

In Ruby, 5 + 3, x = 5, an array's map and 5.between?(1, 10) are written the same way, yet the code that produces the result lives in four different places: in the receiver's class, in a module mixed into the class, in the block the caller supplies, or in the language's own syntax. The course measures every mechanism with a single question — who answers this message? The object model, variables, control flow, methods, symbols, collections, blocks, Enumerable, procs and lambdas, exceptions and file handling all pass through the same frame.

## Language Basics

A design that puts readability first turning even operators into methods, primitive values being objects too, naming rules and scope sigils, conditions and loops that produce values, methods with implicit return, and symbols and strings being two separate representations.

### The Design Philosophy of Ruby

Source: https://academia.sh/en/courses/ruby-fundamentals/the-design-philosophy-of-ruby

In Ruby, 5 + 3, x = 5, the map of an array, and 5.between?(1, 10) are written the same way, yet the code that produces each result lives in four different places. This lesson measures those four responders with twelve observations and shows that a design that puts readability first turns even operators into methods.

M12 closed with PHP. There we measured which channel a question is heard on, where a change reaches, and whether a part runs within a request. The Ruby curriculum starts from a different question. `5 + 3`, `x = 5`, `[1, 2, 3].map { ... }`, and `5.between?(1, 10)` look the same on screen: a value, a dot or an operator, a name. Yet the code that produces the result of these four expressions lives in four different places. This course measures each mechanism with a single question: who answers this message?

A message is a name and arguments sent to an object; the object that receives it is called the receiver, and the code that runs in response is called a method. The Programming Fundamentals course (M01/K02) established the concepts of variable, type, and function; what is new here is the mechanism through which Ruby provides them. This lesson builds the vocabulary of the mechanism and the core of the measurement, and the next five lessons each take a slice from the same core.

#### The Four Responders

There are four places where the code that handles a message can be found. These four names do not change throughout the course; the name of the category itself is **responder**. Because the word "class" collides with Ruby's `Class`, the measurement category is written as "the class responder"; a bare "class" means only the Ruby class.

| Responder | Definition |
|---|---|
| class | The method that handles the message is defined in a class the receiver belongs to |
| module | The method is defined in a module and has been mixed into the class |
| block | The method relies on the block the caller supplies; the same message gives a different result with a different block |
| syntax | There is no message; the language itself processes the expression |

This distinction looks like a question from earlier curricula but is not the same. M08/K03 asked whose word a behavior is: whether the language, a library, or the programmer promises it. Here no promise is sought, only code; what is measured is not the party that promises the behavior but the definition that handles the message at that moment.

**LB1.** In this lesson, the "responder" is the place where the definition of the method that handles the message is found. The order in which a method is searched for and found is not measured in this course; only the result, that is, the owner, is measured. The lookup order is the subject of the next course in the curriculum.

#### The Readability Priority

Ruby's design choice is to put the readability of code like a sentence ahead of operator economy. This choice is most visible in named methods: `5.between?(1, 10)` reads exactly like a question. The second consequence is less visible: operators are methods too. The file below gives the same sum in three spellings.

```ruby
# operators.rb — shows that operators are methods too
puts 1 + 2
puts 1.+(2)
puts 5.send(:+, 3)
puts 5.between?(1, 10)
puts 5.clamp(1, 3)
```

```
3
3
8
true
3
```

`1 + 2` and `1.+(2)` give the same result because the first is the short spelling of the second: receiver `1`, message `+`, argument `2`. `send` takes the name of the message as a symbol, and `5.send(:+, 3)` sends the same message. So although a spelling difference remains between `5 + 3`, `x.upcase`, and `5.between?(1, 10)`, no difference in mechanism remains: all three are messages sent to a receiver. This is the intuition the later lessons rest on; in a language where every operation is a message, the question "who answers" becomes meaningful.

**LB2.** The measurements were run on Ruby 4.0.7. Which class or module a method is defined in can shift from version to version; the owner names written in the lesson are the result of this version, not a promise of the language.

#### Two Spellings of the Same Job

The readability choice shows up not only in method names but also in the fact that the same job has more than one spelling. In Ruby there is no single way to do a job, and the spelling the author chooses determines how clearly the job's purpose is stated to the reader. The two files below ask the measurable part of this choice: do two spellings of the same job differ only in appearance, or do they call a different method? The axis of measurement is the responder; where possible, the owner handling each spelling's message is printed next to the output.

```ruby
# two_spellings.rb — two spellings of the same job and their responders
list = [4, 5, 6]
counter = 0
list.each { |n| counter += n }
puts counter
puts list.sum
puts list.method(:each).owner
puts list.method(:sum).owner
x = 7
puts "small" unless x > 10
puts "small" if !(x > 10)
```

```
15
15
Array
Array
small
small
```

The counter spelling and `sum` gave the same total, `15`. The responder of both turned out to be `Array`: `each` and `sum` are both defined in the array's class. So the difference between the two spellings is not in the responder but in the reading. In the counter spelling, opening a variable, writing a block, and updating the variable inside the block are read together; in the `sum` spelling, the intent is stated with a single name. The `unless` and the negated `if` printed the same line twice. Here the responder question carries a different answer: `unless` and `if` are keywords, not methods; so the responder of these two lines is counted as the syntax of the language. The `x > 10` inside the condition is a message; its owner was not printed in this file. The negation operator in the negated spelling is also a separate message and does not appear in the `unless` spelling; its owner was not measured here either. Turning a negative condition around and writing it with a negated `if` also works, but the reader has to combine two separate operations in the head, first the comparison, then the negation.

```ruby
# parentheses.rb — calls with and without parentheses are the same message
items = [3, 1, 2]
a = items.push 9
b = items.push(9)
puts a.equal?(b)
puts items.size
puts items.method(:push).owner
puts items.sort.first
puts items.sort().first()
puts method(:puts).owner
```

```
true
5
Array
1
1
Kernel
```

The parenthesis-free `items.push 9` and the parenthesized `items.push(9)` returned the same array: `equal?` gave `true`. Both calls returned the same object, and the array grew twice, reaching 5 elements. The responder of the `push` message is `Array`. `sort.first` and `sort().first()` gave the same value `1`, and the owner of the `puts` call turned out to be `Kernel`. Neither of the two spelling pairs is a separate mechanism: parentheses only mark where the arguments end, and change neither the receiver nor the message. The rule that comes out of this measurement is: in Ruby, readability is a freedom in the author's hands, and the freedom does not change the mechanism. The spelling chosen to do the same job should make the reader's work easier, not the machine's.

**LB3.** Which spelling reads faster was not measured. What was measured is only that the two spellings gave the same result and that the responder did not change; the readability judgment depends on a team's spelling preference and is not a claim of this course.

#### The Limit of Readability

The readability priority grants a freedom, but not every freedom can be written without changing the message. The question of this section is: where the spelling changes, does the message change too? When two spellings are placed side by side, sometimes the message stays the same, and sometimes a spelling difference corresponds to a separate message. The measure is the responder and the result; the file prints the owners of four messages it uses.

```ruby
# spelling_limit.rb — does the message change when the spelling changes
a = 2
b = 3
puts a + b * 2
total = (a + b) * 2
puts total == 10
puts a.+(b).*(2)
puts a.method(:+).owner
puts a.method(:*).owner
puts 10.fdiv(4)
puts 10 / 4
puts 10.method(:fdiv).owner
puts 10.method(:/).owner
```

```
8
true
10
Integer
Integer
2.5
2
Integer
Integer
```

The expression `a + b * 2` gave `8`; the multiplication was done before the addition. `(a + b) * 2` gave `10`, and the comparison `total == 10` was `true`. The spelling `a.+(b).*(2)` on the third line also gave `10`: receiver `a`, message `+`, argument `b`, then the `*` message was sent to the result. So the precedence choice the author makes with parentheses gives exactly the same result as the chaining order of messages in dotted spelling. Operator precedence is therefore not a responder question; it is a spelling rule that determines which message goes to which receiver. The owner lines showed that both operators are defined in the `Integer` class.

What is interesting for readability is the two spellings of division. `10.fdiv(4)` gave `2.5`, while `10 / 4` gave `2`. The responder of both turned out to be `Integer`, but the results differ: `/` does integer division, `fdiv` gives a fractional result. The spelling difference here is not two forms of the same job but two separate messages. Ruby's design makes this distinction by name: someone who wants a fractional result writes `fdiv`, and the reader understands the intent from its name. This is also the conclusion of the section: spelling freedom is used safely in named messages; with operators, whether the result stays the same is known by measuring.

**LB4.** That integer division gives `2` for `Integer` was measured in this version. That division behaves the same way for other kinds of numbers was not measured.

#### The Core: A Helper and Twelve Observations

The course uses a measurement core made of two files. The first is a helper that classifies a message and is written as a rule: the order of the questions decides, not a label. First `defined?` says whether the expression is a method call; if it is not, the responder is syntax. Then, if the same message gives two different results with two different blocks, the responder is a block. Finally, if the owner of the method is an instance of `Class`, it is a class; otherwise it is a module.

```ruby
# reply.rb — helpers that classify who answers a message

# Classification asks in order: is the expression a message at all (defined?),
# then do two different blocks give two different results, then who owns the method.
def responder(definition, owner: nil, result: nil, with_blocks: nil)
  return ["syntax", "defined? #{definition}"] unless definition == "method"

  if with_blocks
    first, second = with_blocks.call
    return ["block", "#{first.inspect} / #{second.inspect}"] unless first == second
  end

  kind = owner.instance_of?(Class) ? "class" : "module"
  [kind, "#{owner}, result #{result.inspect}"]
end
```

This file has no output block: it prints nothing on its own and is only a helper that the other file pulls in. The second file makes the twelve observations. Each observation is inside a lambda; when the lambda is called, it gives the `responder` helper the `defined?` result of the message, the owner of the method, and its result. The file writes a `note.txt` to the working directory, because the last observation reads it.

```ruby
# responder.rb — twelve observations: who answers this message
require_relative "reply"

File.write("note.txt", "first line\nsecond line\n")
numbers = [1, 2, 3]

observations = {
  "readable comparison" => -> { responder(defined?(5.between?(1, 10)), owner: 5.method(:between?).owner, result: 5.between?(1, 10)) },
  "integer addition"    => -> { responder(defined?(5 + 3), owner: 5.method(:+).owner, result: 5 + 3) },
  "local assignment"    => -> { responder(defined?(x = 5)) },
  "range match"         => -> { responder(defined?((1..10) === 7), owner: (1..10).method(:===).owner, result: (1..10) === 7) },
  "receiverless call"   => -> { responder(defined?(format("%03d", 7)), owner: method(:format).owner, result: format("%03d", 7)) },
  "symbol conversion"   => -> { responder(defined?("name".to_sym), owner: "name".method(:to_sym).owner, result: "name".to_sym) },
  "default value"       => -> { responder(defined?(Hash.new), with_blocks: -> { [Hash.new { 0 }[:x], Hash.new { |_, k| k.to_s }[:x]] }) },
  "element mapping"     => -> { responder(defined?(numbers.map), with_blocks: -> { [numbers.map { |n| n * 2 }, numbers.map { |n| n.to_s }] }) },
  "slicing"             => -> { responder(defined?((1..5).each_slice(2)), owner: (1..5).method(:each_slice).owner, result: (1..5).each_slice(2).to_a) },
  "lambda call"         => -> { responder(defined?(->(n) { n * 2 }.call(3)), owner: ->(n) { n * 2 }.method(:call).owner, result: ->(n) { n * 2 }.call(3)) },
  "rescue"              => -> { responder(defined?(begin; Integer("x"); rescue ArgumentError; 0; end)) },
  "file reading"        => -> { responder(defined?(File.open("note.txt")), with_blocks: -> { [File.open("note.txt") { |f| f.gets.chomp }, File.open("note.txt") { |f| f.readlines.size }] }) },
}

tally = { "class" => 0, "module" => 0, "block" => 0, "syntax" => 0 }

printf("%-22s%-11s%s\n", "observation", "responder", "evidence")
observations.each do |name, observation|
  kind, evidence = observation.call
  tally[kind] += 1
  printf("%-22s%-11s%s\n", name, kind, evidence)
end

puts
tally.each { |kind, n| printf("%-11s%d\n", kind, n) }
puts "total observations: #{tally.values.sum}"
```

```
observation           responder  evidence
readable comparison   module     Comparable, result true
integer addition      class      Integer, result 8
local assignment      syntax     defined? assignment
range match           class      Range, result true
receiverless call     module     Kernel, result "007"
symbol conversion     class      String, result :name
default value         block      0 / "x"
element mapping       block      [2, 4, 6] / ["1", "2", "3"]
slicing               module     Enumerable, result [[1, 2], [3, 4], [5]]
lambda call           class      Proc, result 6
rescue                syntax     defined? expression
file reading          block      "first line" / 2

class      4
module     3
block      3
syntax     2
total observations: 12
```

Of the twelve observations, a class answered 4, a module answered 3, a block answered 3; in 2 there was no message, and the syntax processed the expression. These numbers are binding for all the lessons of the course: later lessons do not change these numbers, and each takes its own row from the table and measures it in detail.

Four readings come out of the table. The class responder is the most direct: the `+` in `5 + 3` looks like an operator but is a method defined in the `Integer` class; `"name".to_sym` is defined in `String`, and the lambda's `call` in `Proc`. The module responder shows an ability that is not found in the class itself: `between?` is in `Comparable`, not in `Integer`; `each_slice` is in `Enumerable`, not in `Range`; the receiverless `format` is in `Kernel`. The block responder leaves the result to the caller: `Hash.new`, `map`, and `File.open` gave two results with the same message, and the difference was only in the block supplied. In the syntax responder there is neither a receiver nor a message: for `x = 5`, `defined?` returned `"assignment"`, and for `begin ... rescue ... end`, `"expression"`.

There are also things deliberately not printed in the output: `object_id`, `inspect` output that carries a memory address, and exception message text. These values change on every run or depend on the environment; in their place a class name, a boolean result, and a number are printed.

#### The Bounding Measurement

This lesson's row is `readable comparison`, and its responder is a module: the `Comparable` module handles the message `5.between?(1, 10)`. But if this sentence is left half finished, it misleads. The method the module defines does not do the comparison itself; it asks the receiver. To say whether a value lies between two bounds, `between?` sends the `<=>` message to the receiver, and how the comparison is done lies in the `<=>` method of the receiver's class. Print the owners side by side.

```ruby
# comparison.rb — between? is in Comparable, <=> is in the receiver's class
[5, "a", :a].each do |receiver|
  printf("%-4s between? %-11s <=> %s\n", receiver.inspect, receiver.method(:between?).owner, receiver.method(:<=>).owner)
end
```

```
5    between? Comparable  <=> Integer
"a"  between? Comparable  <=> String
:a   between? Comparable  <=> Symbol
```

For all three receivers the owner of `between?` is the same, and the owner of `<=>` is each time the receiver's own class. The same module gives three different classes the same ability, because all three answer the comparison themselves. The module therefore does not answer alone: its ability comes from the module, its basis from the class.

This bound prevents reducing the responder to a single owner name. The classification with four responders finds the definition that handles the message; that definition relying on another message is a new measurement question, and its answer is often a class.

#### Summary

- In Ruby, every operation is a message sent to a receiver; `1 + 2` and `1.+(2)` are the same message, and `send` sends a message by its name.
- The measurement question is "who answers this message", and there are four responders: class, module, block, syntax.
- A rule gives the responder, not a label: first `defined?`, then the comparison with two blocks, then the owner of the method.
- In the core's twelve observations the distribution is 4 class, 3 module, 3 block, and 2 syntax.
- The responder of this lesson is a module: `between?` is in `Comparable`, but `<=>`, the comparison itself, is in the receiver's class.
- Two spellings of the same job (a counter with `each` and `sum`, calls with and without parentheses) did not change the measured owners; the `unless` and the negated `if` gave the same result. Readability is the author's choice and does not change the mechanism.

#### Next Step

That the `+` of `5 + 3` is defined in `Integer` silently assumes something: `5` is an object and belongs to a class. Whether primitive values are objects too, and how the receiver and the responder are separated from each other, is the subject of the next lesson, An Intuition for the Object Model.

### An Intuition for the Object Model

Source: https://academia.sh/en/courses/ruby-fundamentals/the-object-model

Numbers, nil, and true are objects too; in 5 + 3 the receiver is 5 and the responder is Integer; classes are objects themselves, and another class answers the messages sent to them.

The previous lesson established that every operation is a message sent to a receiver, and showed within the twelve observations that the `+` of `5 + 3` is defined in the `Integer` class. That sentence assumed something: `5` is an object and belongs to a class. This lesson measures that assumption. Primitive types were measured in the JavaScript Fundamentals course (M06/K01); in Ruby the same question is asked in a different form: if a message can be sent to a value, who handles that message?

Object orientation was established in M08 with Java, and the concepts of object and class were defined there. What is new here is that in Ruby numbers, `nil`, and truth values are inside the same model. In addition, this lesson takes a single row from the core: `integer addition`.

#### Every Value Is an Object

Ruby has no separate category of primitive values. The file below sends the same message, `class`, to eight different values. On each line the representation of the value and its class are printed side by side.

```ruby
# values.rb — every value has a class
values = [5, 3.5, "name", :name, nil, true, [1], 1..2]

values.each do |value|
  printf("%-6s %s\n", value.inspect, value.class)
end
```

```
5      Integer
3.5    Float
"name" String
:name  Symbol
nil    NilClass
true   TrueClass
[1]    Array
1..2   Range
```

The integer, the floating-point number, the string, the symbol, `nil`, the truth value, the array, and the range all come from a class. `nil` and `true` are the single instance of a class: `NilClass` and `TrueClass`. This means messages can be sent to values. The file below sends one message each to a number, to `true`, and to `nil`, and asks the owner of each method.

```ruby
# primitive_messages.rb — numbers, true, and nil answer messages
puts 5.times.class
puts 5.method(:times).owner
puts true.method(:&).owner
puts nil.method(:to_a).owner
p nil.to_a
p true & false
```

```
Enumerator
Integer
TrueClass
NilClass
[]
false
```

`5.times` returns an enumerator, and its owner is `Integer`. `true & false` looks like an operator but is a method defined in `TrueClass`. `nil.to_a` gives the empty array, and its owner is `NilClass`. All three messages found their answer in the receiver's own class.

**LB5.** In this lesson, an "object" is any value a message can be sent to. How a value is held in memory is not measured; only the behavior is measured, that is, who responds to which message. For this reason `object_id` and `inspect` output that carries a memory address are not printed.

#### Receiver and Responder

In the expression `5 + 3`, the receiver is `5`, the message is `+`, and the argument is `3`. M11/K04 measured which object is the target of an operation. Here the receiver is known; the question is which definition answers the message that reaches the receiver. In `5 + 3` the receiver is `5` and the responder is `Integer`.

```ruby
# receiver_responder.rb — in 5 + 3 the receiver and the responder are different things
receiver = 5

puts defined?(receiver + 3)
puts receiver.method(:+).owner
puts receiver.method(:+).owner.instance_of?(Class)
puts receiver + 3
```

```
method
Integer
true
8
```

`defined?` returned `"method"` for the expression: there really is a message here. The owner of the method is `Integer`, and `Integer` is an instance of `Class`; in this case the classification rule of the course counts the responder as a class. The receiver is a value, the responder is a definition. The two often do not carry the same name: `5` is a value, and `Integer` is the class it belongs to.

This line is the `integer addition` observation in the shared core of the course, and, as in the whole core, its responder is a class. This lesson does not pull in `reply.rb`; the `defined?` result and the owner of the method are printed directly. The rule is the same: first `defined?`, then the owner.

#### Classes Are Objects Too

Since every value has a class, `Integer` must have a class too. In Ruby `Integer` is a constant and points to an object; that object can answer a message as well. But who answers when a message is sent to `Integer`? The file below prints the class of `5` and of `Integer`, and then the owner of the `===` message sent to `Integer`.

```ruby
# class_objects.rb — classes are objects, and another class answers their messages
puts 5.class
puts Integer.class
puts Integer.method(:===).owner
puts Integer.method(:===).owner.instance_of?(Class)
puts Integer === 5
puts Integer === "5"
```

```
Integer
Class
Module
true
true
false
```

The class of `5` is `Integer`; the class of `Integer` is `Class`. So `Integer` is an object in its own right, and the `===` message sent to it is answered not by `Integer` but by `Module`. `Module` is a class too, so the rule again counts the responder as a class. `Integer === 5` returns true and `Integer === "5"` returns false: here `===` asks "does this value belong to this class".

#### A Message Depends on the Receiver's Class

That every value has a class means that not every value can answer every message. The `times` message is defined in `Integer`; it is not in `NilClass`, the class of `nil`. The file below asks three messages of two receivers, and looks at what happens when `times` is sent to `nil`.

```ruby
# message_probe.rb — a message depends on the receiver's class
[5, nil].each do |receiver|
  printf("%-4s times: %-5s to_a: %-5s to_s: %s\n", receiver.inspect, receiver.respond_to?(:times), receiver.respond_to?(:to_a), receiver.respond_to?(:to_s))
end

begin
  nil.times
rescue NoMethodError => e
  puts e.class
end
```

```
5    times: true  to_a: false to_s: true
nil  times: false to_a: true  to_s: true
NoMethodError
```

`respond_to?` asks whether an object answers a message. `5` answers the `times` and `to_s` messages; `nil` answers `to_a` and `to_s` but does not answer `times`. Sending a message that is not answered raises `NoMethodError`, and here only the class of the exception was printed. So the sentence "everything is an object" does not mean "every object accepts every message": the counterpart of a message must be found in the receiver's class or in the definitions above that class.

#### Probing That Everything Is an Object

The previous sections built the object model through the receiver, the responder, and the class. This section tests the claim "everything is an object" one by one, on numbers, `nil`, and the classes themselves. The limit of the claim is also measured: the two queries that ask what kind of thing a value is, `instance_of?` and `is_a?`, do not ask the same question. The axis is again the responder; the owner of one of the queries is read on the last line of the output.

```ruby
# everything_object.rb — there is no value that is not an object
puts 5.respond_to?(:+)
puts nil.respond_to?(:to_a)
puts 5.frozen?
puts 5.methods.size > 50
puts Integer.class
puts Class.class
puts 5.instance_of?(Integer)
puts 5.is_a?(Comparable)
puts 5.instance_of?(Comparable)
puts 5.method(:frozen?).owner
```

```
true
true
true
true
Class
Class
true
true
false
Kernel
```

The first two lines gave `true`: `5` answers the addition message, and `nil` answers the `to_a` message. `5.frozen?` also returned `true`; a number is an immutable object. The question `5.methods.size > 50` was `true`. Here we deliberately did not print the number itself: how many methods an object has changes with the version, but the result that it has quite a few does not. The fifth and sixth lines are the real finding of the section: `Integer.class` and `Class.class` both printed `Class`. `Integer` is a class and an object; the class of that object is `Class`. `Class` itself is an object too, and its class is also `Class`. The chain closes on itself, so the question "what is the class of a class" does not go on forever.

The last three queries show the difference between two methods. `5.instance_of?(Integer)` gave `true`, `5.is_a?(Comparable)` gave `true`, and `5.instance_of?(Comparable)` gave `false`. `instance_of?` asks only about the object's direct class; `is_a?` asks the broader question of whether the object counts as being of that kind. We do not open up how the chain works here; we only record the difference. The owner on the last line turned out to be `Kernel`: the `5.frozen?` message is answered not by the number's class but by a module that every object takes in common.

**LB6.** The `> 50` threshold is a choice: the measurement only shows that the number is above the threshold, and the number itself is not a promise of the language.

```ruby
# nil_object.rb — nil is an object too and has an owner
puts nil.class
puts nil.to_a.inspect
puts nil.to_s.empty?
puts nil.method(:to_a).owner
puts true.class
puts true.method(:&).owner
```

```
NilClass
[]
true
NilClass
TrueClass
TrueClass
```

`nil` is an object too: `nil.class` gave `NilClass`. `nil.to_a` returned an empty array, and because `nil.to_s` gives an empty string, the question `empty?` was `true`. So even the "no value" state responds to messages, and this is why calling `to_a` on `nil` produces `[]` rather than an error. `nil.method(:to_a).owner` turned out to be `NilClass`: the responder is `nil`'s own class. The same pattern holds for `true`: its class is `TrueClass`, and the owner of the `&` message is `TrueClass`. The classes of `true` and `nil` are separate, and the owners of the two measured messages turned out to be those two classes themselves. The common conclusion of these two sections tightens the intuition at the start of the lesson: in Ruby no value is closed to messages. Whatever the value of an expression is, a message can be written after it, and the responder can be found as a class or a module.

#### Chained Messages and the Return Object

Every message returns an object, and the returned object becomes the receiver of the next message. This section measures that at each link of the chain the responder can change. The expression measured is a chain such as `5.to_s.size.to_f`; the chain has to be read from the left: first `5` receives the message, the object that comes out receives the next one, and so it goes on. The axis is again the owner: the owners of two links are printed separately.

```ruby
# chain.rb — every message returns an object and the chain goes on
result = 5.to_s.size.to_f
puts result
puts result.class
puts 5.to_s.class
puts 5.to_s.size.class
puts [3, 1, 2].sort.reverse.first
puts "ruby".reverse.upcase.class
puts 5.method(:to_s).owner
puts 5.to_s.method(:size).owner
```

```
1.0
Float
String
Integer
3
String
Integer
String
```

The result of the chain was `1.0`, and its class is `Float`. Probing the intermediate links one by one shows how the chain is built: `5.to_s` gave a `String`, and `5.to_s.size` gave an `Integer`. So within the same expression there are four separate objects and three separate classes: `Integer` for `5`, `String` for `"5"`, `Integer` again for `1`, and finally `Float` for `1.0`. `[3, 1, 2].sort.reverse.first` went down to an element after two array links and gave `3`; `"ruby".reverse.upcase.class` printed `String`, because both links return strings.

The owner lines showed that two different links are defined in two different classes: `5.method(:to_s).owner` turned out to be `Integer`, and `5.to_s.method(:size).owner` turned out to be `String`. The same names `to_s` and `size` could have gone to a different owner for another receiver; the receiver's class determines the responder, not the position in the chain. This is the concrete counterpart of the claim "Ruby is object-oriented": an operation is sending a message to an object, and the answer is again an object. To read a chain, one has to know the class of the return object of each link; when the class changes, the next message goes to a different responder. So when an error occurs in a long chain, the way to find where it arose is to split the chain link by link and probe the class of each intermediate object.

#### Equality and Identity Are Separate Questions

Equality and identity are two separate questions. `==` asks whether two objects are equal in value, and `equal?` asks whether the two are the same object. This section places the two queries side by side on the same values. The axis is the owner: the place where the method answering the two queries is defined is printed at the end.

```ruby
# identity.rb — equality and identity are separate questions
a = "x"
b = a
puts a.equal?(b)
puts a.equal?("x")
puts a == "x"
puts a.dup.equal?(a)
puts 1.equal?(1)
puts :s.equal?(:s)
puts nil.equal?(nil)
puts 1 == 1.0
puts 1.eql?(1.0)
puts 1.equal?(1.0)
puts a.method(:==).owner
puts a.method(:equal?).owner
```

```
true
false
true
false
true
true
true
true
false
false
String
BasicObject
```

After the assignment `b = a`, `a.equal?(b)` gave `true`: two names point to a single object. `a.equal?("x")` was `false` and `a == "x"` was `true`: the text is the same, the object is different. `a.dup.equal?(a)` also gave `false`, because `dup` produces a new object. A number, a symbol, and `nil` behaved differently: `1.equal?(1)`, `:s.equal?(:s)`, and `nil.equal?(nil)` all three gave `true`; every spelling of these values is the same object.

In comparisons among numbers, the three queries gave two different answers. `1 == 1.0` was `true`, while `1.eql?(1.0)` and `1.equal?(1.0)` gave `false`. So `==` looks at values and sees equality between an integer and a floating-point number; `eql?` and `equal?` ask stricter questions. The owner lines showed where the two queries are defined: `String` for `==` and `BasicObject` for `equal?`. So `==` is a question left to each class's own definition, while `equal?` is defined in a single common place. For this reason, before asking whether a value is "the same", one has to state in which sense it is asked. In conclusion, `equal?` is used for the identity question and `==` for the value question; confusing the two opens the way to taking two separate objects for the same one or the same object for two.

#### The Bounding Measurement

In most of this lesson's measurements the responder is a class; `Kernel`, a module, is the exception for `class` and `frozen?`, and `BasicObject` for `equal?`. The expression "the class responder" can slip toward two different things. The slip goes like this: one may assume that the responder of the `Integer.===` message is `Integer`. The run above showed this is wrong. The responder is not the receiver itself but the chain of classes the receiver belongs to: if the receiver is `5`, it is `Integer`; if the receiver is `Integer`, it is `Module` in the chain of `Class`.

```ruby
# receivers_class.rb — the responder is the class the receiver belongs to, not the receiver itself
[5, "name", Integer, String].each do |receiver|
  printf("%-8s class %-8s method owner %s\n", receiver.inspect, receiver.class, receiver.method(:class).owner)
end
```

```
5        class Integer  method owner Kernel
"name"   class String   method owner Kernel
Integer  class Class    method owner Kernel
String   class Class    method owner Kernel
```

The owner of the `class` message is `Kernel` for all four receivers, because this message is defined in a module common to every object. The classes of the receivers, however, differ. The question "who answers this message" has two layers: which class the receiver belongs to, and whether that class answers the message itself. What was measured in this lesson is the first layer. The measurement of the second, the cases where the method is found not in the receiver's class but in a definition above it, continues in the Methods lesson, whose subject is the module responder.

#### Summary

- In Ruby, numbers, `nil`, `true`, strings, symbols, arrays, and ranges are all objects; each has a class.
- In `5 + 3` the receiver is `5` and the responder is `Integer`; the receiver is a value, the responder is a definition.
- The messages `5.times`, `true & false`, and `nil.to_a` are answered by `Integer`, `TrueClass`, and `NilClass` respectively.
- Classes are objects too: `Integer.class` is `Class`, and the `Integer.===` message is answered by `Module`.
- The responder of this lesson is mostly a class: the message is answered not by the receiver itself but by the class the receiver belongs to. The owner of messages common to every object, such as `class` and `frozen?`, is the `Kernel` module.
- `Integer.class` and `Class.class` both give `Class`; `equal?` asks identity and `==` asks value, and the responder of each link in a chain is the class of that link's receiver.

#### Next Step

It was seen that objects have a class and that messages are sent to them. But how is a name bound to an object, and who is at work when `x = 5` is written? Whether assignment is a message, the naming rules, and the sigils are the subject of the next lesson, Variables and Constants.

### Variables and Constants

Source: https://academia.sh/en/courses/ruby-fundamentals/variables-and-constants

In Ruby, assignment is syntax rather than a message; local variables, instance variables, global variables, and constants are told apart by the sigil on the name; the limits measured with defined?.

The previous lesson established that every value is an object belonging to a class and that the receiver's class answers the message. One question was left open: how is a name bound to one of these objects? When `x = 5` is written, is a message going to `x`, or to `5`, or is there no message at all? The concept of a variable itself was explained in the Programming Fundamentals course (M01/K02); it is not repeated here. This lesson measures only the Ruby-specific question: when a name is bound to an object, who is at work?

In Ruby, the mark at the start of a name determines what the name is. We call these marks sigils: a lowercase name with no mark is a local variable, a name starting with `@` is an instance variable, a name starting with `$` is a global variable, and a name starting with an uppercase letter is a constant. There is also a class variable that starts with `@@`; it is returned to in the Scope Limits and Parallel Assignment section. The file below queries the four marks, an undefined constant, and an assignment one by one with `defined?`.

#### The Name's Sigil Determines the Kind

`defined?` is an expression that says what an expression is without running it. For an undefined name it returns `nil`; for a defined one, a string saying what it is. This is the measuring instrument we will use in the lesson.

```ruby
# scope_sigils.rb — the sigil on a name determines the kind of variable
x = 5
@a = 1
$g = 2

puts defined?(x)
puts defined?(@a)
puts defined?($g)
puts defined?(String)
p defined?(Missing)
puts defined?(result = 5)
```

```
local-variable
instance-variable
global-variable
constant
nil
assignment
```

Six queries, six different answers: `x` is a local variable, `@a` an instance variable, `$g` a global variable, `String` a constant. For the undefined constant `Missing`, `nil` was returned. The last line is the real subject: the expression `result = 5` was reported as `"assignment"`. Neither `"method"` nor `"expression"`; assignment has a name of its own.

**LB7.** In this lesson, "assignment" is the spelling that binds a name to an object with `=`. `defined?` only reads the kind of the expression and does not actually run the assignment; for this reason the name `result` above never came into being. It is known that reassigning to a constant name prints a warning, but because that warning carries a file path it is not printed or measured here; it is mentioned only in prose.

#### A Constant Binds the Name and Does Not Freeze the Object

A name starting with an uppercase letter is a constant. Whether a constant is defined can be read with `Object.const_get`. But what does a constant make constant: the object the name is bound to, or the binding of the name? The file below binds a number and an array to one constant each, and then adds an element to the array.

```ruby
# constant_array.rb — a constant binds the name, it does not freeze the object
LIMIT = 10
LIST = [1]

puts Object.const_get(:LIMIT)
puts defined?(LIMIT)
LIST << 2
p LIST
puts LIST.frozen?
puts LIMIT.frozen?
```

```
10
constant
[1, 2]
false
true
```

Although the array was bound to the constant `LIST`, it changed: the constant marks with a warning that the name is being bound to a different object, but it does not lock the contents of the object. The array is not frozen (`false`). Numbers, on the other hand, are already immutable objects, and `frozen?` returns `true` for them. Someone who really wants to make a constant immutable freezes the object separately; freezing is measured on strings in the Symbols and Strings lesson of this course.

#### Each Kind Is Kept in Its Own Place

The sigils are not only a convenience of spelling; each kind is stored in a different place. Local variables belong to the scope of the running code itself, instance variables are tied to an object, and global variables are visible across the whole program. Ruby offers methods that list these three. The file below defines the three kinds at the top level and looks each one up in its own list.

```ruby
# variable_lists.rb — each kind is kept in its own list
x = 5
@a = 1
$g = 2

p local_variables
p instance_variables
puts global_variables.include?(:$g)
puts self.inspect
puts self.class
```

```
[:x]
[:@a]
true
main
Object
```

`x` was found in the list of local variables and `@a` in the list of instance variables; `$g` is in the list of global variables. The answer to the question of whose instance variable it is, is `self`: at the top level, `self` is the main object of the file, it prints as `main`, and its class is `Object`. So the `@a` written at the top level is bound to an instance of `Object`; an instance variable is always a variable of an object.

#### Assignment Is Not a Message

The rule of the previous lesson was: `5 + 3` is a message, and its responder is the class. The same question can be asked of assignment: if `x = 5` is a message, there must be an owner that answers it. The file below puts three things side by side: plain assignment, a method call that returns a value, and the return value of an assignment.

```ruby
# assignment_value.rb — an assignment expression carries a value
puts defined?(a = 1)
puts defined?(puts)
puts defined?(1 + 2)

value = (b = 7)
puts value
puts b
```

```
assignment
method
method
7
7
```

Three expressions gave three different kinds: the assignment `"assignment"`, the `puts` call `"method"`, the addition `"method"`. So `1 + 2` and `puts` are in the same category, and `a = 1` is separate. Even so, an assignment carries a value: the value of the expression `(b = 7)` is `7`, and `b` was bound as a name. These two observations together say that an assignment is an expression but not a method call.

#### Scope Limits and Parallel Assignment

This section measures where names are visible. `defined?` reports only the kind of an expression without running it; here it is used as a scope probe. The axis is syntax: whether a name is visible or not is determined by the scope rules of the language, not by a method defined by a class. Three questions are asked: does a method opened with `def` see the outer local variable, does a block see it, and what kind is a name marked with `@@` opened in a class body?

```ruby
# scope_measurement.rb — how a method and a block see an outer local
outer = 10
def method_inside
  defined?(outer).inspect
end
puts method_inside
block_result = [1].map { defined?(outer) }
puts block_result.first
class Record
  @@count = 0
  def self.counter_kind
    defined?(@@count)
  end
end
puts Record.counter_kind
```

```
nil
local-variable
class variable
```

Three measurements gave three different results. When the outer local variable `outer` was asked about from inside a method opened with `def`, `defined?(outer).inspect` printed `nil`: a method body starts a new scope, and the outer locals do not pass inside. When the same name was asked about from inside a block, `"local-variable"` was returned: a block sees the locals of the place where it was defined. For `@@count` the result was `"class variable"`; this kind, opened in a class body, is a separate category from the `@` and `$` marks. What these three results have in common is that no message answers any of them: the method not seeing outside, the block seeing, and the kind of the `@@` mark all come from the spelling rules of the language. Where a name is defined matters as much as where it is asked: the same name `outer` gave two different answers when asked from two different places.

```ruby
# parallel_assignment.rb — a, b = b, a is an assignment
a = 1
b = 2
a, b = b, a
puts a
puts b
puts defined?(a = 3)
```

```
2
1
assignment
```

The parallel assignment `a, b = b, a` swapped the values of the two names: `a` is now `2` and `b` is `1`. A third temporary variable was not needed, because the right side was evaluated before the names on the left were rebound. `defined?(a = 3)` reported `"assignment"`, which agrees with the finding of the previous section: assignment is the spelling of the language, not a message. `defined?` was asked here of a single assignment, not of the parallel assignment itself; there is also no trace in the output showing that the swap was done with a method call. Swapping two variables is a one-line operation in Ruby; this shows once more that names are labels pointing at objects, and the value is the object behind the label.

**LB8.** The class variable (`@@`) was probed only with `defined?`. How a class variable is shared under inheritance is not the subject of this course and was not measured.

#### Assignment Chaining and Destructuring

That assignment is the spelling of the language was measured. This section probes three additional forms of assignment: chained assignment, destructuring into multiple targets, and a missing right side. Each asks the same question: do the names point to the object, or do they copy the value? The axis is syntax; only on one line the owner of the `<<` message is also printed, because after a chained assignment what changes the object is a message.

```ruby
# assignment_chain.rb — chained assignment and destructuring
x = y = [1]
puts x.equal?(y)
y << 2
puts x.inspect
puts x.method(:<<).owner
head, *rest = 10, 20, 30
puts head
puts rest.inspect
first, = [7, 8]
puts first
p1, p2 = 1
puts p2.inspect
puts defined?(Math)
puts defined?(puts)
puts defined?(String.new)
```

```
true
[1, 2]
Array
10
[20, 30]
7
nil
constant
method
method
```

The spelling `x = y = [1]` bound both names to the same object: `x.equal?(y)` gave `true`. This is why, when the array changed through `y << 2`, `x` also showed `[1, 2]`. What made the change here is not the assignment but the `<<` message, and its owner turned out to be `Array`. So the assignment did not copy the object; it only bound a name, and when a message that changes the object arrived, all the names bound to the same object saw the change. This is a test of the intuition built in the earlier sections: a name is a label, and the value is the object behind the label. Assigning a label to another label does not duplicate the object.

The destructuring forms are also the spelling of the language. `head, *rest = 10, 20, 30` gave the first value to `head` and the remaining ones to an array: `head` became `10` and `rest` became `[20, 30]`. The trailing comma in the spelling `first, = [7, 8]` marked that only the first of the targets was taken, and `first` became `7`. When the right side is short, no error was raised: on the line `p1, p2 = 1`, `p2` became `nil`. The last three lines are the answer of the `defined?` probe to different kinds of expression: `constant` for `Math`, and `method` for `puts` and for `String.new`. That the constant is classified as `constant` and the calls as `method` shows that the `defined?` probe looks at what the expression looks like.

**LB9.** That the result of `defined?(Math)` is `constant` was measured in this version. In other versions a different word may be returned for the same constant; the word written in the lesson is the output of this version, not a promise of the language.

#### The Definition Query and the Top-Level Object

`defined?` looks not only at local variables but at every kind of name. This section tries the probe on a global variable, an instance variable, and a few expressions, and asks what the top-level `self` object is. The axis is syntax: because `defined?` is an operator of the language rather than a method, the result concerns what the expression looks like.

```ruby
# definition_query.rb — the defined? answer for each kind of name
d1 = defined?($global)
puts d1.inspect
$global = 1
puts defined?($global)
d2 = defined?(@instance)
puts d2.inspect
@instance = 2
puts defined?(@instance)
puts defined?(self)
puts defined?(nil)
puts defined?(String)
puts defined?(3 + 4)
puts self.class
puts self.inspect
```

```
nil
global-variable
nil
instance-variable
self
nil
constant
method
Object
main
```

The global variable `$global` gave `nil` before it was assigned and `"global-variable"` after. The instance variable `@instance` followed the same order: first `nil`, then `"instance-variable"`. So being defined is a consequence not of the name but of the assignment: a name counts as defined only once a value has been bound to it. `defined?(self)` gave `"self"`, `defined?(nil)` gave `"nil"`, and `defined?(String)` turned out to be `"constant"`. `defined?(3 + 4)` reported `"method"`: what concerns the probe is that the outermost operation of the expression is a message.

The last two lines asked about the object this whole file runs inside. `self.class` gave `Object` and `self.inspect` gave `main`. So every expression written without a receiver in a `.rb` file runs inside an object whose class is `Object` and whose name is printed as `main`. The `@instance` variable assigned at the top level is bound to this object too. The implicit receiver of a receiverless call, for example the `puts` that was probed with `defined?` in the previous section, is this `main` object as well. The syntactic scope determines where names are asked; `self` determines in which object the question is answered. The probes of this section arrive at the same point: `defined?` says whether a name is defined and what kind it is, but does not say its value. An undefined name gives `nil`, and a defined name reports its own kind. These two answers are enough to probe a name before using it. Even though the answer `method` came for `3 + 4`, the one giving the answer is still the `defined?` operator: the expression was not run, only its form was looked at.

#### The Bounding Measurement

The responder of this lesson is syntax: the name-binding behavior of the `=` mark comes from the spelling of the language itself, not from a method defined by a class. Here the slip happens with `+=`. When `y += 1` is written, because there is an addition inside it, one may think it is a message. The measurement asks: are `y += 1`, `y ||= 3`, and `y + 1` in the same category?

```ruby
# compound_assignment.rb — += is an assignment, but the addition inside it is a message
y = 1

puts defined?(y += 1)
puts defined?(y ||= 3)
puts defined?(y + 1)
puts y
puts y.method(:+).owner
```

```
assignment
assignment
method
1
Integer
```

`defined?(y += 1)` and `defined?(y ||= 3)` both gave `"assignment"`, and `y + 1` gave `"method"`. Then `puts y` still printed `1`: `defined?` did not run the assignment. The responder of the addition is `Integer`, as in the previous lesson. Two jobs are done on the same line: binding the name is the job of the language, that is, syntax; adding two numbers is the job of the class. Only the first is counted as the responder of this lesson, because the lesson is built on name binding.

The common result of the three measurements is this: when a name is written in Ruby, first the sigil of the name is looked at, then the place the sigil points to. The same `x = 5`, once `@` or `$` is put in front, is written to another place, but in all three it is the language itself that runs the `=` mark. For this reason, in later lessons there will be no need to measure assignment separately; when we see `=` in an expression, we do not look for a message.

#### Summary

- The mark at the start of a name determines the kind of the name: a lowercase name with no mark is a local variable, `@` an instance variable, `$` a global variable, and an uppercase name a constant.
- `defined?` reports the kind of an expression without running it: `nil` for an undefined constant, `"assignment"` for an assignment.
- A constant marks the binding of a name but does not freeze the object; an array bound to a constant can change.
- Assignment is not a method call: `defined?` gives `"assignment"` for `a = 1` and `"method"` for `1 + 2`.
- The responder of this lesson is syntax: `y += 1` and `y ||= 3` are assignments, and the addition inside is answered by `Integer`.
- `defined?` measures scope too: a method body does not see the outer local (`nil`), a block does (`"local-variable"`); `a, b = b, a` swaps the places, and `x = y = [1]` binds two names to the same object.

#### Next Step

Name binding was measured as the work of the language. There are other constructs that work with the language's own spelling: `if`, `while`, and `case`. Of these, who chooses the branches of `case`; is a `when` line sending a message to an object? The next lesson, Control Flow, measures this question.

### Control Flow

Source: https://academia.sh/en/courses/ruby-fundamentals/control-flow

The case expression sends the === message to each when branch, while if and while are syntax that sends no message; statement modifiers and the value of if; the responder of case is the class of the branch object.

The previous lesson measured that assignment is not a message but the language's own spelling, and it left a question: of `if`, `while`, and `case`, which ones work with the language's own spelling, and which one really sends a message? The concepts of conditions and loops were introduced in the Programming Fundamentals course (M01/K02); they are not repeated here. This lesson measures only one question: when a `case` branch is chosen, which message goes to which object?

In the Rust course (M10/K01), the question "does this construct produce a value" was measured. In Ruby, nearly every control structure produces a value, and that question returns in this lesson only as a side observation. The main measurement is the message behind the branching.

#### case Sends === to Each Branch

The file below sends the `===` message to a range object and looks at who answers it. Then it puts the same range in a `when` branch inside a `case`.

```ruby
# range_match.rb — case sends the === message to each when branch
range = 1..10

puts range.class
puts range.method(:===).owner
puts range === 7

result = case 7
         when 1..10 then "inside"
         else "outside"
         end
puts result
```

```
Range
Range
true
inside
```

`1..10` is a `Range` object, and `Range` answers the `===` message. When the range is asked about `7`, the reply is `true`. When `case 7 when 1..10` is written, the branch matches as well: `case` makes its value the argument of the `===` message sent to the object of the `when` branch. So `when 1..10` is, behind the scenes, the question `(1..10) === 7`.

**LB10.** In this lesson, the "branch object" is the object written on the `when` line, and the "case value" is the object that comes after the `case` keyword. The receiver of the `===` message is the branch object, and its argument is the case value. The order matters: if `7 === (1..10)` were written, the receiver would be `7` and the responder would change.

#### if and while Send No Message

We saw that the `===` message goes out in the `case` branches. The same question can be asked about `if` and `while`: is a condition chosen by a message? `defined?` answers this directly.

```ruby
# condition_expressions.rb — if and while are syntax
puts defined?(if true then 1 end)
puts defined?(while false do end)
puts defined?(1 + 2)
puts defined?(case 1 when 1 then 2 end)
```

```
expression
expression
method
expression
```

`if` and `while` were reported as `"expression"`; that is, they are structures of the language, not method calls. `1 + 2`, on the other hand, is `"method"`. `case` also came out as `"expression"`: the branching itself is syntax, but each `when` comparison inside it sends a message. There are two layers in the same structure: the `case` skeleton is syntax, and the choice of a branch is a message answered by the class of an object.

#### Statement Modifiers and the Value of if

In Ruby, a condition or a loop can be put at the end of a sentence. These are called statement modifiers: `puts x if y`, `x += 1 until z`. In addition, `if` produces a value; when no branch runs, that value is `nil`.

```ruby
# statement_modifier.rb — statement modifiers and the value of if
x = 3
puts "big" if x > 2
x += 1 until x >= 6
puts x

y = if false then 1 end
p y
z = if true then 1 end
p z
```

```
big
6
nil
1
```

The first line ran because `x > 2` was true. The `until` modifier repeated the addition until `x` reached six. `if false then 1 end` returned `nil` because it ran no branch, and `if true then 1 end` returned `1`. The result: `if` is not a statement but an expression that produces a value. This is the same property as an assignment carrying the value `7`.

#### Truthiness and the Value of a Loop

Whether a condition counts as true or false is also a rule of the language. In Ruby, only two values count as false: `nil` and `false`. Everything else, including `0` and the empty string, counts as true. The file below tries five values in the same condition.

```ruby
# truthiness.rb — only nil and false count as false
[nil, false, 0, "", []].each do |value|
  status = if value then "truthy" else "falsy" end
  printf("%-5s %s\n", value.inspect, status)
end
```

```
nil   falsy
false falsy
0     truthy
""    truthy
[]    truthy
```

`nil` and `false` came out false, and `0`, `""`, and `[]` came out true. This rule is the language's own: only `nil` and `false` count as false, and every other value is true. Loops produce values too. If a `while` loop exits with `break` and a value, that value becomes the result of the loop; if it ends without `break`, the result is `nil`.

```ruby
# loop_value.rb — while produces a value too
n = 0
result = while true
  n += 1
  break n * 10 if n == 3
end
p result

empty = while false do end
p empty
```

```
30
nil
```

The loop exited on the third round with `30`, and `result` carried this value. The loop that never ran gave `nil`. This is the same behavior as `if` producing `nil` when no branch runs: in Ruby, control structures are expressions and have values.

#### Loops and the Values They Produce

The earlier sections measured branches; this section measures loops. The question is again about values: does a loop expression produce a value, and can `break` determine that value? The responder axis appears in two places in this section: `while` and `until` are keywords and syntax, while `loop` is a method and its owner is read from the output. The second block looks at a scope difference: does a variable of `for` behave the same way toward the outside as a block variable?

```ruby
# loops.rb — the value of loops and next
result = loop { break 5 }
puts result
i = 0
i += 1 while i < 3
puts i
j = 10
j -= 1 until j < 8
puts j
empty = while false do end
puts empty.inspect
total = 0
(1..5).each do |n|
  next if n.even?
  total += n
end
puts total
puts method(:loop).owner
```

```
5
3
7
nil
9
Kernel
```

`loop { break 5 }` gave `5` as the value of the loop: the argument of `break` becomes the result of the loop expression. The modifier `i += 1 while i < 3` raised `i` to `3`, and `j -= 1 until j < 8` stopped `j` at `7`; `while` continues as long as the condition stays true, and `until` as long as the condition stays false. The expression `while false do end` produced a value although it never ran, and this value is `nil`. So a loop, like `if`, is an expression and carries a value. Inside `(1..5).each`, `next if n.even?` skipped the even numbers; the remaining `1`, `3`, and `5` were added up and `9` came out. `next` skips an iteration and `break` ends the loop; both are written inside a block and both are keywords of the language; this file printed no owner for them. The last line showed that the owner of `loop` is `Kernel`: `loop` is not a keyword but a method, like `puts`.

```ruby
# for_leak.rb — the for variable leaks out, the block variable does not
[1, 2].each { |k| k }
d = defined?(k)
puts d.inspect
for m in [1, 2] do end
puts defined?(m)
puts m
puts [1, 2].method(:each).owner
puts 1.upto(3).to_a.inspect
puts 3.times.class
```

```
nil
local-variable
2
Array
[1, 2, 3]
Enumerator
```

When the variable `k` of the `each` block was asked about from outside, it returned `nil`: a block variable stays inside the block. `for m in [1, 2] do end`, on the other hand, leaked `m` to the outside: `defined?(m)` reported `"local-variable"`, and in the end `m` was `2`. So a `for` loop does not open a new scope. The scope behavior of two spellings that do the same job differs; this difference turns the choice between them from a matter of taste into a matter of consequence. `[1, 2].method(:each).owner` gave `Array`; that is, the class of the array answers `each`. `1.upto(3).to_a` gave `[1, 2, 3]`, and `3.times`, called without a block, returned an `Enumerator`: `times` called without a block gives an iterator instead of producing a value.

**LB11.** That `for` calls the `each` message of the array behind the scenes was not measured. What was measured is only the scope difference and that the owner of `each` is `Array`.

#### Short-Circuiting and Comparison

Conditions are not built only with `if` and `case`: the `||` and `&&` operators also decide whether to run the right side. This section measures two questions: what is observed when an operator skips evaluating the right side, and who owns the methods that do comparison? The axis is again the responder; `||` and `&&` are syntax of the language, while `<=>` and `between?` are messages.

```ruby
# short_circuit.rb — does the right side run, and who answers
def visit(name)
  puts name
  true
end
visit("a") || visit("b")
visit("c") && visit("d")
v = nil || "fallback"
puts v
w = false && 1
puts w.inspect
puts 1 <=> 2
puts 5.between?(1, 10)
n = 5
verdict = n > 3 ? "big" : "small"
puts verdict
puts 5.method(:<=>).owner
puts 5.method(:between?).owner
```

```
a
c
d
fallback
false
-1
true
big
Integer
Comparable
```

On the line `visit("a") || visit("b")`, only `a` was printed: because the left side was true, the right side never ran. On the line `visit("c") && visit("d")`, both `c` and `d` were printed, because `&&` also looks at the right side when the left side is true. This is short-circuiting: if the result is already determined by the left side, the right side is not evaluated. The expression `nil || "fallback"` gave `fallback`; `||` does not only produce a truth value, it returns the operand itself. `false && 1` returned `false`. Giving a default value with `||` relies on this behavior: if the left side is `nil` or `false`, the value on the right returns.

`1 <=> 2` gave `-1`: the comparison message reports the ordering as an integer. `5.between?(1, 10)` was `true`. The ternary operator `n > 3 ? "big" : "small"` gave `big`. The owner lines revealed two different responders: `Integer` answers the `<=>` message, while the `Comparable` module answers the `between?` message. Both are sent to a number and give a result, but the places where they are defined differ. So the comparison messages used when writing conditions are divided between the methods of the class itself and the methods of a shared module.

#### Error Flow: rescue, else, and ensure

Control flow does not consist of conditions and loops alone; an error changes the flow as well. This section measures the order in which the `rescue`, `else`, and `ensure` branches run, and which of them determines the value of the method. The error class is printed, its message is not. The axis is syntax: `rescue` and `ensure` are keywords, while `raise` is a method.

```ruby
# exception_flow.rb — the order of rescue, else, and ensure
def guarded
  yield
rescue ZeroDivisionError => e
  puts e.class
  :rescued
else
  :clean
ensure
  puts "cleanup"
end
a = guarded { 1 / 0 }
puts a.inspect
b = guarded { 1 / 1 }
puts b.inspect
c = begin
  Integer("x")
rescue ArgumentError
  -1
end
puts c
puts method(:raise).owner
puts method(:guarded).owner
```

```
ZeroDivisionError
cleanup
:rescued
cleanup
:clean
-1
Kernel
Object
```

In the first call, the block computed `1 / 0` and raised `ZeroDivisionError`. The `rescue` branch ran, printed the class, and returned `:rescued`; then the `ensure` branch wrote `cleanup`. The return value stayed `:rescued`: `ensure` does not determine the value of the method. In the second call no error occurred, so the `else` branch ran and the value of the method became `:clean`; `ensure` again wrote `cleanup`. So `ensure` runs on both paths, `rescue` only when there is an error, and `else` only when there is none.

A `begin` block is also an expression and carries a value. `Integer("x")` raised `ArgumentError`, the `rescue` branch returned `-1`, and `c` took this value. This is the counterpart, in error flow, of `if` producing a value: one of the branches runs, and the last expression of the branch that ran becomes the value of the whole. The owner lines showed `Kernel` for `raise` and `Object` for `guarded`: raising an error is a method call, but `rescue` and `ensure` are the language's own spelling. The text of the error message was not printed in this section, because the text of a message may change between versions, while its class does not.

#### The Bounding Measurement

The responder of this lesson is a class. The slip here is to take the responder of `case` to be the class of the value given to `case`. Yet in the expression `case 7`, the class of `7` is `Integer`, but the receiver of the `===` message is the branch object. For this reason, for the same `7`, `Module` answers on the line `when Integer`, `Regexp` on the line `when /a/`, and `Range` on the line `when 1..10`.

```ruby
# branch_object.rb — the responder of case is the class of the branch object
branches = [Integer, /a/, 1..10]

branches.each do |branch|
  printf("%-8s class %-8s owner %s\n", branch.inspect, branch.class, branch.method(:===).owner)
end

puts Integer === 7
puts(/a/ === "table")
puts branches[2] === 7
```

```
Integer  class Class    owner Module
/a/      class Regexp   owner Regexp
1..10    class Range    owner Range
true
true
true
```

Three branch objects, three different owners: for `Integer`, the `Module` class answers the `===` message (the `Integer.===` observation from the previous lesson), for the regular expression `Regexp` does, and for the range `Range` does. Even when the value of `case` is always `7` or the same `"table"`, the only thing that changes is the receiver. What is read as the responder class is the class of the branch object, not of the case value.

The two layers measured in this lesson must be read together. The skeleton of a control structure, that is, the words `if`, `while`, and `case`, is the language's own spelling, and `defined?` reports them as `"expression"`. The comparison inside a branch, on the other hand, is a message, and the class of an object answers it. Separating the two turns the question "why did this branch match" into the question "what does the `===` method of the branch object do" when reading a `case` expression.

#### Summary

- `case` sends the `===` message to the branch object in each `when` branch; `when 1..10` is, behind the scenes, the question `(1..10) === 7`.
- The receiver of the `===` message is the branch object and its argument is the case value; if the order is reversed, the responder changes.
- `if` and `while` are reported as `"expression"` by `defined?`; they are structures of the language and send no message.
- Statement modifiers (`if`, `unless`, `while`, `until`) make a sentence conditional or looped; `if` produces a value, and returns `nil` when no branch runs.
- The responder of this lesson is a class: the responder of `case` is the class of the branch object, not of the case value.
- `loop { break 5 }` returns `5` and `while false do end` gives `nil`; the `for` variable leaks out and the block variable does not; `||` and `&&` do not run the right side unless needed, and `ensure` runs on every path.

#### Next Step

We measured who answers the condition and the choice of branch. But when the receiver is never written in a method call, for example when `puts` or `format` is written, who handles the message? The next lesson, Methods, measures the responder of a receiverless call.

### Methods

Source: https://academia.sh/en/courses/ruby-fundamentals/methods

Receiverless calls live in the Kernel module; method definitions with default values, variadic arguments, and keyword arguments; implicit return; a top-level def is written into Object and stays private.

The previous lesson showed that `case` sends a message to the branch object when choosing a branch, and it left a question: when `format("%03d", 7)` or `puts "name"` is written, there is no receiver in sight. Where does the message go, and who answers it? The concept of a function was introduced in the Programming Fundamentals course (M01/K02); it is not repeated here. This lesson reads Ruby's methods with a single measure: who is the owner that handles a receiverless call?

#### Who Handles a Receiverless Call

In the expression `format("%03d", 7)` there is no dot and no receiver. But we learned in the earlier lessons: in Ruby every call is a message, and a message has a receiver. When no receiver is written, the receiver is the object the code is running in at that moment. At the top level, this object is the main object of the file. The file below uses `defined?` to ask whether this call is a method, and `method` and `owner` to ask who owns it.

```ruby
# receiverless.rb — the responder of a receiverless call
puts defined?(format("%03d", 7))
puts method(:format).owner
puts method(:format).owner.instance_of?(Class)
puts format("%03d", 7)
```

```
method
Kernel
false
007
```

`format` is a method, and its owner is `Kernel`. `Kernel` is a module, not a `Class`, so `instance_of?(Class)` returned `false`. In this case the classification rule of the course counts the responder as a module. The top-level object of the file also reaches the `Kernel` methods, so even when no receiver is written, the call finds an owner.

**LB12.** In this lesson, a "method" is a unit of work that is defined with `def` or by the language and called by its name. `method(:name).owner` gives the owner of the method, that is, the class or module where its definition is found. The lookup chain itself (the order in which places are searched) is not measured in this course; only the result, the owner, is printed.

#### Familiar Calls in Kernel

In the earlier lessons, `puts` and `p` were used without any explanation. The file below asks for the owner of all six calls.

```ruby
# kernel_calls.rb — the owner of receiverless calls
[:puts, :p, :format, :Integer, :raise, :block_given?].each do |name|
  printf("%-14s %s\n", name, method(name).owner)
end
```

```
puts           Kernel
p              Kernel
format         Kernel
Integer        Kernel
raise          Kernel
block_given?   Kernel
```

All six are in `Kernel`. `puts` and `p` write to the screen, `format` formats, `Integer` converts, `raise` raises an exception, and `block_given?` says whether a method has a block. They all look like "words of the language", but each is actually an ordinary method with a known owner. This differs from `if` and `while` in the previous lesson: those were syntax, and these are messages.

#### Parameter Kinds and Implicit Return

We write our own method with `def`. The parameter list can hold required arguments, default values, variadic arguments, and keyword arguments together. Even when `return` is not written, the method returns the value of its last expression; this is called implicit return.

```ruby
# parameters.rb — parameter kinds and implicit return
def assemble(name, title = "mr", *extras, city: "north")
  "#{title} #{name} #{extras.size} #{city}"
end

puts assemble("pat")
puts assemble("pat", "dr", 1, 2, city: "south")
p method(:assemble).parameters
```

```
mr pat 0 north
dr pat 2 south
[[:req, :name], [:opt, :title], [:rest, :extras], [:key, :city]]
```

In the first call only `name` was given; `title` and `city` took their default values, and `extras` stayed empty. In the second call, `"dr"` overrode the default, `1` and `2` were collected into the `extras` array as variadic arguments, and `city:` was given as a keyword argument. `parameters` labels the kind of each one: `req` for required, `opt` for one with a default, `rest` for variadic, and `key` for keyword. There is no `return` in the method; the last expression, the string, was returned. The `#{...}` spelling inside the string is string interpolation.

#### Methods That Take a Block

A block can be given to a method call with curly braces or `do ... end`. The method runs the block with `yield` and asks whether a block was given with `block_given?`. The owner of `block_given?` came out as `Kernel` two sections earlier: this too is an ordinary method. In a call without a block, the method can return a different value. What happens when a required argument is missing can also be measured.

```ruby
# block_receiving.rb — yield and block_given?
def repeat(count)
  return :empty unless block_given?
  count.times { |index| yield index }
  :done
end

p repeat(2) { |index| puts index }
p repeat(2)

begin
  repeat
rescue ArgumentError => e
  puts e.class
end
```

```
0
1
:done
:empty
ArgumentError
```

In the call with a block, the block ran twice (`0` and `1` were written) and the method returned `:done`. In the call without a block, `block_given?` was `false`, so the method returned early with `:empty`. The call that did not give the required `count` argument raised `ArgumentError`, and only its class was printed. So the interface of a method is not only its parameter list; the block is also part of the call, but it does not appear in the parameter list of the method. If the block is named explicitly in the form `&block`, it enters the list; the `:block` label in a later section shows this.

#### Returning Multiple Values

In Ruby, a method returns a single value, but that value can be an array, and the caller can split the array into pieces in one step. This also works together with implicit return: the last expression of the method is a two-element array in square brackets.

```ruby
# multiple_returns.rb — a method that returns an array and multiple assignment
def divide(a, b)
  [a / b, a % b]
end

result = divide(17, 5)
p result
quotient, remainder = divide(17, 5)
puts quotient
puts remainder
puts result.class
```

```
[3, 2]
3
2
Array
```

The method returned a single `Array`. The spelling `quotient, remainder = ...` unpacks this array into two names; this is not a feature of methods but the spelling of assignment, and it ties back to the syntax observation of the earlier lesson. So the return value of the method and how the caller reads it remain two separate jobs.

#### Probing the Parameter Kinds

This section measures the parameter kinds. In the method below, seven parameter kinds stand together: required, with a default, splat (`*rest`), required keyword, optional keyword, keyword collector (`**opts`), and block taker (`&block`). The measure is the method describing itself: `method(:name).parameters` reports the parameter list of the method, and `arity` reports how many arguments it expects. The axis is the owner; the owner of the defined method is printed too.

```ruby
# parameter_kinds.rb — parameter kinds and arity
def example(a, b = 2, *rest, c:, d: 4, **opts, &block); end
puts method(:example).parameters.inspect
puts method(:example).arity
def two(a, b); end
puts method(:two).arity
def with_default(a, b = 1); end
puts method(:with_default).arity
puts method(:example).owner
```

```
[[:req, :a], [:opt, :b], [:rest, :rest], [:keyreq, :c], [:key, :d], [:keyrest, :opts], [:block, :block]]
-3
2
-2
Object
```

The `parameters` output gave each parameter with a kind label: `:req` is required, `:opt` has a default, `:rest` is splat, `:keyreq` is required keyword, `:key` is optional keyword, `:keyrest` is the keyword collector, and `:block` is the block-taking parameter. The `arity` values showed three separate cases: `2` for `two`, which has two required parameters, `-2` for `with_default`, which has one required and one with a default, and `-3` for `example`. A negative number means "not a fixed number of arguments"; there are optional or unbounded parameters. The absolute value is one more than the minimum number of arguments needed: `-2` asks for at least one, and `-3` asks for at least two. In `example`, next to the required `a` there are a required keyword `c:` and optional parameters, and the result was `-3`. A required keyword parameter counts as a single extra argument in this calculation. The rule was measured here only on these three methods. On the last line the owner of `example` came out as `Object`: the responder of a method defined at the top level is `Object`.

**LB13.** The sign of the `arity` value was read only on these three methods. That the same rule holds for other parameter layouts was not measured.

```ruby
# key_error.rb — missing and extra keywords, and implicit return
def box(width:, height: 1)
  width * height
end
begin
  box(height: 2)
rescue ArgumentError => e
  puts e.class
end
begin
  box(width: 1, depth: 3)
rescue ArgumentError => e
  puts e.class
end
puts box(width: 3)
def implicit
  42
end
def explicit
  return 42
end
puts implicit == explicit
def early(n)
  return :negative if n < 0
  :positive
end
puts early(-1).inspect
puts early(1).inspect
puts method(:box).owner
```

```
ArgumentError
ArgumentError
3
true
:negative
:positive
Object
```

`box(height: 2)` was called without the required `width:` keyword and raised `ArgumentError`; `box(width: 1, depth: 3)` raised `ArgumentError` again because an undefined keyword was given. In both cases only `e.class` was printed, because the text of an error message may depend on the version. The valid call `box(width: 3)` gave `3`. These three results show that keyword parameters are a contract: a missing one and an extra one are both rejected with the same class. In the second half, implicit return was compared with `return`: `implicit` and `explicit` returned the same `42`. `return` is not redundant; the `early(-1)` line shows that `return` ends the method early, and the `early(1)` line shows that the value of the last expression is the return value. The owner of the method came out as `Object` for `box` as well. So the return value is determined in two ways: by the last expression of the method, or by the value given with `return`.

#### Holding a Method as an Object

A method can also be turned into an object. The call `method(:name)` gives the method as an object of the `Method` class; this object can be called, converted into a block, and asked for its receiver and owner. The question of this section is who a method defined at the top level belongs to, and whether it appears public when asked from the outside. The axis is the owner; we print the receiver and the owner together.

```ruby
# method_object.rb — holding a method as an object
def square(n)
  n * n
end
m = method(:square)
puts m.class
puts m.call(4)
puts [1, 2, 3].map(&m).inspect
puts m.name.inspect
puts m.receiver == self
puts m.owner
puts send(:square, 3)
puts respond_to?(:square, true)
puts respond_to?(:square)
```

```
Method
16
[1, 4, 9]
:square
true
Object
9
true
false
```

`method(:square)` gave a `Method` object. `m.call(4)` returned `16`; because the method is an object, it could be held in a variable and called later. The expression `[1, 2, 3].map(&m)` converted the method object into a block with `&` and gave `[1, 4, 9]`. The name `:square` is a symbol. `m.receiver == self` was `true`: the receiver of a method defined at the top level is the top-level `main` object, and `m.owner` came out as `Object`. This also explains why a receiverless call `square(3)` works: when no receiver is written, the message goes to the current `self` object.

The last three lines measure the difference in access. `send(:square, 3)` gave `9`, so sending the message by its name worked. `respond_to?(:square, true)` printed `true`, and `respond_to?(:square)` printed `false`. The second argument says to count private methods too; the question without an argument counts only public methods, and the top-level method is not among them. So `square` is defined in the `Object` class and is not counted among the public methods, but it can be reached with a receiverless call or with `send`. What a call written with an explicit receiver would give was not measured here. This difference shows that the `Object` owner and the access level are two separate properties.

**LB14.** That a top-level method is private was inferred from the `respond_to?` pair; the access level was not verified with another measurement (such as `private_methods`).

#### Splat on the Call Side

The `*` and `**` marks seen in a parameter definition do the opposite on the call side: they expand an array or a hash into the argument list. This section measures both directions together. The axis is the owner; how many arguments a method can expect is printed with `arity`, and who defined it with `owner`.

```ruby
# splat_call.rb — * and ** on the call side
def add_all(*numbers)
  numbers.sum
end
list = [1, 2, 3]
puts add_all(*list)
puts add_all(list.first, *list.last(2))
puts add_all
def options(**o)
  o.inspect
end
h = {a: 1}
puts options(**h)
puts options(b: 2, **h)
def single(x)
  x
end
puts single([1, 2]).inspect
puts method(:add_all).arity
puts method(:add_all).owner
```

```
6
6
0
{a: 1}
{b: 2, a: 1}
[1, 2]
-1
Object
```

`add_all(*list)` expanded the array into three separate arguments and gave `6`. The spelling `add_all(list.first, *list.last(2))` combined one single argument with two expanded ones and again came out as `6`. The call `add_all` with no arguments gave `0`: a splat parameter accepts an empty array. `options(**h)` expanded the hash into keyword arguments and gave `{a: 1}`; `options(b: 2, **h)` combined an explicitly written key with an expanded key and gave `{b: 2, a: 1}`. In the merge, the explicitly written key came first.

The contrasting example is the call `single([1, 2])`: an array given without a star stayed a single argument, and the method returned `[1, 2]`. So expansion is written on purpose on the call side; where there is no star, an array is an array. `method(:add_all).arity` gave `-1`: no fixed number of arguments is expected, and at least zero are needed. The owner came out as `Object`. The spelling of the language expands the arguments of a call, but what receives the expanded list is still the parameter definition of the method; when the two sides match, the call works. The parameter definition and the call side use the same mark, but their directions are opposite: in a definition `*` collects the arguments, and in a call `*` expands the array.

#### The Bounding Measurement

The responder of this lesson is a module, but there is a slip: does the owner of a method we write ourselves also become a module? When `def` is written at the top level of a file, where does the method go? The measurement below asks this.

```ruby
# top_level.rb — a top-level def is written into Object and is private
def square(n)
  n * n
end

puts method(:square).owner
puts method(:square).owner.instance_of?(Class)
puts respond_to?(:square)
puts respond_to?(:square, true)
puts square(4)
```

```
Object
true
false
true
16
```

A top-level `def square` writes the method into the `Object` class. `Object` is an instance of `Class` (`true`), so in this case the responder is not a module but a class. The method also stays private: `respond_to?(:square)` returned `false`, because private methods are invisible when asked from the outside. `respond_to?(:square, true)` includes private methods and gives `true`. The receiverless call `square(4)` returned `16`, because when no receiver is written, private methods are reachable too. The methods in `Kernel` are owned by a module; our own `def` is owned by `Object`, that is, by a class.

#### Summary

- In a receiverless call, the receiver is the object the code is running in at that moment; the `Kernel` module answers the call `format("%03d", 7)`.
- `puts`, `p`, `format`, `Integer`, `raise`, and `block_given?` are ordinary methods, and the owner of all six is `Kernel`.
- A method can carry required arguments, default values, variadic arguments, and keyword arguments together; if `return` is not written, the last expression is returned.
- A top-level `def` writes the method into the `Object` class and leaves it private; `respond_to?(:square)` returns `false` and `respond_to?(:square, true)` returns `true`.
- The responder of this lesson is a module: the owner of receiverless calls is the `Kernel` module, while the owner of a top-level method we write ourselves is `Object`, which is a class.
- `method(:name).parameters` names the seven parameter kinds in the measured method; a missing or undefined keyword gives `ArgumentError`; on the call side `*` and `**` expand an array and a hash into arguments.

#### Next Step

The owners of methods were measured, and we saw where a receiverless call goes. Next come strings: when `"name".to_sym` is written, a string turns into a symbol, and a symbol is also an object. Once a string turns into a symbol, who answers the message, and does a magic comment change that? The next lesson, Symbols and Strings, measures this.

### Symbols and Strings

Source: https://academia.sh/en/courses/ruby-fundamentals/symbols-and-strings

A string turning into a symbol and the identity difference between the two representations; string literals frozen by a magic comment, and the comment taking effect per file; the magic comment not changing the responder.

The previous lesson measured the responder of a receiverless call, and it left a question: when `"name".to_sym` is written, a string turns into a symbol, and a symbol is also an object. Once a string turns into a symbol, who answers the message? And does a magic comment written at the top of a file change that answer? In the JavaScript Fundamentals course (M06/K01), the primitive types and the symbol were measured; in Ruby, a symbol is not a separate primitive but an instance of the `Symbol` class. A string, on the other hand, is a mutable object, and this lesson measures the difference between those two objects.

In M11/K03, the question "who sees this change" was measured. In this lesson, a mutable string is only a subject of observation; a single example is seen in which two names share the same string, but the axis of the lesson is the responder, not sharing.

#### From String to Symbol

A symbol is an immutable value that has a name: `:name`. It is obtained from a string with `to_sym`. The file below measures the conversion, the identity of the two representations, and the methods of the symbol itself. `equal?` is used for identity: do two expressions point at the same object?

```ruby
# symbol_conversion.rb — a string turns into a symbol
puts "name".method(:to_sym).owner
p "name".to_sym
puts :name.equal?(:name)
puts "name".equal?("name")
p :name.to_s
p :name.upcase
puts :name.method(:to_s).owner
puts :name.method(:upcase).owner
```

```
String
:name
true
false
"name"
:NAME
Symbol
Symbol
```

`to_sym` is defined in the string's own class, and the return value is printed as `:name`. The real difference is in identity: `:name.equal?(:name)` returned `true`, so a symbol with the same name is always the same object. `"name".equal?("name")` is `false`: two string literals are two separate objects even when their contents are the same. `:name.to_s` produces a string again, and `:name.upcase` produces a new symbol; the `Symbol` class answers both.

**LB15.** In this lesson, a "string literal" is a string written in quotes in the source code. What is measured is whether it produces a new string object on every evaluation. No address or `object_id` is printed; identity is compared only with `equal?`.

#### String Operations

A string carries many methods. The file below tries length, upper-casing, slicing, splitting, joining, repeating, and formatting on a string. The identifiers and the printed strings were kept ASCII.

```ruby
# string_operations.rb — string methods
text = "welcome"

puts text.length
puts text.upcase
puts text[0, 3]
p "a,b,c".split(",")
puts text + " world"
puts "ha" * 3
puts "%s-%03d" % ["code", 7]
puts "name".to_sym.class
puts :name == "name"
```

```
7
WELCOME
wel
["a", "b", "c"]
welcome world
hahaha
code-007
Symbol
false
```

`length` gives seven, `upcase` gives the upper-case copy, and `[0, 3]` gives the first three characters. `split` returns an array, `+` joins two strings, `*` repeats, and `%` formats. These operations were called not to change `text` but to produce a new value; `length` returned a number, `split` an array, and the others a new string. The last two lines show the boundary between the two representations: `"name".to_sym` gives a `Symbol`, and `:name == "name"` returns `false`; a symbol and a string that carries the same name are not counted as equal.

#### Character and Byte

A string is a sequence of characters, but it is kept in memory as bytes. Turkish letters are a single character each, yet they take more than one byte. Turkish letters are used on purpose in this measurement, because this is exactly its subject.

```ruby
# char_byte.rb — the character count and the byte count are separate things
puts "ig".length
puts "ig".bytesize
puts "ığ".length
puts "ığ".bytesize
```

```
2
2
2
4
```

`"ig"`, made of ASCII letters, is two characters and two bytes. `"ığ"` stays two characters but grows to four bytes, because `ı` and `ğ` take two bytes each. So `length` counts characters and `bytesize` counts bytes; the two diverge in non-ASCII text. The encoding of the symbol was not measured here; this lesson measures the identity of a symbol, not its content.

#### Symbol Spellings

There is more than one way to write a symbol. A name that contains a space is written in quotes, the `%i[...]` shortcut is used to write several symbols together, and string interpolation works in a symbol just as in a string.

```ruby
# symbol_spellings.rb — ways to write a symbol
p :"full name"
p %i[alpha beta gamma]
label = "rose"
p :"prefix_#{label}"
p :ant <=> :bee
p %i[gamma alpha beta].sort
```

```
:"full name"
[:alpha, :beta, :gamma]
:prefix_rose
-1
[:alpha, :beta, :gamma]
```

`:"full name"` is the quoted spelling, and it keeps its quotes when printed, because the name contains a space. `%i[...]` produces an array of three symbols. `:"prefix_#{label}"` builds the symbol at run time with interpolation. Symbols can be ordered: `<=>` gives `-1` according to the character order of the names, and `sort` arranges the array accordingly. A symbol is still an object; these spellings only open a different door to it.

#### The Magic Comment

The line `# frozen_string_literal: true`, written at the very top of a file, is called a magic comment. It freezes the string literals in the file. In the file below the comment is on the first line, and the line carrying the file name is the second.

```ruby
# frozen_string_literal: true
# commented.rb — the magic comment freezes string literals
puts "name".frozen?
puts "name".equal?("name")

text = "name"
begin
  text << "x"
rescue FrozenError => e
  puts e.class
end

puts "name".dup.frozen?
mutable = +"name"
puts mutable.frozen?
```

```
true
true
FrozenError
false
false
```

While the comment was in effect, `"name".frozen?` was `true`, and two separate `"name"` literals turned out to be the same object (`equal?` was `true`): because the literals are frozen, Ruby can share the same content in a single object. Appending to the string with `<<` raised `FrozenError`; only the class of the caught exception was printed. `dup` does not give a frozen copy, and `+"name"` returns an unfrozen string; both gave `false`. A frozen string stays frozen; if a mutable string is needed, a copy of it is taken.

#### Measuring the Difference Between a Symbol and a String

The earlier sections measured the spellings and the mutability of symbols and strings. This section puts their conversion into each other and their identity behavior side by side. The axis is the owner: who answers the `to_s` and `<<` messages?

```ruby
# round_trip.rb — a round trip between a symbol and a string
s = :name
puts s.to_s
puts s.to_s.to_sym == s
puts s.equal?(:name)
puts "name".equal?("name")
puts "name".to_sym.equal?(:name)
label = "Anna"
puts "Hello #{label}"
puts 'Hello #{label}'
puts "Hello #{label.size + 1}"
puts s.method(:to_s).owner
```

```
name
true
true
false
true
Hello Anna
Hello #{label}
Hello 5
Symbol
```

`:name.to_s` gave `"name"`, and the way back with `to_sym` produced the same symbol: `s.to_s.to_sym == s` is `true`. In this example the conversion lost no information on the round trip. Identity is a separate question. `s.equal?(:name)` gave `true`: the same symbol spelling is the same object everywhere. `"name".equal?("name")` gave `false`: even though the two string literals carry the same text, they are two separate objects. `"name".to_sym.equal?(:name)` was `true` again: once a string moves to a symbol, the identity is the identity of the symbol. These three measurements show the division of labor between a symbol and a string: a symbol is used for identity, and a string is used for text. If we are going to hold a name as a label, it helps that a symbol is the same object everywhere; if the text is going to be changed or formatted, a string is needed.

Interpolation worked in double quotes: `"Hello #{label}"` printed `Hello Anna`, while the single-quoted `'Hello #{label}'` printed the text as it is, including `#{label}`. Inside `"#{label.size + 1}"` an expression ran and `5` was placed, because `Anna` has four letters. On the last line, the owner of the `to_s` message came out as `Symbol`: the conversion of a symbol to a string is answered by the class of the symbol itself. That interpolation does not work in single quotes prevents the two spellings from being used in place of each other: whoever wants to add the value of a variable into the text chooses double quotes, and whoever wants to keep the text as it is chooses single quotes. The interpolated expression can also be a number; the output `Hello 5` shows this.

```ruby
# in_place_new.rb — << is in place, + makes a new object; the Turkish letter measurement
a = +"name"
b = a
a << "x"
puts b
c = a + "y"
puts c
puts a.equal?(b)
puts a.equal?(c)
puts "ı".upcase
puts "İ".downcase.size
puts "ı".size
puts "ı".bytesize
puts a.method(:<<).owner
```

```
namex
namexy
true
false
I
2
1
2
String
```

`a << "x"` worked in place: `b`, which points at the same object as `a`, became `namex` too. `a + "y"`, on the other hand, produced a new string and did not change `a`; `c` became `namexy`. `a.equal?(b)` gave `true` and `a.equal?(c)` gave `false`: `<<` changes the same object, and `+` returns a new object. The owner of `<<` came out as `String` on the last line. The file started with `+"name"`, because this spelling gives a mutable string; there is no magic comment in this block.

The Turkish letters make a separate measurement. `"ı".upcase` gave `I`. `"İ".downcase.size` gave `2`: a single letter turned into two characters. If the expected result were a single-character `i`, the size would be `1`. `"ı".size` came out `1` and `"ı".bytesize` came out `2`: one character, two bytes. This is why the character count and the byte count cannot be used in place of each other. What these two measurements have in common is that they show the limit of thinking of a string as a flat sequence of characters. `size` counts characters and `bytesize` counts bytes; `upcase` and `downcase` do not have to turn one letter into one letter. Code that computes the length of a string, or whether it fits in a field, must know which unit it is counting.

**LB16.** That the result of `"İ".downcase` is two characters is the result of the default letter mapping of this version. That it is the same in another version or locale was not measured.

#### Formatting and Comparison

A string is not only joined and changed; it is also formatted and sorted. This section writes a string with a format specification, aligns it, repeats it, moves it to the next string, compares it, and converts between a letter and a code point. The axis is the owner: of the three messages whose owner is printed, two are owned by `String` and one by `Kernel`.

```ruby
# formatting.rb — formatting and comparing a string
puts "%05.1f" % 3.14159
puts format("%-4s|", "ab")
puts "ab".center(6, "*")
puts "ab" * 3
puts "az".succ
puts "a" <=> "b"
puts "Ab".casecmp?("aB")
puts "a".ord
puts 98.chr
puts "ab".method(:center).owner
puts "ab".method(:%).owner
puts method(:format).owner
```

```
003.1
ab  |
**ab**
ababab
ba
-1
true
97
b
String
String
Kernel
```

The expression `"%05.1f" % 3.14159` gave `003.1`: the `%05.1f` format wrote the number with a total width of five, one decimal, and zero padding at the start. `format("%-4s|", "ab")` uses the same specification language; the `-` mark left-aligns the text and `ab  |` came out. Their owners differ: `%` is a method of `String`, and `format` is a method of `Kernel`. The same format language can be called through two different doors. `"ab".center(6, "*")` added stars on both sides and gave `**ab**`, and `"ab" * 3` wrote the string three times in a row: `ababab`.

`"az".succ` gave `ba`: the next string is the value that follows in order, starting from the last letter, and after `z` comes `a` with a carry into a left digit. `"a" <=> "b"` gave `-1`; the ordering message reports the result as a number. `"Ab".casecmp?("aB")` returned `true`, ignoring case. `"a".ord` gave `97`, and `98.chr` returned `b`; the two directions are inverses of each other. The owner lines showed `String` for `center` and `%`; in these two jobs, where the receiver is a string, the responder is the string's own class. No owner was printed for `succ`, `<=>`, `casecmp?`, and `ord`. Formatting a string needs no separate helper class: a string carries alignment, repetition, and format specification as its own methods.

#### Encoding and the Byte Reality

The character count and the byte count of a string can differ; this difference comes from the encoding of the string. This section asks about the encoding directly: the name of the encoding, the code point of a letter, validity, and the raw byte view. The axis is the owner; which class answers the byte query is printed at the end.

```ruby
# encoding.rb — encoding, code point, and byte
puts "a".encoding
puts "ı".bytes.inspect
puts "ı".ord
puts 305.chr(Encoding::UTF_8)
puts "ı".valid_encoding?
puts "\xff".valid_encoding?
puts "ı".unpack("U*").inspect
puts "ı".b.size
puts "a".method(:bytes).owner
```

```
UTF-8
[196, 177]
305
ı
true
false
[305]
2
String
```

The encoding of a string in the source file came out as `UTF-8`. `"ı".bytes` gave two bytes, `[196, 177]`; `"ı".ord` gave the code point of the letter, `305`. A byte and a code point are two separate numbers: the code point identifies the letter, and the bytes identify the encoded form of the letter. `305.chr(Encoding::UTF_8)` produced the letter back from the code point, and `ı` was printed. So `ord` and `chr` form a round trip.

The validity measurement gave two results. `"ı".valid_encoding?` was `true`, and `"\xff".valid_encoding?` was `false`: the byte `0xff` on its own does not form a valid letter in UTF-8 encoding. `"ı".unpack("U*")` returned `[305]`; that is, the same letter is decoded into a single code point from the combination of two bytes. `"ı".b.size` gave `2`: because `b` turns the string into its raw byte view, the size showed the byte count, not the character count. The `size` and `bytesize` difference of the earlier section reappears here at the level of encoding. The owner came out as `String`: the byte query is answered by the string's own class as well.

#### The Bounding Measurement

The responder of this lesson is a class. Two slips are possible here. The first: one may think the magic comment changes the responder of a message. The second: one may think that once the comment is written, it affects the whole program. Both are wrong; the measurement puts two files side by side. First a file without the comment is written to disk, and then the main file with the comment pulls it in with `require_relative`.

```ruby
# uncommented.rb — no magic comment
def uncommented_frozen
  "name".frozen?
end

def uncommented_owner
  "name".method(:upcase).owner
end
```

```ruby
# frozen_string_literal: true
# main.rb — the comment is per file
require_relative "uncommented"

puts "name".method(:upcase).owner
puts uncommented_owner
puts "name".frozen?
puts uncommented_frozen
```

```
String
String
true
false
```

The owner of the message `"name".upcase` is the same in both files: `String`. The comment did not change the responder; what changed is the production of the literal. `"name".frozen?` was `true` in the file with the comment and stayed `false` in the file without it, although both ran in the same process. The comment takes effect per file: a comment in one file does not freeze the literals of another file that it pulls in.

#### Summary

- `"name".to_sym` turns a string into a symbol and the `String` class answers the conversion; `:name.to_s` and `:name.upcase` belong to the `Symbol` class.
- A symbol with the same name is always the same object (`:name.equal?(:name)` is `true`); two string literals are different objects even if their contents are the same.
- The magic comment `# frozen_string_literal: true` freezes string literals: `frozen?` is `true`, and `<<` raises `FrozenError`.
- `dup` and `+"name"` give an unfrozen string; a frozen string itself stays frozen.
- The responder of this lesson is mostly a class (`String`, `Symbol`; the `Kernel` module for `format`): the magic comment does not change the responder of a message and affects only the literals in the file where it appears.
- `size` counts characters and `bytesize` counts bytes (`1` and `2` for `"ı"`); `<<` changes in place and `+` produces a new string; `ord` and `chr` form a round trip for the code point.

#### Next Step

Strings and symbols were measured, but the question of who answers was always asked about single-valued objects. In a collection, for example in a hash, when a key that does not exist is asked about, who gives the answer? The next lesson, Arrays and Hashes, measures who supplies the value of a missing key.

## Collections and Blocks

Arrays and hashes, blocks that pass behaviour into a method, Enumerable as a shared iteration interface, the difference between a proc and a lambda, catching and retrying exceptions, and text processing flows with files.

### Arrays and Hashes

Source: https://academia.sh/en/courses/ruby-fundamentals/arrays-and-hashes

Ruby's two basic collections, the array and the hash, are measured for which methods they answer themselves, which a shared module answers, and how a block decides the value of a missing key.

The Symbols and Strings lesson measured that the `String` class answers the message `"name".to_sym`, and that the resulting `:name` symbol can be compared with `equal?` because it carries the same value as a single object. The symbol's real job did not show up in that lesson yet: being a key in a hash. This lesson takes the two basic collections, the array and the hash, and asks them the same question the course always asks: who answers the message sent to a method?

The course's four responders do not change: **class**, **module**, **block** and **syntax**. In prose, a bare "class" is always Ruby's `Class`, and the measurement category is called a "class responder". Variables, types and loops were established as concepts in M01/K02; here an array is an ordered list of elements numbered from zero. A hash is a mapping from keys to values, and it keeps its elements in insertion order.

#### Basic Operations

The first measurement only shows behavior. An array is written with square brackets, `<<` appends an element, a negative index counts from the end, and a range gives a slice. A hash is written with curly braces and the `key: value` form; in this form the keys are symbols.

```ruby
# basics.rb — basic operations on arrays and hashes
items = [10, 20, 30]
items << 40
puts "first=#{items[0]} last=#{items[-1]} count=#{items.size} outside=#{items[10].inspect}"
puts "slice=#{items[1..2].inspect} first_two=#{items.first(2).inspect}"

person = { name: "Alice", age: 30 }
person[:city] = "Izmir"
puts "name=#{person[:name]} missing=#{person[:mail].inspect} keys=#{person.keys.inspect}"
person.each { |key, value| puts "#{key} -> #{value}" }
puts person.select { |_, value| value.is_a?(String) }.inspect
puts person.map { |key, value| key }.inspect
```
```
first=10 last=40 count=4 outside=nil
slice=[20, 30] first_two=[10, 20]
name=Alice missing=nil keys=[:name, :age, :city]
name -> Alice
age -> 30
city -> Izmir
{name: "Alice", city: "Izmir"}
[:name, :age, :city]
```

An index outside the array raises no error and returns `nil`; so does a key that is not in the hash. These two silent `nil` results are the ground for the second half of this lesson: what determines the result for a missing key? A hash keeps insertion order; the order on the `keys` line shows that `city` was appended at the end. `select` and `map` took the same two-parameter block form, but one returned a hash and the other an array. The reason is the ownership difference measured in the next section. **CB1.** The measurements were made with symbol-keyed hashes; hashes with string keys were not measured in this lesson and the results are not carried over to them.

#### Which Definition Carries the Method

Two methods written on the same collection can be defined in different places even when they are called the same way. `method(:name).owner` gives the class or module that defines that method. This lesson does not show the lookup order; it only reads the result, the owner.

```ruby
# collection.rb — who owns the collection methods
items = [1, 2, 3]
pairs = { a: 1, b: 2 }

puts "items.size    #{items.method(:size).owner}"
puts "items.map     #{items.method(:map).owner}"
puts "pairs.each    #{pairs.method(:each).owner}"
puts "pairs.select  #{pairs.method(:select).owner}"
puts "pairs.fetch   #{pairs.method(:fetch).owner}"
puts "pairs.map     #{pairs.method(:map).owner}"
```
```
items.size    Array
items.map     Array
pairs.each    Hash
pairs.select  Hash
pairs.fetch   Hash
pairs.map     Enumerable
```

In Ruby 4.0.7, a class answered five of the methods: `size` and `map` are defined in `Array`, while `each`, `select` and `fetch` are defined in `Hash`. The sixth is different: `pairs.map` is not defined in `Hash` but in the `Enumerable` module. So the same `map` name is answered by the class on an array and by the module on a hash. Which definition holds which method can shift from version to version, so the result is read as a measurement of this version, not as a promise of the language. The `Enumerable` module is the third lesson and is only named here. In the previous section `select` returned a hash and `map` returned an array: `select` is `Hash`'s own method, so it builds its result as a hash too, while `map`, which comes from `Enumerable`, always produces an array.

#### Who Supplies the Value of a Missing Key

In the basic operations, `person[:mail]` returned `nil`. To change that behavior, a default value can be given when the hash is built. There are two forms: `Hash.new(0)` supplies a fixed value, and `Hash.new { ... }` supplies a **block** that runs when the key is missing. The block can take the key as a parameter, so the result can depend on the key. M12/K02 measured where a change to a shared default value ends up in PHP; that question is not asked here, only who produces the default value.

The course's shared helper `reply.rb` is given first, verbatim. It classifies an expression with three questions in order: does `defined?` say it is a method call, do two different blocks give two different results, is the method's owner a `Class`. It has no output block, so it is only written to disk.

```ruby
# reply.rb — helpers that classify who answers a message

# Classification asks in order: is the expression a message at all (defined?),
# then do two different blocks give two different results, then who owns the method.
def responder(definition, owner: nil, result: nil, with_blocks: nil)
  return ["syntax", "defined? #{definition}"] unless definition == "method"

  if with_blocks
    first, second = with_blocks.call
    return ["block", "#{first.inspect} / #{second.inspect}"] unless first == second
  end

  kind = owner.instance_of?(Class) ? "class" : "module"
  [kind, "#{owner}, result #{result.inspect}"]
end
```

Now the core's `default value` observation runs. Two `Hash.new` blocks look at the same `[:x]` message: one always returns `0`, the other returns the text form of the key. After that, the fixed-default form and the two cases of `defined?` are printed.

```ruby
# defaults.rb — who supplies the value of a missing key
require_relative "reply"

zero = Hash.new { 0 }
text = Hash.new { |_, key| key.to_s }

kind, evidence = responder(defined?(Hash.new), with_blocks: -> { [zero[:x], text[:x]] })
puts "#{kind}  #{evidence}"
puts Hash.new(0)[:x]
puts defined?(Hash.new)
puts defined?(Hash.new { 0 })
```
```
block  0 / "x"
0
method
expression
```

The first line is the core's `default value` line: the **responder** is a **block**. The same `[:x]` message gave `0` and `"x"` on two hashes; the class of the object receiving the message was the same (`Hash`), and the only difference was the block supplied. So the answer is decided by the block, not by a class or a module. On the second line, `Hash.new(0)[:x]` with a fixed default also gave `0`; there is no block there, the value was supplied at construction.

The last two lines show a subtlety of the rule. `defined?(Hash.new)` returns `"method"`, but `defined?(Hash.new { 0 })`, the call with a block, returns `"expression"`. This is why the core queries the message **without** a block and measures the effect of the block in the second step; if it queried the block form, the first step would count the message as syntax. **CB2.** The `defined?` result for a call with a block was measured in Ruby 4.0.7; the same result is not assumed in another version. This detail is named in the Blocks lesson.

#### Array Operations

The Basic Operations section wrote only `<<` and single-index access. The other common array operations are appending, removing from the end, and slicing. The open-ended range `a[1..]` gives everything after the first element, and `first(2)` takes two elements from the front. `push` and `<<` append; `pop` both removes the last element from the array and returns it.

```ruby
# array_ops.rb — slicing, appending and removing
a = [5, 3, 8, 1]
puts "a[1..]=#{a[1..].inspect} a[-1]=#{a[-1]} first(2)=#{a.first(2).inspect}"
a.push(9)
a << 7
puts "after=#{a.inspect}"
puts "pop=#{a.pop} rest=#{a.inspect}"
```
```
a[1..]=[3, 8, 1] a[-1]=1 first(2)=[5, 3]
after=[5, 3, 8, 1, 9, 7]
pop=7 rest=[5, 3, 8, 1, 9]
```

The slice and `first(2)` each returned an array and did not change `a`: by the time execution reached the `push` line, `a` still held its original four elements. `push(9)` and `<<` did the same job and the array grew to six elements. `pop` returned `7` and the array shrank to five elements: `pop` has two effects, returning a value and changing the array. **CB19.** Whether a slice is a new array or a view of the same memory was not examined; only the printed values were measured.

#### The In-Place Form

Many array methods have two forms: one that returns a new array and one that changes the array in place. For sorting, this pair is `sort` and its in-place sorting form. The difference is measured with `equal?`: do two variables point to the same object?

```ruby
# in_place.rb — a new array versus changing the same array
a = [3, 1, 2]
b = a.sort
puts "new=#{b.inspect} a=#{a.inspect} same_object=#{a.equal?(b)}"
c = a.sort!
puts "in_place a=#{a.inspect} same_object=#{a.equal?(c)}"
puts "owner=#{a.method(:sort!).owner}"
```
```
new=[1, 2, 3] a=[3, 1, 2] same_object=false
in_place a=[1, 2, 3] same_object=true
owner=Array
```

`sort` gave a new array: `b` was sorted, `a` stayed in its original order, and `equal?` returned `false`. The in-place sorting form sorted `a` itself and returned the same object: `a` became `[1, 2, 3]` and `equal?` returned `true`. The owner of the in-place sorting form printed as `Array`, so its responder is a class; the difference between the two forms lies in whether the method's result is a new object or the receiver itself. **CB20.** The measurement was made only on the sorting pair; the same pattern is not assumed for other pairs.

#### Order in a Hash and Nested Access

A hash keeps insertion order, and `to_a` turns it into an array of pairs. In nested hashes, `dig` follows the keys in order; if the path breaks somewhere, it returns `nil` instead of raising an error.

```ruby
# hash_more.rb — insertion order, to_a and dig
h = { b: 1, a: 2 }
h[:c] = 3
puts h.keys.inspect
puts h.to_a.inspect
nested = { user: { address: { city: "Izmir" } } }
puts nested.dig(:user, :address, :city)
puts nested.dig(:user, :phone, :number).inspect
```
```
[:b, :a, :c]
[[:b, 1], [:a, 2], [:c, 3]]
Izmir
nil
```

`h` was first built with the keys `b` and `a`, and `c` was appended at the end; `keys` gave this order, and `to_a` gave the same order as pairs. There is no alphabetical ordering. `dig` followed three keys and found `Izmir`; in the second call `:phone` did not exist, and `dig` returned `nil` without raising an error. **CB21.** The order behavior was measured in Ruby 4.0.7 and only with this example.

#### The Shared Default Object

Giving the default value as an object instead of a block, in the form `Hash.new([])`, carries a trap: that object is a single one, and the same one comes back for every missing key. M12/K02 measured the behavior of a shared default value in another language; here the Ruby output is only read.

```ruby
# shared.rb — a shared default object versus the block form
shared = Hash.new([])
shared[:a] << 1
shared[:b] << 2
puts "shared=#{shared.inspect} default=#{shared.default.inspect} keys=#{shared.keys.inspect}"
separate = Hash.new { |h, k| h[k] = [] }
separate[:a] << 1
separate[:b] << 2
puts separate.inspect
```
```
shared={} default=[1, 2] keys=[]
{a: [1], b: [2]}
```

`shared[:a] << 1` received the default array for the missing key and appended to it; no key was assigned to the hash. That is why `shared` stayed empty, `keys` was `[]`, and the default array became `[1, 2]`: `:a` and `:b` wrote into the same array. The block form, on the other hand, ran the block for every missing key and wrote the result into the hash with `h[k] = []`; the two keys got two separate arrays. The value of a missing key is supplied by a single object in the first form and by the block each time in the second. **CB22.**

#### Transforming a Hash

Producing a new hash or a single value from a hash is a frequent need. `transform_values` transforms the values, `to_h` with a block can change the keys as well, and `sum` comes through `Enumerable`. On the array side, `compact` drops `nil` elements, `uniq` removes duplicates, and `flatten` flattens a nested array.

```ruby
# transform.rb — hash transformations and array cleanup
h = { a: 1, b: 2 }
puts h.transform_values { |v| v * 10 }.inspect
puts h.to_h { |key, v| [key.to_s, v] }.inspect
puts h.sum { |_, v| v }
puts h.min_by { |_, v| -v }.inspect
puts "owner=#{h.method(:transform_values).owner} #{h.method(:sum).owner} #{h.method(:to_h).owner}"
d = [3, 1, 2, 3, nil]
puts d.compact.uniq.sort.inspect
puts [[1, [2]], [3]].flatten.inspect
```
```
{a: 10, b: 20}
{"a" => 1, "b" => 2}
3
[:b, 2]
owner=Hash Enumerable Hash
[1, 2, 3]
[1, 2, 3]
```

`transform_values` multiplied only the values and the keys were kept; when the `to_h` block returned each pair as `[key, value]`, the keys were turned into strings. `sum` added the values and gave `3`, and `min_by` chose the pair with the smallest `-v`, that is, `[:b, 2]`. The owner line shows the distinction: `transform_values` and `to_h` are in `Hash`, while `sum` is in `Enumerable`. The `compact`, `uniq` and `sort` chain dropped the `nil` and the repeated `3` and gave `[1, 2, 3]`; `flatten` turned the nested array into `[1, 2, 3]`. **CB38.** These methods are not assumed to have the same owner on other receivers.

#### The Bounding Measurement

Does the block answer on every shortfall, or only in one specific case? The `fetch` method measures this directly. `fetch` looks for the key; if it finds it, it returns the value. If it does not, it runs the block when one is given and raises an error when none is. The measurement below counts with a counter how many times the block runs. This counter is not read as an answer to the question of how many times it ran: M10/K03 measured that question on another axis, and here the counter only shows that the block was never called.

```ruby
# fetch.rb — the block answers only when the key is absent
calls = 0
pairs = { a: 1 }

found = pairs.fetch(:a) { calls += 1; 0 }
missing = pairs.fetch(:b) { |key| calls += 1; key.to_s }
puts "found=#{found.inspect} missing=#{missing.inspect} calls=#{calls}"

without_block = begin
  pairs.fetch(:b)
rescue KeyError => error
  error.class
end
puts "without_block=#{without_block}"
puts "bracket=#{pairs[:b].inspect}"
puts "owner=#{pairs.method(:fetch).owner}"
```
```
found=1 missing="b" calls=1
without_block=KeyError
bracket=nil
owner=Hash
```

`fetch` gave three different results for the same missing key. The call with a block returned `"b"`, the call without a block raised `KeyError`, and square brackets returned `nil`. When the key existed, the counter-incrementing `fetch(:a)` with a block returned `1` and the counter went up only once: that increment belongs to the call whose key was absent. So the block was never called when the key was found. What the block answers is the missing key; the value of an existing key comes from the hash itself, without any block being asked.

This narrows the responder from the first section. The owner of `fetch` is `Hash`, a class, but the result is decided by the block when the key is missing. The core's classification measures the block case in a separate step for this reason: the owner of the message is always a class or a module, and the question is where the result comes from. Like `Hash.new { ... }`, `fetch` leaves the default in the caller's hands; the difference between the two is that the `Hash.new` block is bound to the hash permanently, while the `fetch` block belongs only to that call.

#### Summary

- An array is an ordered list numbered from zero; a hash is a mapping from keys to values that keeps insertion order. An index outside the array and a missing key return `nil`.
- In Ruby 4.0.7, `size`, `map` (on an array), `each`, `select` and `fetch` are defined in their own classes; `pairs.map` is in the `Enumerable` module.
- This lesson's responder is the **block**: `Hash.new { 0 }[:x]` gave `0` and `Hash.new { |_, k| k.to_s }[:x]` gave `"x"`; the class receiving the message was the same, and the difference was in the block supplied.
- `defined?` returns `"expression"` on a call with a block; the core queries the message without a block.
- The bounding measurement: the block answers only when the key is absent. The counter-incrementing `fetch(:a)` with a block returns `1` for an existing key, and the block never runs.

#### Next Step

In this lesson the block separated the results of two hashes, but the block itself has not yet been taken up as a topic: `{ |_, k| k.to_s }` and `{ 0 }` were only written. The Blocks lesson runs the same `map` message with two different blocks and measures how a method calls the block given to it.

### Blocks

Source: https://academia.sh/en/courses/ruby-fundamentals/blocks

The same map message is measured giving two different results with two different blocks, a method is shown calling its block with yield, and the block is shown deciding the result without changing the class's return rule.

The previous lesson showed that `Hash.new { 0 }[:x]` and `Hash.new { |_, k| k.to_s }[:x]` give two different results, and that the difference is decided not by the object receiving the message but by the block supplied. In that lesson the block itself was only written. This lesson makes it the subject: a **block** is a piece of code written with curly braces or `do ... end` and given to a method together with a message. A block is not a value by itself; it runs when the method calls it. M10/K03 measured how many times a closure runs in PHP; that question is not asked here, and the question is again who answers.

The course's responders are **class**, **module**, **block** and **syntax**. This lesson's responder is the **block**, but the bounding measurement will show that the block does not decide every result.

#### The Same Message, Two Blocks

The measurement below gives the same `map` message on the same array to two different blocks. The array does not change and the message does not change; only the block does. The core classifies the same result too: `reply.rb` counts the responder as a **block** when the two results differ.

```ruby
# reply.rb — helpers that classify who answers a message

# Classification asks in order: is the expression a message at all (defined?),
# then do two different blocks give two different results, then who owns the method.
def responder(definition, owner: nil, result: nil, with_blocks: nil)
  return ["syntax", "defined? #{definition}"] unless definition == "method"

  if with_blocks
    first, second = with_blocks.call
    return ["block", "#{first.inspect} / #{second.inspect}"] unless first == second
  end

  kind = owner.instance_of?(Class) ? "class" : "module"
  [kind, "#{owner}, result #{result.inspect}"]
end
```

```ruby
# block.rb — the same map message with two blocks
require_relative "reply"

numbers = [1, 2, 3]
doubled = numbers.map { |n| n * 2 }
text = numbers.map { |n| n.to_s }

kind, evidence = responder(defined?(numbers.map), with_blocks: -> { [doubled, text] })
puts "#{kind}  #{evidence}"
puts numbers.inspect
```
```
block  [2, 4, 6] / ["1", "2", "3"]
[1, 2, 3]
```

The first line is the core's `element mapping` observation: the **responder** is a **block**. The second line shows that the array did not change; `map` built a new array. **CB3.** The measurement is on an array; on a hash, `map` comes from `Enumerable` and works with the same block logic, but this lesson does not measure it separately.

#### How a Method Calls the Block

A method definition can ask whether a block was given when it was called. `block_given?` says this directly; inside the method body, `defined?(yield)` gives the same information: a string on a call with a block, `nil` on a call without one. `yield` calls the given block and passes its arguments to the block's parameters. `yield` is not written at the top level, outside a method; every measurement is inside a method.

```ruby
# yield.rb — a method calls its block with yield
def twice
  puts "with_block=#{block_given?} yield=#{defined?(yield).inspect}"
  return unless block_given?

  yield 1
  yield 2
end

twice
twice { |n| puts "block #{n}" }
```
```
with_block=false yield=nil
with_block=true yield="yield"
block 1
block 2
```

On the call without a block, `block_given?` returned `false`, `defined?(yield)` returned `nil`, and the method exited early. On the call with a block, they were `true` and `"yield"`; the block ran twice, with `1` and `2`. **CB4.** `defined?(yield)` returns `"yield"`, not `"method"`; `yield` is not a method call but a construct of the language itself.

`map` and `each` called without a block raise no error; they return an **iterator (`Enumerator`)**. On a call with a block, `defined?` gives `"expression"`; the core queries the message without a block for this reason, as seen before.

```ruby
# without_block.rb — a call without a block returns an iterator
puts [1, 2, 3].map.class
puts [1, 2, 3].each.class
puts defined?([1, 2, 3].map)
puts defined?([1, 2, 3].map { |n| n })
```
```
Enumerator
Enumerator
method
expression
```

#### The Block's Value and Its Surroundings

`yield` is an expression and returns the value of the block's last expression. The method can use this value; `map` does exactly that. A block also sees the local variables of the place where it was defined: this is the **closure** behavior explained in M01/K02, and it is not redefined here, only measured.

```ruby
# value.rb — yield returns the block's value, the block sees its surroundings
def triple
  result = yield 3
  "yield returned: #{result}"
end

factor = 5
puts triple { |n| n * factor }
factor = 7
puts triple { |n| n * factor }

total = 0
[1, 2, 3].each { |n| total += n }
puts "total=#{total}"
```
```
yield returned: 15
yield returned: 21
total=6
```

The block was written the same way in both calls, but it saw the current value of the `factor` variable: first `15`, then `21`. The block did not copy the value when it was defined; it read the variable's current value at the moment it was called. Inside the `each` block, `total` increased the outer variable. **CB6.** This lesson only measures that the block sees the variables of the scope where it was defined; it does not measure other scope rules.

#### Two Forms, Many Parameters

Curly braces are preferred for one-line blocks and `do ... end` for multi-line blocks; both build the same block. A block can take several parameters, and the parameters are bound in order to however many values `yield` gives.

```ruby
# forms.rb — two forms build the same block, several parameters
def pair
  yield 1, 2
end

short = pair { |a, b| a + b }
long = pair do |a, b|
  a + b
end
puts "short=#{short} long=#{long}"

{ name: "Alice", age: 30 }.each do |key, value|
  puts "#{key}=#{value}"
end
```
```
short=3 long=3
name=Alice
age=30
```

Both forms gave `3`. The hash's `each` block received the key and the value as two separate parameters. **CB7.** The two forms were measured to give the same result only in these calls; which method a block binds to in a call without parentheses is measured separately in the Which Method It Binds To section.

#### Shorthand Forms

Since Ruby 3.4, `it` can be written in a block with a single parameter; `_1` has existed since Ruby 2.7. Both give the same result.

```ruby
# shorthand.rb — it and _1 in a single-parameter block
puts [1, 2].map { it * 3 }.inspect
puts [1, 2].map { _1 * 3 }.inspect
```
```
[3, 6]
[3, 6]
```

#### Unpacking Parameters and Order

If a block takes several parameters, a single array passed by the method is distributed across the parameters. `|a, b|` unpacks a pair, and `|a, (b, c)|` unpacks a nested array too. `each_with_index` passes the position along with the element.

```ruby
# unpacking.rb — array unpacking in block parameters
[[1, 2], [3, 4]].each { |a, b| puts "#{a}+#{b}=#{a + b}" }
%w[x y].each_with_index { |letter, index| puts "#{index}:#{letter}" }
[[1, [2, 3]]].each { |a, (b, c)| puts "a=#{a} b=#{b} c=#{c}" }
puts [[1, 2], [3, 4]].map { |pair| pair }.inspect
```
```
1+2=3
3+4=7
0:x
1:y
a=1 b=2 c=3
[[1, 2], [3, 4]]
```

On `[[1, 2], [3, 4]]`, `|a, b|` split each pair into two parameters: `1+2=3` and `3+4=7`. `each_with_index` gave the element and the position as two separate parameters. The parenthesized `(b, c)` unpacked the inner array. The single-parameter `map { |pair| pair }`, on the other hand, took the array whole, without splitting it. **CB23.** Unpacking was measured only on array elements and in these four calls.

#### The Block's Outer Local

A block sees the local variables of the place where it was defined and can change them. This is the closure behavior measured above. What happens when a block parameter has the same name as an outer variable is also measured below; the `|x; y|` form makes the name after the semicolon local to the block.

```ruby
# outer_local.rb — a block sees and changes an outer variable
total = 0
[1, 2, 3].each { |n| total += n }
puts total
n = 100
[1, 2].each { |n| n }
puts n
y = 1
[5].each { |x; y| y = x }
puts y
```
```
6
100
1
```

`total` was defined outside the block and the block increased it on every turn: the result is `6`. In the second example, the block parameter `n` had the same name as the outer `n`; the `n` inside the block became a separate variable and the outer `n` stayed `100`. In the third, `|x; y|` declared a `y` local to the block; even though the block wrote `y = x`, the outer `y` stayed `1`. A block can change an outer local; this happens only when the name is not declared local to the block. **CB24.**

#### Which Method It Binds To

The two block forms gave the same result at the start of this lesson. The difference appears in a call without parentheses: curly braces bind to the nearest method, while `do` and `end` bound to the outer method in the measurement below. The `ask` method below only reports whether a block was given to it.

```ruby
# binding.rb — which method the block binds to
def ask(*) = "block=#{block_given?}"
puts ask(1) { }
puts ask 1 do end
```
```
block=true
block=false
```

`ask(1) { }` gave the block to `ask` and `block=true` was printed. `puts ask 1 do end` bound the block to `puts`; `ask` received no block and `block=false` was printed. In a call without parentheses, `do` and `end` carry the block to the outer method. **CB25.** Only these two calls were measured; binding in chained calls was not measured separately.

#### Returning a Value from a Block

Inside a block, `next` ends the block for that turn and makes the given value the block's value. `break` ends the method itself, and the given value becomes the result of the method call.

```ruby
# next_break.rb — values with next and break
puts [1, 2, 3, 4].map { |n| next 0 if n.even?; n * 10 }.inspect
puts [1, 2, 3].each { |n| break n * 5 if n == 2 }
```
```
[10, 0, 30, 0]
10
```

`next 0 if n.even?` made the block's value `0` on even numbers, and `map` returned `[10, 0, 30, 0]`. Inside `each`, `break n * 5` cut the loop when `n == 2`, and the value of the `each` call became `10`. **CB26.** `next` and `break` were measured only with `map` and `each`.

#### A Flexible Number of Parameters

A block does not require the number of parameters to equal the number of values the method passes. Extra values are dropped, and a missing parameter becomes `nil`. When a single array is passed, several parameters unpack it.

```ruby
# flexible.rb — block parameter count versus passed value count
def two
  yield 1, 2
end
two { |a| puts "a=#{a.inspect}" }
two { |a, b, c| puts "c=#{c.inspect}" }
def one
  yield [1, 2]
end
one { |a, b| puts "a=#{a} b=#{b}" }
one { |a| puts "a=#{a.inspect}" }
```
```
a=1
c=nil
a=1 b=2
a=[1, 2]
```

`yield 1, 2` passed two values. The single-parameter block took only the first, the third parameter of the three-parameter block stayed `nil`, and no error occurred. `yield [1, 2]` passed a single array: the two-parameter block unpacked it, and the single-parameter block took the array whole. This looseness is seen in a block and in a proc; the Procs and Lambdas lesson runs the same test with a lambda. **CB39.**

#### Using the Block's Value in a Method

`yield` is an expression and its value is the block's last expression. The method can accumulate this value or use it as a condition; in that case, what is done is decided not by the method but by the given block.

```ruby
# yield_value.rb — the value of a yield expression
def transform(list)
  result = []
  list.each { |x| result << yield(x) }
  result
end
puts transform([1, 2, 3]) { |x| x * 2 }.inspect
def count_matching(list) = list.count { |x| yield x }
puts count_matching([1, 2, 3, 4]) { |x| x.even? }
```
```
[2, 4, 6]
2
```

`transform` appended each `yield(x)` value to the result and gave `[2, 4, 6]`. `count_matching` counted the elements for which the block returned true and gave `2`. In both methods the block decided the result; `transform` does not know about doubling and `count_matching` does not know about picking even numbers; both just use the block's value. The responder is therefore the block. **CB40.**

#### Block Parameter and Symbol

If a method defines an `&block` parameter, it receives the given block as an object; if no block was given, this parameter is `nil`. The `&:name` form passes a symbol in place of a block.

```ruby
# block_object.rb — the &block parameter and a symbol
def take(&b) = b
puts take.inspect
puts take { }.class
puts %w[a b].map(&:upcase).inspect
puts :upcase.to_proc.call("x")
```
```
nil
Proc
["A", "B"]
X
```

`take` returned `nil` when called without a block and a `Proc` when called with one. `map(&:upcase)` sent the `upcase` message to each element and gave `["A", "B"]`; `:upcase.to_proc.call("x")` returned `X`. A symbol itself is not a block; it turns into a `Proc` with `to_proc`. **CB41.** Proc objects are detailed in the Procs and Lambdas lesson.

#### The Bounding Measurement

Does the block always decide the result? The `each` message runs the block for every element, but its return value is not the block's result. Two different blocks give the same return value, and the responder is not the block but the class that owns `each`.

```ruby
# return_value.rb — a block's result does not change the return of each
numbers = [1, 2]
a = numbers.each { |n| n * 10 }
b = numbers.each { |n| n * 99 }
puts a.inspect
puts b.inspect
puts "owner=#{numbers.method(:each).owner}"
```
```
[1, 2]
[1, 2]
owner=Array
```

The two blocks computed different values, but both returned `[1, 2]` from `each`. The return value is decided not by the blocks but by the definition of `each` in the `Array` class. So the block decides the result for `map` and does not for `each`; the same block syntax does two different jobs on two messages. **CB5.** This difference was measured only on these two methods and is not generalized to other iteration methods.

#### Summary

- A block is a piece of code given to a method together with a message; the method calls it with `yield`.
- This lesson's responder is the **block**: the same `map` message gave `[2, 4, 6]` with `{ |n| n * 2 }` and `["1", "2", "3"]` with `{ |n| n.to_s }`.
- `block_given?` and `defined?(yield)` query whether a block is present; `map` and `each` without a block return an iterator.
- `it` is available in a single-parameter block since Ruby 3.4, and `_1` since Ruby 2.7.
- The bounding measurement: `each` returns `[1, 2]` with both of two different blocks; in this case the `Array` class decides the return value.

#### Next Step

`map` was `Array`'s method on an array and `Enumerable`'s method on a hash. The next lesson looks at that module itself: where does a method that the array does not have itself come from, and how do you give your own class the same ability with a single `each`.

### The `Enumerable` Module

Source: https://academia.sh/en/courses/ruby-fundamentals/the-enumerable-module

The iteration methods an array does not define itself are measured as coming from the Enumerable module, your own class is given the same ability with a single each, and the module is shown not to take over every job.

The previous lesson showed that `map` on a hash is defined not in `Hash` but in the `Enumerable` module. This lesson looks at that module. A **module** is a collection of methods that is **mixed into** (`include`) a class; it does not produce objects by itself. M08 built the object-oriented foundations; here only the messages this module answers are measured.

This lesson's responder is the **module**. Arrays, ranges and hashes are different classes, but a shared module answers many of their iteration messages.

#### The Messages the Module Answers

The first measurement sends the `each_slice` message to a range. The core classifies the result by looking at the method's owner: if the owner is a `Class`, the responder is a class; otherwise it is a module.

```ruby
# reply.rb — helpers that classify who answers a message

# Classification asks in order: is the expression a message at all (defined?),
# then do two different blocks give two different results, then who owns the method.
def responder(definition, owner: nil, result: nil, with_blocks: nil)
  return ["syntax", "defined? #{definition}"] unless definition == "method"

  if with_blocks
    first, second = with_blocks.call
    return ["block", "#{first.inspect} / #{second.inspect}"] unless first == second
  end

  kind = owner.instance_of?(Class) ? "class" : "module"
  [kind, "#{owner}, result #{result.inspect}"]
end
```

```ruby
# slice.rb — who answers the each_slice message
require_relative "reply"

range = (1..5)
owner = range.method(:each_slice).owner
result = range.each_slice(2).to_a
kind, evidence = responder(defined?(range.each_slice(2)), owner: owner, result: result)
puts "#{kind}  #{evidence}"
```
```
module  Enumerable, result [[1, 2], [3, 4], [5]]
```

This is the core's `slicing` observation: the **responder** is a **module**, and the owner is `Enumerable`. The `Range` class does not define `each_slice` itself; it is enough for a range to know how to hand out its elements with `each`. The module's other methods are in this lesson's second measurement.

```ruby
# tools.rb — Enumerable methods
fruits = %w[plum peach grape fig]
puts fruits.sort_by { |f| f.size }.first
puts fruits.group_by { |f| f.size }.inspect
puts fruits.min_by { |f| f.size }
puts %w[a b a].tally.inspect
puts (1..6).select(&:even?).inspect
```
```
fig
{4 => ["plum"], 5 => ["peach", "grape"], 3 => ["fig"]}
fig
{"a" => 2, "b" => 1}
[2, 4, 6]
```

`sort_by` put the shortest name first, `group_by` built a hash that groups the names by length, and `min_by` gave the shortest name. `tally` counted how many times each element appears. `select(&:even?)` picked the even numbers from the range.

#### Mixing It into Your Own Class

The module's ability becomes a class's ability only once it is mixed into the class. The smallest form: the class defines only `each` and says `include Enumerable`. Class design is the subject of M08; here the class is built in its smallest form just for the measurement.

```ruby
# numbers.rb — mixing Enumerable into our own class
class Numbers
  include Enumerable

  def each
    yield 3
    yield 1
    yield 2
  end
end

numbers = Numbers.new
puts numbers.map { |n| n * 2 }.inspect
puts numbers.sort.inspect
puts numbers.min_by { |n| -n }
puts "map=#{numbers.method(:map).owner} each=#{numbers.method(:each).owner}"
puts numbers.to_a.inspect
puts numbers.include?(2)
puts numbers.first
puts numbers.reduce(:+)
puts numbers.each_with_index.to_a.inspect
puts numbers.is_a?(Enumerable)
```
```
[6, 2, 4]
[1, 2, 3]
3
map=Enumerable each=Numbers
[3, 1, 2]
true
3
6
[[3, 0], [1, 1], [2, 2]]
true
```

The class wrote only `each`; `map`, `sort` and `min_by` arrived without any extra definition. `min_by { -n }` gave the largest number, because the one with the smallest negative value is `3`. The fourth line shows the sharing: the owner of `map` is `Enumerable`, and the owner of `each` is the `Numbers` class. `to_a` collected the elements in the order `each` yielded them: `[3, 1, 2]`. `include?`, `first` and `reduce` also rested on the same single `each`; `reduce(:+)` summed the elements and gave `6`. `each_with_index` attached an index to each element. The last line confirms that instances of the class are recognized as `Enumerable`. The class did not define `<=>`, because `sort` here compares numbers with the numbers' own comparison; comparing the class's instances with each other is a separate job. **CB8.** This class yields three values with `each`; if `each` produced an endless sequence, methods that read everything, such as `sort`, would never finish, but that behavior is not measured here.

#### The Types the Module Returns

The module's methods return particular types regardless of the receiver's type. This is a contract the module gives its users: when you send the same message to a range, an array or your own class, you know the result type without looking at the receiver.

```ruby
# types.rb — types returned by Enumerable methods
range = (1..3)
puts range.map { |n| n }.class
puts range.select { |n| n > 1 }.class
puts range.group_by { |n| n.odd? }.class
puts range.tally.class
puts range.each_slice(2).class
puts range.min_by { |n| -n }.class
```
```
Array
Array
Hash
Hash
Enumerator
Integer
```

`map` and `select` returned arrays, `group_by` and `tally` returned hashes; `each_slice` without a block gave an iterator, and `min_by` gave a single element. The range was the receiver here, but the result types depend on the module's definition, not on the range. The `Hash#select` example in the Arrays and Hashes lesson shows the limit of this: there the class's own definition had returned a hash. **CB9.** This table was measured only with a range receiver; that the same methods return the same types on your own class has been seen for `map` in the `Numbers` measurement above, and is not assumed for the others.

#### Chained Methods

The real strength of `Enumerable` is that small methods connect to each other. The measurement below runs five methods on a range and prints the owner of each next to it.

```ruby
# chain.rb — the result and owner of each method
s = (1..4)
puts "each_with_object=#{s.each_with_object([]) { |n, a| a << n * n }.inspect} owner=#{s.method(:each_with_object).owner}"
puts "inject=#{s.inject(:+)} reduce=#{s.reduce(1) { |a, n| a * n }} owner=#{s.method(:inject).owner}"
puts "zip=#{s.zip(%w[a b c d]).inspect} owner=#{s.method(:zip).owner}"
puts "partition=#{s.partition(&:even?).inspect} owner=#{s.method(:partition).owner}"
puts "each_cons=#{s.each_cons(2).to_a.inspect} owner=#{s.method(:each_cons).owner}"
```
```
each_with_object=[1, 4, 9, 16] owner=Enumerable
inject=10 reduce=24 owner=Enumerable
zip=[[1, "a"], [2, "b"], [3, "c"], [4, "d"]] owner=Enumerable
partition=[[2, 4], [1, 3]] owner=Enumerable
each_cons=[[1, 2], [2, 3], [3, 4]] owner=Enumerable
```

`each_with_object` carried an empty array along and collected the squares. `inject(:+)` gave the sum and `reduce(1) { ... }` gave the product; the owner of `inject` was printed as `Enumerable`. `zip` paired two sequences, `partition` split the elements into two arrays by whether the block was true, and `each_cons(2)` gave consecutive pairs. In all five, the printed owner was `Enumerable`: even though `Range` defines its own `each`, the responder for this chain is the module. **CB27.** The owner measurement was made only with a `Range` receiver; the owner of `reduce` was not printed separately.

#### The Lazy Chain

A `lazy` chain does not run immediately; it produces only as many elements as the result asks for. That is why `select` and `first(3)` can finish on an endless range. `with_index` attaches an index to the elements, and the first index can be given.

```ruby
# lazy.rb — a lazy chain and with_index
puts (1..Float::INFINITY).lazy.select(&:even?).first(3).inspect
puts (1..3).lazy.class
puts "lazy owner=#{(1..3).method(:lazy).owner}"
puts %w[a b].each.with_index(1).to_a.inspect
puts %w[a b].map.with_index { |h, i| "#{i}#{h}" }.inspect
```
```
[2, 4, 6]
Enumerator::Lazy
lazy owner=Enumerable
[["a", 1], ["b", 2]]
["0a", "1b"]
```

On the endless range, `first(3)` stopped after finding the first three even numbers and gave `[2, 4, 6]`. `Enumerable` answered the `lazy` message, and the class of the returned object was `Enumerator::Lazy`. `each.with_index(1)` started the index at one; `map.with_index` handed the index to the block along with the element and produced the transformed array. **CB28.** Only these three chains were measured.

#### An External Iterator

`each` called without a block returns an iterator; `next` advances it by hand. When the elements run out, `StopIteration` is raised.

```ruby
# external_iterator.rb — advancing by hand with next
e = [1, 2, 3].each
puts e.next
puts e.next
puts e.next
result = begin
  e.next
rescue StopIteration
  "done"
end
puts result
puts e.size
```
```
1
2
3
done
3
```

Three `next` calls gave the elements in order. The fourth call raised `StopIteration`, `rescue` caught it and `done` was printed. `size` returned `3`. **CB29.** `size` was measured only in this example; iterators whose size is not known in advance were not measured.

#### Methods Coming from the Mixin

When your own class defines only `each` and mixes in `Enumerable`, it gains dozens of methods. The `Box` class below keeps its elements in an array and delegates to the array's `each` inside its own `each`. Then nine different methods are called.

```ruby
# box.rb — a class that defines only each
class Box
  include Enumerable

  def initialize(*items)
    @items = items
  end

  def each(&block)
    @items.each(&block)
    self
  end
end

k = Box.new(3, 1, 2)
puts "sort=#{k.sort.inspect} owner=#{k.method(:sort).owner}"
puts "min=#{k.min} max=#{k.max} sum=#{k.sum}"
puts "map=#{k.map { |n| n * 2 }.inspect}"
puts "each_slice=#{k.each_slice(2).to_a.inspect}"
puts "first=#{k.first} count=#{k.count} sort_by=#{k.sort_by { |n| -n }.inspect}"
puts "each owner=#{k.method(:each).owner}"
```
```
sort=[1, 2, 3] owner=Enumerable
min=1 max=3 sum=6
map=[6, 2, 4]
each_slice=[[3, 1], [2]]
first=3 count=3 sort_by=[3, 2, 1]
each owner=Box
```

`Box` defined only `each`; the owner of `sort` was printed as `Enumerable`. `min`, `max`, `sum`, `map`, `each_slice`, `first`, `count` and `sort_by` all worked through `each` and gave the expected results. The responder is the module; the owner of `each` is `Box`. The module learns how to reach the elements from the class. **CB42.** Only these nine methods were tried; it is not assumed that every method of the mixin works.

#### Without an each

The module does not provide `each` itself; the class must define it. A class that does not define `each` accepts the mixin, but the methods do not work.

```ruby
# empty.rb — a class that does not define each
class Empty
  include Enumerable
end

result = begin
  Empty.new.map { |x| x }
rescue NoMethodError => error
  error.class
end
puts result
puts Empty.new.respond_to?(:each)
```
```
NoMethodError
false
```

The `Empty` class mixed in `Enumerable` but did not define `each`. The `map` call raised `NoMethodError` and `respond_to?(:each)` gave `false`. The contract runs one way: `Enumerable` methods assume that the class's `each` method can be called. **CB43.** The error message was not printed; only its class was measured.

#### Query Methods and Their Owners

`any?`, `all?`, `none?`, `find` and `take_while` query the collection. On an array, do all of these methods come from `Enumerable`? The owner line measures this.

```ruby
# queries.rb — query methods and their owners
s = [1, 2, 3, 4]
puts "any=#{s.any? { |n| n > 3 }} all=#{s.all?(Integer)} none=#{s.none?(&:zero?)}"
puts "find=#{s.find { |n| n > 2 }} take_while=#{s.take_while { |n| n < 3 }.inspect}"
puts "owner=#{s.method(:take_while).owner} #{s.method(:find).owner} #{s.method(:any?).owner}"
```
```
any=true all=true none=true
find=3 take_while=[1, 2]
owner=Array Array Array
```

The query results are as expected: `any?`, `all?(Integer)` and `none?(&:zero?)` were true, `find` gave `3` and `take_while` gave `[1, 2]`. The owner of all three methods was printed as `Array`: the array defines these methods itself instead of leaving them to `Enumerable`. In the previous section the owner of `sort` for `Box` was `Enumerable`; so the responder depends on the receiver's class. **CB44.** The measurement was made on Ruby 4.0.7 and only with an `Array` receiver.

#### Other Methods from the Mixin

The methods that come with the mixin are not limited to summing and sorting. `minmax`, which gives the smallest and the largest together, `min(2)`, which gives the first `n` smallest, `each_with_index`, which passes the index along, lazy chains and reverse traversal all rest on `each` too. The `Shelf` class below again defines only `each`.

```ruby
# shelf.rb — a second class that defines each, and more methods
class Shelf
  include Enumerable

  def initialize(*items)
    @items = items
  end

  def each(&block) = @items.each(&block)
end

r = Shelf.new(4, 2, 9)
puts r.minmax.inspect
puts r.min(2).inspect
puts r.each_with_index.map { |n, i| n * i }.inspect
puts r.lazy.map { |n| n + 1 }.first(2).inspect
puts r.entries.inspect
puts r.reverse_each.to_a.inspect
```
```
[2, 9]
[2, 4]
[0, 2, 18]
[5, 3]
[4, 2, 9]
[9, 2, 4]
```

`minmax` gave `[2, 9]` and `min(2)` gave `[2, 4]`. `each_with_index.map` multiplied each element by its index: `[0, 2, 18]`. `lazy.map { ... }.first(2)` transformed only the first two elements: `[5, 3]`. `entries` gave the elements in insertion order and `reverse_each` in reverse. In none of the six calls did `Shelf` define a new method; all derived from `each`. **CB52.** The owners of these methods were not printed; only their results were measured.

#### Comparable for Sorting

`sort` compares elements with `<=>`. Objects that do not know this comparison cannot be sorted. A class that defines `<=>` and mixes in `Comparable` gains `sort`, `min` and operators such as `<`.

```ruby
# compare.rb — comparable and sort
class Thing; end
result = begin
  [Thing.new, Thing.new].sort
rescue ArgumentError => error
  error.class
end
puts result

class Label
  include Comparable
  attr_reader :length

  def initialize(length)
    @length = length
  end

  def <=>(other) = length <=> other.length
end

puts [Label.new(3), Label.new(1)].sort.map(&:length).inspect
puts [Label.new(3), Label.new(1)].min.length
puts Label.new(1) < Label.new(2)
puts "owner=#{Label.new(1).method(:<).owner}"
```
```
ArgumentError
[1, 3]
1
true
owner=Comparable
```

Two `Thing` objects without a `<=>` definition could not be sorted: `sort` raised `ArgumentError`. `Label` defined only `<=>` and mixed in `Comparable`; `sort` gave `[1, 3]` and `min` gave `1`. The `<` operator worked too, and the owner of `<` was printed as `Comparable`. The module answers the operator; the class only supplied the comparison rule. **CB53.** The measurement was made with a single field.

#### The Bounding Measurement

The module does not take over every job. A method of the same name can come from a different owner on different receivers: if the class wrote its own definition, that definition answers.

```ruby
# owners.rb — the same name, different owners
puts "(1..3).sum   #{(1..3).method(:sum).owner}"
puts "[3, 1].sum   #{[3, 1].method(:sum).owner}"
puts "[1].sort_by  #{[1].method(:sort_by).owner}"
puts "[3, 1].sort  #{[3, 1].method(:sort).owner}"
puts "[3, 1].min   #{[3, 1].method(:min).owner}"
puts "(1..3).min   #{(1..3).method(:min).owner}"
```
```
(1..3).sum   Enumerable
[3, 1].sum   Array
[1].sort_by  Enumerable
[3, 1].sort  Array
[3, 1].min   Array
(1..3).min   Range
```

`sum` comes from `Enumerable` on a range and from `Array` on an array. `sort_by` belongs to `Enumerable` on an array too, but `sort` and `min` are `Array`'s own methods on an array. For a range, `min` is `Range`'s own method. The results belong to Ruby 4.0.7. The six lines gave three different owners: `Enumerable`, `Array` and `Range`. The names `sum` and `min` work on both receivers and the caller sees no difference, but a class or a module answers the message depending on the receiver. So the `owner` question starts from the receiver's type, not from the method's name. **CB10.** None of the six lines compares the value of the result; only the owner is asked, so this measurement does not say whether the module and the class give the same result. In other words, the same name is the module's answer or the class's answer depending on the receiver; the reason for this is the subject of a later course and is not explained here.

#### Summary

- `Enumerable` is a module that gives every class defining `each` methods such as `map`, `sort`, `min_by`, `group_by` and `tally`.
- This lesson's responder is the **module**: the `(1..5).each_slice(2)` message is answered by `Enumerable`, not by `Range`.
- Adding `include Enumerable` and a single `each` to your own class is enough; the owner of `map` turns out to be the module and the owner of `each` the class.
- The bounding measurement: the same name comes from a different owner depending on the receiver; `sum` comes from the module on a range and from `Array` on an array.

#### Next Step

In this lesson a method's body called the block with `yield`. The next lesson turns the block into an object: `Proc` and lambda are two ways of keeping the same code in a variable, and the owner of the `call` message is the same for both.

### Procs and Lambdas

Source: https://academia.sh/en/courses/ruby-fundamentals/procs-and-lambdas

For procs and lambdas, the two ways of turning a block into an object, a class is measured answering the call message, and the difference is shown to lie in argument checking and in the lambda? state.

In the previous two lessons a block was only a piece of code accompanying a method call: it stayed where it was written and could not be stored directly in a variable. A **proc** turns the block into an object; a **lambda** is a stricter form of the same object. Both have the class `Proc`. A proc carries the variables of the scope where it was defined; M01/K02 called this property a **closure**, and it is not redefined here.

This lesson's responder is the **class**: the `Proc` class answers the `call` message. The difference lies not in the class but in the object's **`lambda?` state**.

#### Three Ways to Call

A lambda is written with `->`. To call it you use `call`, the dot-parenthesis shorthand `.()`, or square brackets; the three are the same message. The core classifies this call: if the owner of `call` is a `Class`, the responder is a **class**.

```ruby
# reply.rb — helpers that classify who answers a message

# Classification asks in order: is the expression a message at all (defined?),
# then do two different blocks give two different results, then who owns the method.
def responder(definition, owner: nil, result: nil, with_blocks: nil)
  return ["syntax", "defined? #{definition}"] unless definition == "method"

  if with_blocks
    first, second = with_blocks.call
    return ["block", "#{first.inspect} / #{second.inspect}"] unless first == second
  end

  kind = owner.instance_of?(Class) ? "class" : "module"
  [kind, "#{owner}, result #{result.inspect}"]
end
```

```ruby
# lambda.rb — who answers the lambda call
require_relative "reply"

double = ->(n) { n * 2 }
owner = double.method(:call).owner
kind, evidence = responder(defined?(double.call(3)), owner: owner, result: double.call(3))
puts "#{kind}  #{evidence}"
puts double.class
puts double.call(3)
puts double.(3)
puts double[3]
```
```
class  Proc, result 6
Proc
6
6
6
```

The first line is the core's `lambda call` observation: the **responder** is a **class**, and the owner is `Proc`. All three call forms gave `6`. **CB11.** The three forms were measured with the same result in this lesson; which one is preferred is a matter of writing style and is not measured.

#### The Difference Between a Proc and a Lambda

The difference is not visible at first glance: both are called and both return a value. The distinction appears in argument checking. A lambda expects exactly the right number of arguments; a proc ignores the extra ones and fills in the missing ones with `nil`. In the error case, the class of the exception the lambda raises is printed, not its message.

```ruby
# difference.rb — the argument difference between a proc and a lambda
loose = proc { |x| x }
strict = lambda { |x| x }
puts loose.call(1, 2)

result = begin
  strict.call(1, 2)
rescue ArgumentError => error
  error.class
end
puts result

pair = proc { |x, y| [x, y] }
puts pair.call(1).inspect
puts "loose=#{loose.lambda?} strict=#{strict.lambda?}"
```
```
1
ArgumentError
[1, nil]
loose=false strict=true
```

When given two arguments at once, the proc dropped the extra one and returned `1`; the lambda raised `ArgumentError` on the same call. When the two-parameter proc was given a single argument, the missing parameter became `nil`. `lambda?` tells the two apart: `false` for the proc, `true` for the lambda.

#### Procs from Other Objects

In Ruby, a proc can be obtained from other sources too: `to_proc` from a method object and `to_proc` from a symbol. Both behave like a lambda.

```ruby
# conversion.rb — a method and a symbol turn into a proc
def square(n)
  n * n
end

puts method(:square).to_proc.lambda?
puts :upcase.to_proc.lambda?
puts :upcase.to_proc.call("a")
```
```
true
true
A
```

The procs obtained from a method and from a symbol have a `lambda?` value of `true`. `:upcase.to_proc.call("a")` gave the uppercase string. This is the mechanism behind the `select(&:even?)` form seen in the previous lesson: `&` turns the symbol into a proc and passes it in place of the block.

#### Carrying a Variable

A proc stays bound to the variables of the scope where it was defined. To see this, a method returns a lambda, and the lambda keeps incrementing the variable even after the method has finished.

```ruby
# counter.rb — a lambda carries the variable from where it was defined
def make_counter
  total = 0
  -> { total += 1 }
end

first = make_counter
second = make_counter
puts first.call
puts first.call
puts second.call
puts first.call
```
```
1
2
1
3
```

Each `make_counter` call set up its own `total` variable. `first` gave `1`, `2` and `3` in turn; `second` gave `1` from its own counter. The method had finished, but the lambda kept carrying the variable. **CB13.** This measurement shows two independent counters; two lambdas sharing the same variable are not measured in this lesson.

#### Passing an Object in Place of a Block

A proc or a lambda can be passed to a method as a block by putting `&` in front of it. This joins `map` from the Blocks lesson with this lesson's objects. `arity`, for its part, gives as a number how many parameters the object expects.

```ruby
# as_block.rb — passing an object as a block, and arity
double = ->(n) { n * 2 }
loose = proc { |x, y| [x, y] }
puts [1, 2, 3].map(&double).inspect
puts "arity lambda=#{double.arity} proc=#{loose.arity}"
puts [[1, 2], [3, 4]].map(&loose).inspect
```
```
[2, 4, 6]
arity lambda=1 proc=2
[[1, 2], [3, 4]]
```

`map(&double)` handed each element to the lambda and collected `[2, 4, 6]`: there was no need to write a separate block. `arity` returned `1` for the lambda and `2` for the two-parameter proc. On the nested arrays, the proc split each inner array into two parameters and rebuilt the `[x, y]` array, so the result looked the same as the input. **CB14.** In this measurement the lambda was always called with a single element; what happens when a nested array is passed to a lambda with `&` was not measured.

#### Where return Returns To

Another difference between a proc and a lambda is where `return` goes. The two methods below do the same job: they build an object inside, call it, and then try to return `2`.

```ruby
# return_difference.rb — return inside a proc and a lambda
def with_proc
  pr = proc { return 1 }
  pr.call
  2
end

def with_lambda
  l = lambda { return 1 }
  l.call
  2
end

puts with_proc
puts with_lambda
```
```
1
2
```

`with_proc` returned `1`: the `return` inside the proc ended not the proc but the method enclosing it, and the line with `2` was never reached. `with_lambda` returned `2`: the `return` inside the lambda ended only the lambda, and the method carried on from where it was. **CB30.** The measurement was made only inside a method; a `return` inside a proc at the top level was not tried in this lesson.

#### Partial Application

`curry` turns a multi-parameter lambda into a chain that takes one parameter per call. When some of the parameters are given, a new object that waits for the rest is returned.

```ruby
# curry.rb — calling parameter by parameter with curry
add = ->(a, b, c) { a + b + c }
puts add.curry[1][2][3]
puts add.curry.(1, 2).(3)
partial = add.curry[1]
puts partial.class
puts partial.lambda?
```
```
6
6
Proc
true
```

With three square-bracket calls and with the `(1, 2).(3)` form, `curry` gave the same result, `6`. `add.curry[1]` returned a `Proc`, and this object was still a lambda. **CB31.** `curry` was measured only with a lambda of fixed parameters.

#### The Method Object

`method(:name)` turns a method into an object. This object can be called, its owner can be asked, and it can be turned into a block with `&`.

```ruby
# method_object.rb — a Method object and to_proc with method
def square(n) = n * n
m = method(:square)
puts m.class
puts m.call(4)
puts "owner=#{m.owner} arity=#{m.arity}"
puts [1, 2, 3].map(&m).inspect
puts m.to_proc.class
```
```
Method
16
owner=Object arity=1
[1, 4, 9]
Proc
```

`method(:square)` gave a `Method` object and `call(4)` returned `16`. The owner of `square`, defined at the top level, was printed as `Object`. `map(&m)` passed the method in place of a block and produced `[1, 4, 9]`; `to_proc` returned a `Proc`. **CB32.** The measurement was made only with a method defined at the top level.

#### Converting Between a Block and a Proc

If a method's last parameter is `&block`, the block given to it is turned into a `Proc` object. Conversely, a lambda can be passed in place of a block with `&`. The measurement below looks at whether the object's identity is preserved in these two directions.

```ruby
# block_proc.rb — conversion between a block and a lambda
def take(&block) = block
b = take { |x| x + 1 }
puts b.class
puts b.lambda?
puts b.call(1)
l = ->(x) { x + 1 }
puts take(&l).equal?(l)
puts take(&l).lambda?
```
```
Proc
false
2
true
true
```

The object that came back from the block was a `Proc`, and `lambda?` gave `false`. When a lambda was passed with `&l`, `take` returned the same object: `equal?` was `true` and `lambda?` was still `true`. So `&` did not turn a lambda into a proc. **CB33.** The measurement was made only with a single-parameter block and one lambda.

#### Composing Callable Objects

`>>` and `<<` combine two callable objects into a single `Proc`. `f >> g` applies `f` first and then `g`; `f << g` does the reverse. A `Method` object can take part in the same composition.

```ruby
# compose.rb — composing procs
inc = ->(x) { x + 1 }
dbl = ->(x) { x * 2 }
puts (inc >> dbl).call(3)
puts (inc << dbl).call(3)
puts (inc >> dbl).class
puts (method(:Integer) >> inc).call("4")
```
```
8
7
Proc
5
```

`(inc >> dbl).call(3)` applied `inc` first and then `dbl`: `(3 + 1) * 2 = 8`. `(inc << dbl).call(3)` reversed the order: `inc(dbl(3)) = 7`. The result of the composition was a `Proc`. `method(:Integer) >> inc` composed a `Method` object with a lambda: `"4"` first became `4`, then gave `5`. **CB45.** The measurement was made with single-parameter lambdas.

#### arity and parameters

`arity` reports how many parameters a callable object expects, and `parameters` reports the kind of each. A proc and a lambda can report the same parameter list differently.

```ruby
# arity.rb — arity and parameters
puts proc { |a, b| }.arity
puts lambda { |a, b| }.arity
puts proc { |*a| }.arity
puts lambda { |a, b = 1| }.arity
puts lambda { |a, b = 1| }.parameters.inspect
puts proc { |a, b| }.parameters.inspect
```
```
2
2
-1
-2
[[:req, :a], [:opt, :b]]
[[:opt, :a], [:opt, :b]]
```

`proc { |a, b| }` and `lambda { |a, b| }` both reported `2`. `*a` printed `-1`, and one required plus one optional parameter printed `-2`: in both measurements the negative value was the negation of the required parameter count plus one. `parameters` shows the difference: in the lambda, `a` is required (`:req`), while in the proc the same parameter is optional (`:opt`). The looseness in the section The Difference Between a Proc and a Lambda can thus be read in the object's own definition. **CB46.**

#### A Closure Shares the Variable

A lambda carries not the value of the outer local variable but the variable itself. If the variable changes later, the lambda sees the new value; if the lambda writes to the variable, the outer variable changes too.

```ruby
# sharing.rb — a closure and the outer variable
x = 1
reader = -> { x }
x = 5
puts reader.call
writer = -> { x = 9 }
writer.call
puts x
```
```
5
9
```

When `reader` was defined, `x` was `1`; `x` was later set to `5` and `reader.call` gave `5`. The `x = 9` inside `writer` wrote to the outer `x`, and `puts x` printed `9`. A lambda uses the variable from where it was defined, not a copy. **CB47.** The measurement is for top-level locals; the case that crosses a method boundary was measured in the Carrying a Variable section.

#### A Lambda with case

A `case` expression tests a `when` condition with `===`. Thanks to `Proc#===`, a lambda can take part in this test directly: `===` calls the lambda with the given value.

```ruby
# case_lambda.rb — a lambda in a when branch
even = ->(n) { n.even? }
[1, 2].each do |n|
  case n
  when even then puts "#{n} even"
  else puts "#{n} odd"
  end
end
puts even === 2
puts "owner=#{even.method(:===).owner}"
```
```
1 odd
2 even
true
owner=Proc
```

`when even` called the lambda for every `n`: for `1` it returned `false`, so `else` ran; for `2` it returned `true`, so the `when` branch ran. `even === 2` gave `true` directly, and the owner of `===` was printed as `Proc`. So when `case` tests the condition, it sends the `===` message to the lambda, and the responder is a class. This ties in with the common question of the Procs and Lambdas lesson: the same callable object can be used with `call`, with `.()`, in place of a block with `&`, and here with `===`; in each, the object itself is the same and only the message changes. **CB54.** The measurement was made with a single-parameter lambda; the behavior of a multi-parameter lambda in a `when` branch was not tried. Since the value of the `===` branch is used in the `case` test itself, it was not assigned to a separate result variable in this measurement.

#### The Bounding Measurement

Can we see the difference between a proc and a lambda by looking at the owner of `call`? No: the owner is the same for both kinds, because both are instances of the `Proc` class. The difference is not in the owner but in the object's state.

```ruby
# owner.rb — the owner of the call message is the same for both kinds
loose = proc { |x| x }
strict = lambda { |x| x }
puts "proc    #{loose.method(:call).owner} lambda?=#{loose.lambda?}"
puts "lambda  #{strict.method(:call).owner} lambda?=#{strict.lambda?}"
```
```
proc    Proc lambda?=false
lambda  Proc lambda?=true
```

The owner is `Proc` on both lines; only `lambda?` differs. In the earlier lessons the `owner` question separated which class or module a method is defined in; here, because the two objects are of the same class, the same question does not reveal a difference. Sending the same message to two objects gives not two separate responders but two states of a single responder. So the `owner` question does not show the difference between a proc and a lambda: the class is the same, and the difference is carried by the object's own state. **CB12.** This measurement asks only about the `call` message; whether there is an owner difference for other messages is not measured in this lesson.

#### Summary

- A proc and a lambda are two ways of turning a block into an object; both have the class `Proc`.
- This lesson's responder is the **class**: the `Proc` answers the `call` message, and `call`, `.()` and square brackets give the same result.
- A lambda checks the argument count and raises `ArgumentError` on an excess; a proc drops the extra and turns the missing into `nil`.
- The procs obtained from a method object and from a symbol return `true` for `lambda?`.
- The bounding measurement: the owner of `call` is `Proc` for both kinds; the difference is in the `lambda?` state.

#### Next Step

A lambda's error was in the argument count. The next lesson moves on to the concept of an error itself: `rescue` is not a method but a construct of the language, and what this construct returns in which expressions will be measured.

### Exceptions

Source: https://academia.sh/en/courses/ruby-fundamentals/exceptions

The rescue, ensure and retry constructs are measured as the language's own syntax, raise as a message, and the match in a rescue branch as being made with a message to a class.

At the end of the previous lesson, a lambda called with the wrong number of arguments raised `ArgumentError`, and that exception was caught with `rescue`. This lesson makes that construct its subject. An **exception** is an event that interrupts the normal flow; **raising** produces it and **rescuing** receives it. M12/K01 measured the error channels in PHP; the question of an error channel is not asked here, only who answers the construct is measured.

This lesson's responder is **syntax**: `rescue` is not a method but a construct of the language itself.

#### rescue Is an Expression

The core observation rests on the `defined?` question: what does `defined?` say about a `begin ... rescue ... end` block? If it were a method call, `"method"` would come back.

```ruby
# reply.rb — helpers that classify who answers a message

# Classification asks in order: is the expression a message at all (defined?),
# then do two different blocks give two different results, then who owns the method.
def responder(definition, owner: nil, result: nil, with_blocks: nil)
  return ["syntax", "defined? #{definition}"] unless definition == "method"

  if with_blocks
    first, second = with_blocks.call
    return ["block", "#{first.inspect} / #{second.inspect}"] unless first == second
  end

  kind = owner.instance_of?(Class) ? "class" : "module"
  [kind, "#{owner}, result #{result.inspect}"]
end
```

```ruby
# rescue.rb — who answers the rescue construct
require_relative "reply"

definition = defined?(begin; Integer("x"); rescue ArgumentError; 0; end)
kind, evidence = responder(definition)
puts "#{kind}  #{evidence}"
value = Integer("x") rescue 0
puts value
puts Integer("x", exception: false).inspect
```
```
syntax  defined? expression
0
nil
```

The first line is the core's `rescue` observation: the **responder** is **syntax**, and `defined?` gave `"expression"`. The second line shows the statement modifier form: `Integer("x") rescue 0` caught the exception and gave `0`. The third line did the same job with the `exception: false` keyword argument, without raising an exception, and returned `nil`. **CB15.** This lesson measures the modifier `rescue` only with `ArgumentError`; whether it catches other exception classes is not measured.

On the second line, `Integer("x") rescue 0` was first assigned to a variable and then printed; the modifier form was not written directly inside a method argument or inside `defined?`. How it resolves in those positions is not measured in this lesson.

#### Exception Classes

Every exception is an instance of a class. `e.class` gives its type; the class name is used instead of the message (`e.message`), because the message text can change between versions. The lesson's own `raise` text is outside this rule.

```ruby
# classes.rb — exception classes
def attempt
  yield
rescue => e
  e.class
end

puts attempt { 1 / 0 }
puts attempt { raise "x" }
puts attempt { Integer("x") }
puts 1.0 / 0

class OutOfStock < StandardError; end

begin
  raise OutOfStock, "out of stock"
rescue OutOfStock => e
  puts "#{e.class} #{e.message}"
end
```
```
ZeroDivisionError
RuntimeError
ArgumentError
Infinity
OutOfStock out of stock
```

Integer division by zero raised `ZeroDivisionError`; float division gave `Infinity` instead of an exception. A plain `raise "x"` produced a `RuntimeError`. Our own class `OutOfStock` derived from `StandardError`, and `rescue OutOfStock => e` caught only it. The message could be printed here because it is the lesson's own `raise` text.

#### ensure and retry

The `ensure` branch runs whether or not there is an exception; it is used for resource cleanup. `retry` runs the `begin` block again from the start.

```ruby
# retry.rb — ensure and retry
attempts = 0
begin
  attempts += 1
  raise "not yet" if attempts < 3
  puts "succeeded #{attempts}"
rescue
  retry
end

events = []
begin
  events << 1
  raise "error"
rescue
  events << :rescued
ensure
  events << 2
end
puts events.inspect
```
```
succeeded 3
[1, :rescued, 2]
```

On every failed attempt, `retry` restarted the block, and the job finished on the third attempt. In the second block, the `ensure` branch ran even after the exception was caught. **CB16.** The retry count is the lesson's own counter; in a real retry scenario an upper bound is needed to prevent an endless loop, and that is not measured here.

#### Branches and else

A `begin` block can carry several `rescue` branches; the exception goes to the first branch that matches. The `else` branch runs only when no exception occurred. A class's parent is asked with `superclass`.

```ruby
# branches.rb — rescue branches and else
def classify(input)
  Integer(input)
rescue ArgumentError
  "invalid"
rescue TypeError
  "type"
else
  "ok"
end

puts classify("5")
puts classify("x")
puts classify(nil)
puts ZeroDivisionError.superclass
puts StandardError.superclass
```
```
ok
invalid
type
StandardError
Exception
```

Because `"5"` raised no exception, the `else` branch ran. `"x"` went to the `ArgumentError` branch and `nil` to the `TypeError` branch: the first matching branch won. The parent of `ZeroDivisionError` turned out to be `StandardError`, and the parent of that is `Exception`; a bare `rescue` therefore catches `ZeroDivisionError`. **CB18.** The order of the branches matters: if a broad class is written first, the branches after it never run, but that behavior is not measured here.

#### StandardError and Exception

A `rescue` with no parameter and `rescue => e` catch only `StandardError` and its subclasses. Exceptions that stand outside `StandardError` in the hierarchy pass through this catcher. The measurement below shows that an `ArgumentError` is caught, that a `NotImplementedError` is not caught, and the relevant parent classes.

```ruby
# hierarchy.rb — which exceptions does rescue catch
def guarded
  yield
  "none"
rescue => e
  "caught #{e.class}"
end

puts guarded { raise ArgumentError }
begin
  guarded { raise NotImplementedError }
rescue NotImplementedError => e
  puts "outside #{e.class}"
end
puts NotImplementedError.superclass
puts ScriptError.superclass
puts KeyError.superclass
puts IndexError.superclass
```
```
caught ArgumentError
outside NotImplementedError
ScriptError
Exception
IndexError
StandardError
```

`ArgumentError` was caught and `caught ArgumentError` was printed. `NotImplementedError` passed through the `rescue` of `guarded` and was caught in the outer `begin` block. The parent chain explains this: `NotImplementedError` is under `ScriptError`, which is under `Exception`; it does not reach `StandardError`. `KeyError`, on the other hand, connects to `StandardError` through `IndexError`. **CB34.** Only the parent chain of these four classes was printed.

#### ensure Does Not Change the Return Value

The `ensure` branch runs however the method ends. However, the last expression of this branch does not become the method's return value. `ensure` runs on an early exit with `return` too.

```ruby
# ensure_value.rb — the ensure branch and the return value
def value
  1
ensure
  2
end

def early
  return 1
ensure
  puts "cleanup"
end

puts value
puts early
```
```
1
cleanup
1
```

`value` returned `1`; the `2` inside `ensure` did not change the return value. `early` exited with `return 1`, but `ensure` first printed the `cleanup` line and then `1` was returned. **CB35.** Only these two forms were measured; writing a `return` inside `ensure` was not measured.

#### Re-raising

A bare `raise` inside `rescue` re-raises the caught exception as it is. This way a layer records the event but leaves the decision to the layer above.

```ruby
# reraise.rb — re-raising with raise
def outer
  yield
rescue ArgumentError => e
  puts "logged #{e.class}"
  raise
end

begin
  outer { Integer("x") }
rescue => e
  puts "again #{e.class}"
end
```
```
logged ArgumentError
again ArgumentError
```

The inner `rescue` first printed the `logged ArgumentError` line and raised the same exception with a bare `raise`. The outer `rescue` saw the same class: `again ArgumentError`. **CB36.** The exception message was not printed; only the class was measured.

#### The retry Limit and the Cause Chain

`retry` runs the block from the start; left unbounded, it falls into an endless loop. That is why it is always written together with a counter. If a new exception is raised inside a `rescue`, the earlier exception remains on the new one through `cause`.

```ruby
# limit.rb — a retry counter and cause
attempts = 0
begin
  attempts += 1
  raise "x" if attempts < 10
rescue
  retry if attempts < 3
  puts "gave up #{attempts}"
end

begin
  begin
    Integer("x")
  rescue => e
    raise RuntimeError, "upper"
  end
rescue => upper
  puts upper.cause.class
end
```
```
gave up 3
ArgumentError
```

In the first two rounds `retry` ran the block again; in the third round the condition was not met, so `gave up 3` was printed. In the second part, `Integer("x")` raised an `ArgumentError`, a new `RuntimeError` was raised inside the `rescue`, and the outer `upper.cause.class` gave `ArgumentError`. **CB37.** `retry` was measured only in the `begin` and `rescue` form.

#### Multiple Classes and Branch Order

A `rescue` can list several classes separated by commas. If there are several `rescue` branches, the first matching branch runs and the later ones are not looked at.

```ruby
# order.rb — a rescue list and branch order
def classify
  yield
rescue ZeroDivisionError, TypeError
  "both"
rescue StandardError
  "general"
end
puts classify { 1 / 0 }
puts classify { 1 + nil }
puts classify { Integer("x") }
def reversed
  yield
rescue StandardError
  "general"
rescue ZeroDivisionError
  "specific"
end
puts reversed { 1 / 0 }
```
```
both
both
general
general
```

`rescue ZeroDivisionError, TypeError` caught both `1 / 0` and `1 + nil`: both printed `both`. `Integer("x")` was not in this list and fell to the `general` branch. In the `reversed` method, `StandardError` was written first; `1 / 0` got caught by that branch and the `specific` branch was never reached. The specific class must be written before the general one. **CB48.** The measurement was made with these three exception classes.

#### The Order of begin, else and ensure

The branches in a `begin` block run in a particular order. The order in which the branches run is read from the symbols appended to an array.

```ruby
# sequence.rb — the running order of the branches
steps = []
begin
  steps << :body
rescue
  steps << :rescue
else
  steps << :else
ensure
  steps << :ensure
end
puts steps.inspect
steps = []
begin
  steps << :body
  raise "x"
rescue
  steps << :rescue
else
  steps << :else
ensure
  steps << :ensure
end
puts steps.inspect
```
```
[:body, :else, :ensure]
[:body, :rescue, :ensure]
```

With no error, `else` ran after the body and `ensure` ran last. When an error was raised, `rescue` came into play, `else` was skipped and `ensure` ran again. `ensure` took the last place in both cases. **CB49.**

#### An Exception Class with a Field

Your own exception class can carry extra information. You store the field in `initialize` and pass the message with `super`.

```ruby
# with_field.rb — an exception class with its own field
class StockError < StandardError
  attr_reader :quantity

  def initialize(quantity)
    @quantity = quantity
    super("insufficient stock")
  end
end

begin
  raise StockError.new(3)
rescue StockError => e
  puts "#{e.class} quantity=#{e.quantity} message=#{e.message}"
end
puts StockError.superclass
puts StockError.instance_method(:quantity).owner
```
```
StockError quantity=3 message=insufficient stock
StandardError
StockError
```

`StockError` derived from `StandardError`, carried the `quantity` field and gave its own message text with `super`. Because `rescue` reached the field with `e.quantity`, `quantity=3` was printed; `message=insufficient stock` is text the lesson wrote itself. The owner of the `quantity` reader is `StockError`: the class answers this method. **CB50.**

#### catch and throw

`catch` and `throw` provide a labeled exit. `throw` climbs out to the nearest `catch` block opened with the same label and carries a value to it.

```ruby
# tags.rb — catch and throw
r = catch(:done) do
  [1, 2, 3].each { |n| throw :done, n * 10 if n == 2 }
  :nothing
end
puts r
puts "owner=#{method(:throw).owner}"
```
```
20
owner=Kernel
```

`throw :done, 20` ended the inner `each` block and the outer `catch` block; `catch` returned `20` instead of `:nothing`. The owner of `throw` was printed as `Kernel`. **CB51.** Only this single-label example was measured.

#### Nested ensure and the rescue Suffix

If an inner `begin` block has an `ensure` and the exception is not caught there, `ensure` runs before the exception goes out. As a short form, `expression rescue value` uses the value if the expression fails; this form can be assigned to a variable in parentheses.

```ruby
# nested.rb — nested ensure and the rescue suffix
log = []
begin
  begin
    log << :inner
    raise ArgumentError
  ensure
    log << :cleanup
  end
rescue ArgumentError
  log << :outer
end
puts log.inspect
recovered = (Integer("x") rescue -1)
puts recovered
```
```
[:inner, :cleanup, :outer]
-1
```

The exception was not caught in the inner block, but `ensure` ran before it went out, and the order became `[:inner, :cleanup, :outer]`. So `ensure` runs before the outer `rescue`. When `Integer("x")` failed, the `-1` in the suffix form was used. **CB55.** That the suffix form catches only the `StandardError` family was not measured; only this single example was tried in this lesson.

#### The Bounding Measurement

`raise` is a message, that is, a method call; `rescue` is syntax. The two have different responders. One can also ask whether the `rescue ArgumentError => e` branch makes a match of its own: does the branch ask the `ArgumentError` class with a message?

```ruby
# matching.rb — the match of a rescue branch
puts "raise owner=#{method(:raise).owner}"

begin
  Integer("x")
rescue ArgumentError => e
  puts ArgumentError === e
  puts "=== owner=#{ArgumentError.method(:===).owner}"
end
```
```
raise owner=Kernel
true
=== owner=Module
```

`raise` is a method and its owner is the `Kernel` module. The branch's match is made with the `===` message: `ArgumentError === e` gave `true`, and the owner of `===` is `Module`, not `Class`; that is, the class takes its own `===` from a higher structure. This sharpens the distinction: the language resolves the `rescue` keyword, but which branch receives the exception is decided by the class's `===` answer. The language builds the construct and the class decides the match. The class written on the same `rescue` line is therefore an ordinary class object and answers its own match message. **CB17.** This measurement was made only for the `ArgumentError` branch; that different exception classes get the same message from the same owner is not assumed.

The result agrees with the core's classification: `defined?` named the expression `"expression"`, that is, the `rescue` keyword is not a message; the branch match, as seen above, is made with the `===` message. The raising side is different; `raise` is an ordinary method, it comes from the `Kernel` module and takes an exception class or text as an argument. So in an exception event two responders stand side by side: a module answers the raising and the language itself answers the rescuing. This is also the reason **syntax** was chosen as the lesson's responder: the question measured is who builds the rescuing construct.

#### Summary

- An exception is an event that interrupts the normal flow; `rescue` catches, `raise` raises, `ensure` runs in every case, and `retry` restarts the block.
- This lesson's responder is **syntax**: `defined?` returns `"expression"` for `begin ... rescue ... end`.
- The statement modifier `rescue` and `exception: false` do the same job in two ways: a value instead of an exception.
- `raise` is a method and its owner is the `Kernel` module; the match of a `rescue` branch is made with the `===` message.
- The bounding measurement: `rescue` is syntax, but the match inside its branch is a message to a class.

#### Next Step

In this lesson, all the operations that raise exceptions ran in memory. The last lesson brings files and input/output: whether the thing that opens a file is a block or a method, and which construct `ensure` cleans up in this job.

### Files and Input/Output

Source: https://academia.sh/en/courses/ruby-fundamentals/files-and-io

In a File.open call, the block decides the result and the method decides that the file is closed; even when the block raises an exception, the file closes. The lesson also gives the closing table of the course's twelve observations.

At the end of the previous lesson, `ensure` was seen as a branch that runs in every case. Files are where this construct is used most often: an opened file has to be closed, and forgetting to close it is a leak. In Ruby, `File.open` can be given a block; the question is who owns the job of closing. **Input/output** is the program's exchange of data with the outside world, that is, with files and the standard streams.

This lesson's responder is the **block**: the `File.open` message gives two different results for the same file with two different blocks. At the end of the lesson, all twelve observations of the course are gathered in a single table.

#### The File Reading Observation

The core's twelfth observation is `file reading`. The classification helper `reply.rb` has been the same since the start of the course and is only written to disk here.

```ruby
# reply.rb — helpers that classify who answers a message

# Classification asks in order: is the expression a message at all (defined?),
# then do two different blocks give two different results, then who owns the method.
def responder(definition, owner: nil, result: nil, with_blocks: nil)
  return ["syntax", "defined? #{definition}"] unless definition == "method"

  if with_blocks
    first, second = with_blocks.call
    return ["block", "#{first.inspect} / #{second.inspect}"] unless first == second
  end

  kind = owner.instance_of?(Class) ? "class" : "module"
  [kind, "#{owner}, result #{result.inspect}"]
end
```

```ruby
# file_reading.rb — the same File.open gives two results with two blocks
require_relative "reply"

File.write("note.txt", "first line\nsecond line\n")

definition = defined?(File.open("note.txt"))
kind, evidence = responder(definition, with_blocks: -> {
  [File.open("note.txt") { |f| f.gets.chomp }, File.open("note.txt") { |f| f.readlines.size }]
})
puts definition
puts "#{kind}  #{evidence}"
puts File.method(:open).owner.instance_of?(Class)
```

```
method
block  "first line" / 2
true
```

`File.write` wrote the two-line file `note.txt`. `defined?` reported the call without a block as `"method"`, so there is a message. The first block returned the first line and the second block returned the line count: the same message gave `"first line"` and `2`. The responder is the **block**. The owner of `File.open` is a class (`instance_of?(Class)` gives `true`); why the owner appears as a singleton class is the subject of the second course, and here only the result is read. **CB56.** The measurement was made with a single file and two blocks.

#### Line Reading and Writing Streams

The shortcuts without a block open and close the file themselves. `File.write` returns the number of bytes written; `File.readlines` makes the lines an array and `chomp: true` drops the line endings; `File.foreach` yields the lines one at a time. A file that does not exist raises `Errno::ENOENT`. Because the error message carries a path, only the class is printed.

```ruby
# streams.rb — writing, reading and a missing file
File.write("note.txt", "first line\nsecond line\n")

puts File.write("b.txt", "abc")
puts File.readlines("note.txt", chomp: true).inspect
puts File.foreach("note.txt").count
begin
  File.read("missing.txt")
rescue Errno::ENOENT => e
  puts e.class
end
```

```
3
["first line", "second line"]
2
Errno::ENOENT
```

`abc` is three bytes and `File.write` returned `3`. `readlines` gave an array of two strings. When `foreach` is called without a block it returns an iterator; `count` consumed it and found `2`. The missing file raised `Errno::ENOENT` and the branch caught it. **CB57.** `File.write` was measured only with string content.

When working with an open file, the line reading methods are called on the instance. The owners of these can also be measured.

```ruby
# owners.rb — the owners of file and standard output methods
File.write("note.txt", "first line\nsecond line\n")

File.open("note.txt") do |f|
  puts f.class
  puts f.method(:gets).owner
  puts f.method(:each_line).owner
end
puts $stdout.method(:puts).owner
```

```
File
IO
IO
IO
```

The file object is a `File`, but `gets` and `each_line` are defined not in its own class but in the `IO` class. `$stdout.puts` is in the same `IO` class too. A file and standard output are members of the same class family, and for all three the responder is the class. **CB58.** The owners were measured on Ruby 4.0.7; which method is defined in which class can move from version to version.

#### A File Left Open and a File Closed

If `File.open` is called without a block it returns the file object and the file stays open; closing it is the caller's job. In a call with a block, the file object is given to the block, the file closes when the block ends, and the value of the call is the last expression of the block.

```ruby
# closing.rb — File.open without and with a block
File.write("note.txt", "first line\nsecond line\n")

f = File.open("note.txt")
puts f.class
puts f.closed?
f.close
puts f.closed?

taken = nil
value = File.open("note.txt") do |file|
  taken = file
  puts file.closed?
  42
end
puts value
puts taken.closed?
```

```
File
false
true
false
42
true
```

The call without a block returned a `File`, `closed?` printed `false`, and it became `true` only when `close` was called by hand. In the call with a block, the file was open inside the block. The block ended with `42`: the return value was `42`, while the file object carried out of the block was now closed. The result comes from the block, the closing comes from the method. **CB59.** The measurement was made for a local file and a single block.

#### Opening Modes

`File.open` takes a mode as its second argument. `"w"` resets the file as it opens it, and `"a"` appends to the end of the existing content. The difference is measured with the line count. The value of a `File.open` call with a block is the last expression of the block; `write` gives the number of bytes written.

```ruby
# modes.rb — w resets, a appends
File.write("mode.txt", "one\ntwo\n")

File.open("mode.txt", "a") { |f| f.puts "three" }
puts File.readlines("mode.txt").size

result = File.open("mode.txt", "w") { |f| f.write("one\n") }
puts result
puts File.readlines("mode.txt").size

File.open("mode.txt", "a") { |f| puts f.method(:puts).owner }
```

```
3
4
1
IO
```

When a line was added to the two-line file with `"a"`, the count became `3`. Opening with `"w"` reset the file: the block returned the value of `write`, that is, the `4` bytes, and the line count dropped to `1`. The `puts` method is defined in the `IO` class; the responder is the class. **CB60.** The measurement was made only with the `"a"` and `"w"` modes; the read and write modes were not tried together.

#### The Difference Between puts and print

When writing to a file, `puts` adds a line ending and `print` does not. The difference shows in the `inspect` output of the written content.

```ruby
# write_difference.rb — puts adds a line ending, print does not
File.open("diff.txt", "w") do |f|
  f.puts "a"
  f.print "b"
  f.puts "c"
end
puts File.read("diff.txt").inspect
puts File.size("diff.txt")
File.open("diff.txt") { |f| puts f.method(:print).owner }
```

```
"a\nbc\n"
5
IO
```

The file has `a`, a line ending, `b`, `c` and a line ending again; `b` and `c` were written adjacent. Five bytes in total. `print` is also in the `IO` class; the responder is the class. **CB61.** The content was ASCII strings; with multibyte characters the byte count separates from the character count, and that was not measured here.

#### Existence and Size

`File.exist?` tells whether a path exists and `File.size` gives the size in bytes. An open file object also has its own `size` method, and its owner is `File`.

```ruby
# size.rb — existence and size
File.write("size.txt", "abcd")
puts File.exist?("size.txt")
puts File.exist?("missing.txt")
puts File.size("size.txt")
File.open("size.txt") do |f|
  puts f.size
  puts f.method(:size).owner
end
```

```
true
false
4
4
File
```

It returned `true` for the existing file and `false` for the missing one. The size was `4` bytes and the open object gave the same value. This time the owner is not `IO` but `File`: information specific to files sits in the subclass, stream work in the parent class. **CB62.** An empty file and a deleted file were not measured.

#### Line by Line and Enumerable

If `each_line` is not given a block it returns an iterator, and `chomp: true` drops the line endings. The iterator's `map` method comes from the `Enumerable` module. Reading, transforming, filtering and writing can be chained as one flow.

```ruby
# flow.rb — read, map, select, write
File.write("input.txt", "plum\nfig\ngrape\nox\n")

lines = []
File.open("input.txt") { |f| f.each_line(chomp: true) { |s| lines << s } }
puts lines.inspect

iterator = File.foreach("input.txt", chomp: true)
puts iterator.method(:map).owner

selected = iterator.map(&:upcase).select { |s| s.size > 3 }
written = File.write("output.txt", selected.join("\n") + "\n")
puts written
puts File.read("output.txt").inspect
```

```
["plum", "fig", "grape", "ox"]
Enumerable
11
"PLUM\nGRAPE\n"
```

`each_line` gave the four lines without the trailing line ending. When `File.foreach` was called without a block it returned an iterator, and `map` for it is in the `Enumerable` module; the responder is the module. After `upcase`, `select` kept those longer than three letters: `PLUM` and `GRAPE`. The written `PLUM`, a line ending, `GRAPE`, a line ending is eleven bytes. **CB63.** The flow was single-pass and measured with a small input; what happens to memory with a large file was not measured.

#### Reading Standard Input from a Pipe

Standard input is the stream from which the program takes lines from outside, and it is read with `$stdin`. The lesson's blocks do not wait for interactive input; so the input is given through a **pipe**: the output of one command becomes the input of the next.

```ruby
# read.rb — reads standard input line by line
first = $stdin.gets.chomp
rest = $stdin.each_line.map(&:chomp)
puts first
puts rest.inspect
puts $stdin.method(:gets).owner
```

```bash
# input.sh — feeds read.rb from a pipe
printf 'one\ntwo\nthree\n' | ruby read.rb
```

```
one
["two", "three"]
IO
```

`gets` read the first line and `each_line` consumed the rest. `$stdin.gets` is also in the `IO` class; the same class reads a file and the keyboard. **CB64.** The input was a fixed three-line string; empty input and very large input were not measured.

The same pipe is also used for counting lines. When `each_line` is called without a block it returns an iterator; `count` comes to it from the `Enumerable` module.

```ruby
# count.rb — counts the lines on standard input
puts $stdin.each_line.count
puts $stdin.method(:each_line).owner
puts $stdin.each_line.method(:count).owner
```

```bash
# count.sh — feeds count.rb from a pipe
printf 'a\nb\nc\nd\n' | ruby count.rb
```

```
4
IO
Enumerable
```

The four-line input was counted as `4`. The `each_line` method is in the `IO` class, while the iterator's `count` method is in the `Enumerable` module; these two responders work one after the other in the same expression. **CB65.** The input was fixed and four lines long; input whose last line does not end with a line ending was not measured.

#### The Bounding Measurement

This lesson's responder is the block, but if this sentence is left half finished it misleads: the block determines the result, and it is not the block that cleans up the resource. The block form of `File.open` takes on the closing. To separate this, an exception is raised inside the block. The file object is kept outside the block, the exception is caught, and then the state of the file is asked. For comparison, the same exception is raised next to a file opened without a block.

```ruby
# exception_closing.rb — the file closes even when the block raises
File.write("note.txt", "first line\nsecond line\n")

taken = nil
begin
  File.open("note.txt") do |file|
    taken = file
    raise "block broke"
  end
rescue => e
  puts e.class
end
puts taken.closed?

manual = File.open("note.txt")
begin
  raise "left open"
rescue => e
  puts e.class
end
puts manual.closed?
manual.close
puts manual.closed?
```

```
RuntimeError
true
RuntimeError
false
true
```

In the first case the block raised a `RuntimeError`, the branch caught it, and the file was closed all the same (`true`). In the second case the same exception was raised next to a file opened without a block, and the file stayed open (`false`) until `close` was called by hand. The difference is not in the content of the block but in where the responsibility for closing sits: `File.open` with a block runs the block inside an `ensure` and closes the file whatever happens. This is the resource cleanup construct of the previous lesson placed in the language's library. The block determines the result and `File.open` takes on the resource cleanup. **CB66.** The measurement was made with `RuntimeError`; leaving the block with `break` or `throw` was not tried.

#### Summary

- `File.read`, `File.readlines` and `File.foreach` open and close the file themselves; `File.write` returns the number of bytes written; reading a missing file raises `Errno::ENOENT`.
- This lesson's responder is the **block**: the same `File.open` message gave `"first line"` with one block and `2` with the other.
- `File.open` without a block leaves the file open; in a call with a block, `closed?` becomes `true` when the block ends and the return value is the block's value.
- `gets`, `each_line` and `$stdout.puts` are defined in the `IO` class; standard input is read by feeding it from a pipe.
- `"w"` resets the file and `"a"` appends to the end; `puts` adds a line ending and `print` does not; the iterator's `map` and `count` methods are in the `Enumerable` module.
- The bounding measurement: even if the block raises an exception the file closes, because the closing is done by the `File.open` method, not the block.

#### Course Wrap-Up

Every lesson of the course asked the same question: who answers this message. The table puts side by side the message each of the twelve lessons measured, that lesson's responder, and what its bounding measurement said. The responder column is what is written in each lesson's `## Summary` section.

| Lesson | Measured message | Responder | Bounding measurement |
|---|---|---|---|
| The Design Philosophy of Ruby | `5.between?(1, 10)` | module | `between?` is in `Comparable`, `<=>` in the receiver's class |
| An Intuition for the Object Model | `5 + 3` | class | `Module` answers the `Integer.===` message |
| Variables and Constants | `x = 5` | syntax | `y += 1` is an assignment, `Integer` answers the addition inside it |
| Control Flow | `(1..10) === 7` | class | `case` asks the class of the branch object |
| Methods | `format("%03d", 7)` | module | a top-level `def` is written to `Object` and is private |
| Symbols and Strings | `"name".to_sym` | class | the magic comment does not change the owner, it applies per file |
| Arrays and Hashes | `Hash.new { 0 }[:x]` | block | the block answers only when the key is missing |
| Blocks | `numbers.map { ... }` | block | `each` gives `[1, 2]` with both blocks, its owner is `Array` |
| The `Enumerable` Module | `(1..5).each_slice(2)` | module | `sum` is in the module on a range, in `Array` on an array |
| Procs and Lambdas | `->(n) { n * 2 }.call(3)` | class | the owner is the same, the difference is in the `lambda?` state |
| Exceptions | `begin ... rescue ... end` | syntax | the match of a branch is made with the `===` message |
| Files and Input/Output | `File.open("note.txt") { ... }` | block | the method, not the block, does the closing |

The distribution of responders over the twelve lessons: **4 class · 3 module · 3 block · 2 syntax**. This is the same as the distribution of the core's twelve observations, because the core carries one observation per lesson. Two results can be read from the table. First, the responder often did not work alone: most of the bounding measurements showed that another responder was working next to the lesson's responder. Second, the answer to the responder question depends not on the message but on the message's receiver, the block given and the form of the expression. **CB67.** The table gives the distribution of this course's twelve observations; it carries no claim about the distribution over Ruby as a whole.

The course ends here. In Ruby, the code that produces the result of an operation sits in four places: in the receiver's class, in a mixed-in module, in the block the caller gave, or in the language's own syntax. This course told these four places apart only by measuring the **result**; the question of why a method is found in a particular class or module stayed open. The subject area of the next course is class definition, inheritance, modules and mixins, the method lookup chain and metaprogramming; those lessons will take up the machinery behind today's measurements.

