In this example, does it detect the certainty of running out of stack or does it actually run out of stack?
It actually runs out of stack. It is, in fact, impossible to "detect the certainty of running out of stack" in the general case because of the halting problem, one of the core problems in computer science.
Is it possible to produce this error without using recursion?
Sure. Just define a lot of methods, each of which calls the next:
20000.times do |n|
define_method :"foo_#{n}" do
puts "Called #{__method__}"
send :"foo_#{n+1}"
end
end
foo_0
# -> Called foo_0
# -> Called foo_1
# -> Called foo_2
# -> Called foo_3
# ...
# -> Called foo_4931
# -> SystemStackError: stack level too deep