Recently, I dove into some pure Ruby just for fun to challenge myself and understand the language at a deeper level. I wasn’t solving any real-world problem; I was simply curious about how I might calculate the largest prime factor of a number without relying on any built-in gems like Prime
. This hands-on approach turned out to be really insightful, especially because I hit a frustrating yet enlightening error:nil can't be coerced into Integer
This blog walks through that experience where I went wrong, what I learned, how I fixed it, and how I built in even more functionality to make the code cleaner and more educational.
My Code
LargestPrimeFactor
attr_accessor :factors_list, :prime_factors, :idx, :below_n, :half_num
def initialize(below_n)
@below_n = below_n
@half_num = (below_n / 2).floor
@idx = 2
@factors_list = []
@prime_factors = []
end
def prime_checker
sorted_list = factors_list.sort
sorted_list.combination(2) do |el, others|
if !prime_factors.include?(el) && others % el == 0
prime_factors << el
end
end
puts "Prime checker returned: #{prime_factors}"
end
def factors
congruent = (below_n % idx == 0)
not_listed = !factors_list.include?(idx)
number_candidate = idx
if congruent && not_listed && number_candidate
factors_list << number_candidate
puts "#{idx}"
puts "#{below_n}"
puts "why nil? #{below_n.divmod(idx)[0]}"
tmp = below_n.divmod(idx)[0]
self.idx = tmp # 👈 This was supposed to update the loop
puts "idx now: #{idx}"
elsif factors_list.length > 0 && (factors_list[-1] - idx < 0)
primes = prime_checker
puts "Prime factors: #{primes}"
puts "COMPLETE"
else
self.idx += 1
end
end
def find_primes
(1..half_num).each do |_|
factors
end
end
end
p = LargestPrimeFactor.new(10)
p.find_primes
The Error: nil can't be coerced into Integer
When I ran this code, here’s what happened:
10
why nil? 5
idx now: 5
Traceback (most recent call last):
...
largest_prime_factor.rb:46:in `-': nil can't be coerced into Integer (TypeError)
Explanation of the Problem
The error pointed to this line:
factors_list.length > 0 && (factors_list[-1] - idx < 0)
It was clear from the message: idx
was nil
at the time of subtraction.
But how did idx
, which started as 2
, end up as nil
?
The issue was this line:
tmp = below_n.divmod(idx)[0]
idx = tmp
What I mistakenly did was assign tmp
to a new local variable called idx
, which only existed within the scope of the factors
method. I never updated the actual instance variable @idx
, so it eventually became stale, and the logic that relied on it failed.
The Fix
All I needed to do was explicitly refer to the instance variable using self
:
self.idx = tmp
This ensures Ruby knows I’m setting the instance attribute, not just declaring a local variable that dies once the method ends.
Update Code with Practice Improvement
I didn’t stop at just fixing the bug. I also made a few enhancements:
- Added a reliable
is_prime?
method - Removed fragile sorting logic from
prime_checker
- Introduced a method to return the largest prime factor cleanly
class LargestPrimeFactor
attr_accessor :factors_list, :prime_factors, :idx, :below_n, :half_num
def initialize(below_n)
@below_n = below_n
@half_num = (below_n / 2).floor
@idx = 2
@factors_list = []
@prime_factors = []
end
def is_prime?(num)
return false if num <= 1
(2..Math.sqrt(num)).each do |i|
return false if num % i == 0
end
true
end
def factors
if below_n % idx == 0 && !factors_list.include?(idx)
factors_list << idx
self.prime_factors << idx if is_prime?(idx)
end
self.idx += 1
end
def find_primes
while idx <= half_num
factors
end
end
def largest_prime
find_primes
prime_factors.max
end
end
# 🔁 Usage
p = LargestPrimeFactor.new(10)
p.find_primes
puts "Factors of 10: #{p.factors_list}"
puts "Prime Factors: #{p.prime_factors}"
puts "Largest Prime Factor: #{p.largest_prime}"
Run Compile
Factors of 10: [2, 5]
Prime Factors: [2, 5]
Largest Prime Factor: 5
Practice Feature You Can Add
Feature | Description |
---|---|
all_factors | Return all integer factors (not just primes) |
is_prime? reuse | You can extract this method and reuse it across projects |
largest_prime method | Easy access to the result with minimal effort |
Error-proofing patterns | Avoid variable shadowing with self. when necessary |
Final Thoughts
This little journey reminded me of something crucial when coding in Ruby: variable scope matters more than you think. It’s very easy to assume you’re updating a value, when you’re actually creating a temporary variable that disappears after a method call.
For anyone learning Ruby, especially without frameworks like Rails to abstract things away, I’d highly recommend small exercises like this. They sharpen your mental model of how Ruby works under the hood and every bug, like this nil
coercion issue, becomes a learning opportunity.