Checkpoint: Advanced Ruby

This checkpoint consolidates the advanced block of the course — custom iterators, method arguments, Comparable, refinements, fibers, threads, Rake, OptionParser, and benchmarking — into one integrated build challenge and quiz.

Learn Checkpoint: Advanced Ruby in our free Ruby course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick…

Part of the free Ruby course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

You'll combine the Comparable module with a lazy Enumerator to prove the pieces fit together.

What This Checkpoint Reviews

📚 Recap: What You've Mastered

Before the build, here's the toolkit you've assembled across the last nine lessons. Each row is a tool you should now reach for without prompting.

🛠️ Build Challenge: Sortable Temperatures + a Lazy Curve

Combine two big ideas. Make a Temperature class that's sortable via Comparable , and a cooling_from generator that returns a lazy Enumerator yielding ever-cooler readings on demand. Fill the three blanks below, then check yourself against the full solution.

Here's the complete, runnable program. Compare it line by line with your attempt and re-type anything you missed.

📝 Checkpoint Quiz

Answer each in your head, then expand to check. If any feel shaky, revisit that lesson before the capstone.

Define each , yielding every element. Enumerable implements all its collection methods in terms of each .

Give it a default after the colon, e.g. def f(role: "member") . A bare colon ( role: ) makes it required.

It returns -1, 0, or 1 (or nil if uncomparable). Comparable derives < , <= , == , > , >= , between? , and clamp .

A refinement is lexically scoped — active only in files that call using — so it doesn't change the class for the whole program or for other libraries.

Threads help with I/O-bound work (the lock releases while waiting), not CPU-bound work. A Ractor provides true multi-core parallelism with isolated memory.

The winner — which approach is consistently faster — not the raw seconds, which depend on the machine and the run.

Practice quiz

What single method must you define so that 'include Enumerable' gives you map, select, and count for free?

  • to_a
  • map
  • each
  • size

Answer: each. Enumerable implements all its collection methods in terms of each, so you only need to define each (yielding every element).

In the build challenge, which module does Temperature include to get <, sort, min, max, and clamp from one method?

  • Comparable
  • Enumerable
  • Kernel
  • Numeric

Answer: Comparable. include Comparable derives <, <=, ==, >, >=, between?, and clamp once you define the spaceship operator <=>.

What does the spaceship operator <=> return?

  • true or false
  • Always an integer count
  • The larger of the two values
  • -1, 0, or 1 (or nil if uncomparable)

Answer: -1, 0, or 1 (or nil if uncomparable). <=> returns -1, 0, or 1 (or nil when the two cannot be compared). Comparable builds all the other operators from it.

Inside an Enumerator block, which line pushes the next value out to the consumer?

  • return Temperature.new(current)
  • yielder << Temperature.new(current)
  • puts Temperature.new(current)
  • next Temperature.new(current)

Answer: yielder << Temperature.new(current). yielder << value (equivalent to yielder.yield value) emits the next element from the Enumerator.

How do you make a keyword argument OPTIONAL in a method definition?

  • Give it a default after the colon, e.g. role: 'member'
  • Use a bare colon, e.g. role:
  • Prefix it with *
  • Wrap it in parentheses

Answer: Give it a default after the colon, e.g. role: 'member'. A bare colon (role:) makes the keyword required; supplying a default (role: 'member') makes it optional.

Which argument form captures any number of EXTRA keyword arguments into a hash?

  • *args
  • &block
  • **kwargs
  • (...)

Answer: **kwargs. **kwargs collects extra keyword arguments into a hash. *args collects positional ones, &block captures a block.

Why is a refinement safer than re-opening (monkey-patching) a class?

  • It runs faster
  • It is lexically scoped, active only where you call 'using'
  • It cannot be undone
  • It works only on built-in classes

Answer: It is lexically scoped, active only where you call 'using'. A refinement applies only in files that call using, so it does not change the class for the whole program or other libraries.

Given Ruby's GVL, when do threads actually help?

  • For CPU-bound number crunching
  • Only inside a Fiber
  • Never; they always block
  • For I/O-bound work, where the lock releases while waiting

Answer: For I/O-bound work, where the lock releases while waiting. The GVL releases during I/O waits, so threads speed up I/O-bound work. For true multi-core CPU parallelism use a Ractor.

Which construct gives true multi-core parallelism with isolated memory?

  • Thread
  • Ractor
  • Fiber
  • Mutex

Answer: Ractor. Ractor provides actual parallelism across cores with isolated memory. Threads share memory but are limited by the GVL for CPU work.

When benchmarking two approaches, what should you report as the result?

  • The exact seconds from one run
  • The memory address
  • The winner (which approach is consistently faster)
  • The number of lines of code

Answer: The winner (which approach is consistently faster). Raw seconds depend on the machine and run, so report which approach is consistently faster rather than absolute times.