basics/floats/numeric_protocol_parens.rb
tests/_examples/basics/floats/numeric_protocol_parens.rb in the metorex repository.
# Numeric carries the protocol every number answers, written in terms of the
# methods a subclass supplies. Here that is `<=>`, `to_f`, `-@`, and `/`.
class Money < Numeric
attr_reader :cents
def initialize(cents)
@cents = cents
end
def <=>(other)
return cents <=> other.cents if other.is_a?(Money)
to_f <=> other
end
def ==(other)
other.is_a?(Money) && cents == other.cents
end
def -@
Money.new(-cents)
end
def to_f
cents / 100.0
end
def /(other)
Money.new(cents / other)
end
end
owed = Money.new(-250)
p(owed.negative?)
p(owed.positive?)
p(owed.zero?)
p(owed.abs.cents)
p(owed.nonzero?.cents)
p(Money.new(0).nonzero?.nil?)
# The rounding methods read the number through `to_f`.
p(Money.new(250).ceil)
p(Money.new(255).floor)
p(Money.new(255).round)
p(Money.new(-255).truncate)
# `div` floors the quotient `/` answers, and `divmod` pairs it with what is
# left over.
p(Money.new(500).div(2))
p(owed.integer?)
p(owed.real?)
p(owed.real.cents)
# `coerce` answers two of the same class, or two Floats when they differ.
p(Money.new(100).coerce(Money.new(200)).map { |m| m.cents })
p(Money.new(100).coerce(3))
# `eql?` is equality without conversion, so a different class is never equal.
p(Money.new(100).eql?(Money.new(100)))
p(Money.new(100).eql?(1.0))
Output:
true
false
false
250
-250
false
3
2
3
-2
2
false
true
-250
[200, 100]
[3.0, 1.0]
true
false