Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
100: Ractor for True Parallelism
Wait, I thought Ruby had threads. Why do I need Ractors?
Here is the short version: Ruby threads are great for waiting on a database or an API call, but they're useless for heavy math. This is because of the GVL (Global VM Lock). Even if you have a 16-core processor, the GVL ensures that only one thread is actually executing Ruby code at any given moment. You're basically just switching between tasks very quickly, not doing them at the same time.
Ractors (short for "Ruby Actors") change the game by giving each Ractor its own GVL. If you spin up four Ractors on a four-core machine, you are finally utilizing 100% of your CPU. I usually tell people to think of Threads as "concurrency" (managing many things at once) and Ractors as "parallelism" (doing many things at once).
How do I actually send work to a Ractor and get a result back?
You don't share variables with a Ractor; you communicate with it. Think of a Ractor like a separate little person in a different room. You send them a message, they do the work, and they send a message back. You use Ractor.new to create one, and ractor.take to wait for the result.
Let's say we have a massive array of numbers and we want to calculate the sum of squares. Doing this in one thread is slow. Here is how we'd split it across two Ractors:
numbers = (1..10_000_000).to_a
half = numbers.size / 2
# We split the work into two Ractors
r1 = Ractor.new(numbers[0...half]) do |slice|
slice.sum { |n| n * n }
end
r2 = Ractor.new(numbers[half..-1]) do |slice|
slice.sum { |n| n * n }
end
# .take blocks the main thread until the Ractor returns a value
total = r1.take + r2.take
puts "The total is #{total}"
Why am I getting "can't share this object" errors?
This is the part that trips everyone up. To prevent the "race conditions" that make multi-threaded programming a nightmare, Ruby strictly forbids Ractors from sharing mutable objects. If you try to access a global variable or a string from the main thread inside a Ractor, Ruby will throw a Ractor::IsolationError.
You have two choices here. First, you can pass the data in as an argument to Ractor.new (which Ruby handles by moving or copying the object). Second, if you have a large configuration hash or a lookup table that many Ractors need to read, you can make it "shareable" using Ractor.make_shareable. This effectively freezes the object so it can't be changed, making it safe for every Ractor to look at simultaneously.
# This would normally fail if accessed inside a Ractor
CONFIG = { timeout: 30, retry_limit: 3, mode: :production }
Ractor.make_shareable(CONFIG)
r = Ractor.new do
# Because we called make_shareable, this is allowed
puts "The current mode is #{CONFIG[:mode]}"
end
r.take
I'll be honest: the restriction is annoying at first, but it's a lifesaver. It forces you to design your system so that data flows in one direction, which means you won't spend three days debugging a random crash caused by two threads editing the same string at the same millisecond.
📋 Practical Task
Parallel Image Metadata Processor
You have a list of 10 simulated "large image files" (represented as large strings or arrays of bytes). Writing a processor that analyzes these one-by-one is too slow. Your task is to build a MetadataAnalyzer that utilizes Ractors to process these files in parallel.
- Create an array of 10 large strings (simulating file data).
- Create a "shareable" configuration hash that contains a
versionnumber and athresholdvalue. - Spawn 5 Ractors. Each Ractor should take a portion of the files, read the shared configuration, and return the "checksum" (e.g., the sum of the bytes/characters) of the files it processed.
- Collect the results using
.takeand print the final aggregate checksum.
Make sure you handle the object isolation correctly, or your program will crash before it even starts processing!
There are no comments for now.