Checkpoint: Advanced Ruby

Reviewed & published by Brayan K

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.

Part of the free Ruby course at LearnCodingFast — hands-on lessons with worked examples and the output they print, 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.

ConceptKey idea
Custom iteratorsdefine each + include Enumerable for map/select free; .lazy for infinite streams
Method argumentspositional, keyword, *args, **kwargs, &block, and (...) forwarding
Comparabledefine <=> to get all operators, between?, clamp, and sortability
Refinementsrefine + using for scoped, opt-in changes — safer than open classes
FibersFiber.yield/resume for cooperative pausing and lazy generators
Threads & RactorThread.new/join, Mutex for shared state, Ractor for true parallelism
Raketasks with dependencies, namespaces, desc, and rake -T
OptionParserparse ARGV flags into an options hash, with types and exit codes
BenchmarkingBenchmark.bm/bmbm/realtime — report the winner, not the seconds

🛠️ 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.

# 🎯 CHECKPOINT BUILD — finish the blanks (___) to make it run.
# Goal: a Temperature class that is SORTABLE (Comparable) plus a LAZY
# Enumerator that yields a cooling sequence on demand.

class Temperature
  include ___                       # 👉 which module gives you <, sort, clamp?
  attr_reader :celsius

  def initialize(celsius)
    @celsius = celsius
  end

  def <=>(other)                    # one method unlocks all comparisons
    celsius ___ other.celsius       # 👉 the operator that returns -1/0/1
  end

  def to_s
    "#{celsius}C"
  end
end

def cooling_from(start, step)
  Enumerator.new do |yielder|
    current = start
    loop do
      yielder ___ Temperature.new(current)   # 👉 push the next value out
      current -= step
    end
  end
end

readings = [Temperature.new(22), Temperature.new(8), Temperature.new(15)]
puts "Sorted:  #{readings.sort.map(&:to_s).join(', ')}"
puts "Coldest: #{readings.min}"
puts "Warmest: #{readings.max}"
puts "Clamped: #{Temperature.new(40).clamp(Temperature.new(0), Temperature.new(30))}"

curve = cooling_from(20, 4)
puts "Curve:   #{curve.first(5).map(&:to_s).join(', ')}"
freezing = curve.lazy.find { |t| t <= Temperature.new(0) }
puts "Freezes: #{freezing}"

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.

🎉 Checkpoint Complete!

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.

Continue this course

Frequently asked questions

What advanced Ruby topics does this checkpoint cover?

This checkpoint consolidates the advanced and intermediate block of the course: custom iterators and Enumerator (including Enumerable, lazy evaluation), the five method-argument styles (positional, keyword, splat, double-splat, block, and forwarding), the Comparable module, refinements for scoped monkey-patching, fibers and cooperative pausing, threads with Mutex and Ractor, automation with Rake, building CLI tools with OptionParser and ARGV, and benchmarking with the Benchmark library. The build challenge deliberately combines Comparable and a lazy Enumerator so you exercise several of these ideas at once.

How is a checkpoint lesson different from a normal lesson?

A normal lesson introduces one new concept with worked examples. A checkpoint instead reviews and integrates everything from the preceding stretch: it recaps the key ideas, gives a multi-step build challenge that forces you to combine them, provides a full reference solution you can check yourself against, and finishes with a short quiz to confirm the concepts stuck. The aim is consolidation and confidence — proving you can reach for the right tool unprompted, rather than learning something brand new.

Why does the build challenge pair Comparable with an Enumerator?

Because the two complement each other and reflect real Ruby design. Comparable makes individual objects orderable, which is exactly what sort, min, and max need; an Enumerator produces a stream of those objects, lazily if it's infinite. Combining them mirrors how you'd model real data: a class of comparable values plus a generator that yields them on demand. Building both in one program shows you understand not just each feature in isolation but how they fit together into idiomatic, composable code.

What should I do if I get stuck on the build challenge?

First, run the starter with your best guesses for the blanks and read any error message — Ruby's errors usually name the missing method or module precisely. If the comparison operators don't work, you probably forgot include Comparable or defined <=> wrongly. If the Enumerator never produces values, check that you used yielder << value (or yielder.yield value) inside the block. When you've genuinely tried, open the full solution, compare it to yours line by line, and re-type the parts you missed so the pattern sticks.

Where does the course go after this checkpoint?

Next is the Capstone Project, where you bring the whole course together into one larger program rather than isolated exercises. The capstone is your chance to apply object design, the Enumerable and Comparable mixins, well-structured method arguments, and the standard-library tools — Rake, OptionParser, Benchmark — in a project with several moving parts. Treat this checkpoint as a dress rehearsal: the concepts you confirm here are exactly the ones you'll lean on to plan and build the capstone confidently.