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
- Custom iterators, Enumerable & lazy Enumerators
- Method arguments: keyword, splat, block, forwarding
- The Comparable module and sorting
- Refinements: scoped, safer monkey-patching
- Fibers, threads, Mutex, and Ractor
- Rake, OptionParser, and benchmarking
📚 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.
| Concept | Key idea |
|---|---|
| Custom iterators | define each + include Enumerable for map/select free; .lazy for infinite streams |
| Method arguments | positional, keyword, *args, **kwargs, &block, and (...) forwarding |
| Comparable | define <=> to get all operators, between?, clamp, and sortability |
| Refinements | refine + using for scoped, opt-in changes — safer than open classes |
| Fibers | Fiber.yield/resume for cooperative pausing and lazy generators |
| Threads & Ractor | Thread.new/join, Mutex for shared state, Ractor for true parallelism |
| Rake | tasks with dependencies, namespaces, desc, and rake -T |
| OptionParser | parse ARGV flags into an options hash, with types and exit codes |
| Benchmarking | Benchmark.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!
- ✅ Reviewed iterators, method args, Comparable, and refinements
- ✅ Reviewed fibers, threads/Ractor, Rake, OptionParser, and benchmarking
- ✅ Built a sortable class (Comparable) with a lazy Enumerator
- ✅ Checked the full solution and the checkpoint quiz
- ✅ Ready to combine it all in a larger program
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
- Previous: Benchmarking & Profiling
- Next: Capstone: A CLI App — Combine everything to build a real command-line application
- Quick reference: Ruby cheat sheet
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.