A follow up on the question How to create a random time between a range .
Kernel#rand
works with Time
range:
require 'time'
rand(Time.parse('9 am')..Time.parse('11:30 am'))
But when I tried with a custom class, I ended up with the error:
`rand': no implicit conversion of Range into Integer (TypeError)
class Int
include Comparable
attr_reader :num
def initialize(num)
@num = num
end
def succ
Int.new(num + 1)
end
def <=>(other)
num <=> other.num
end
def to_s
"Int(#{num})"
end
def to_int
@num
end
alias_method :inspect, :to_s
end
puts rand(Int.new(1)..Int.new(3))
Why? What am I missing in the custom class? Can we use such a custom class in rand(Range)
?
I don't know of any documentation for what specifically Kernel#rand
expects from a Range
argument but we can get a look at what's going on by overriding respond_to?
in your class and then watching as things fall apart:
def respond_to?(m)
puts "They want us to support #{m}"
super
end
Doing that tells us that rand
wants to call the #-
and #+
methods on your Int
instances. This does make some sense given that rand(a..b)
is designed for working with integers.
So we throw in quick'n'dirty implementations of addition and subtraction:
def -(other)
self.class.new(to_int - other.to_int)
end
def +(other)
self.class.new(to_int + other.to_int)
end
and we start getting rand Int
s out of our calls to rand
.
I'm not sure where (or if) this is documented so you'll have to excuse a bit of hand waving. I normally spend some time rooting around the Ruby source code to answer this sort of question but I lack the time right now.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With