Skip to content

Instantly share code, notes, and snippets.

@jubishop
Created September 16, 2015 02:59
Show Gist options
  • Save jubishop/9d63b4544a0784cf3ba0 to your computer and use it in GitHub Desktop.
Save jubishop/9d63b4544a0784cf3ba0 to your computer and use it in GitHub Desktop.
Dollar class for dealing with dollars and cents without damnable floating points.
def Dollar(val)
Dollar.createRoundedDollar(val.kind_of?(Dollar) ? val.raw : val)
end
def getDollar
input = gets.chomp
return nil if (input.empty?)
Dollar.createRoundedDollar(input)
end
class Dollar
include Comparable
# By default no dollar amount can be instantiated with a rounding error
# This lets you enable rounding behaviors
DO_NOT_ROUND = 1
ROUND = 2
ROUND_FLOOR = 3
ROUND_CEIL = 4
@@roundingBehavior = DO_NOT_ROUND
def self.setRoundingBehavior(behavior)
raise 'Rounding behavior must be one of DO_NOT_ROUND, ROUND, ROUND_FLOOR, or ROUND_CEIL' unless
[DO_NOT_ROUND, ROUND, ROUND_FLOOR, ROUND_CEIL].include?(behavior)
@@roundingBehavior = behavior
end
def self.createRoundedDollar(init_raw)
init_raw = Rational(init_raw)
case @@roundingBehavior
when DO_NOT_ROUND
self.new(init_raw)
when ROUND
self.newRoundPenny(init_raw)
when ROUND_FLOOR
self.newFloorPenny(init_raw)
when ROUND_CEIL
self.newCeilPenny(init_raw)
else
raise "@@roundingBehavior is #{@@roundingBehavior} ??!"
end
end
# Actual rational value representing dollar amount
attr_accessor :raw
# Initializers for rounding to the nearest penny
def self.newRoundPenny(init_raw)
self.new(init_raw.rationalize.round(2))
end
def self.newFloorPenny(init_raw)
self.new((init_raw * 100).floor.rationalize / 100)
end
def self.newCeilPenny(init_raw)
self.new((init_raw * 100).ceil.rationalize / 100)
end
# Object Overrides
def ===(other)
raw === other.rationalize
end
def ==(other)
raw == other.rationalize
end
def to_s
"$#{sprintf('%.2f', raw)}"
end
def inspect
to_s
end
def rationalize
raw
end
# Numeric Overrides
def %(other)
self.class.new(raw % other.rationalize)
end
def +(other)
self.class.new(raw + other.rationalize)
end
def -(other)
self.class.new(raw - other.rationalize)
end
def <=>(other) # Comparable mixin
raw <=> other.rationalize
end
def abs
self.class.new(raw.abs)
end
def abs2
self.class.new(raw.abs2)
end
def ceil
self.class.new(raw.ceil)
end
def floor
self.class.new(raw.floor)
end
def round
self.class.new(raw.round)
end
def div(other)
self.class.new(raw.div(other.rationalize))
end
def divmod(other)
raw.divmod(other.rationalize)
end
def remainder(other)
raw.remainder(other.rationalize)
end
def nonzero?
raw.nonzero?
end
def zero?
raw.zero?
end
# Rational Overrides
def *(other)
self.class.createRoundedDollar(raw * other.rationalize)
end
def /(other)
self.class.createRoundedDollar(raw / other.rationalize)
end
def **(other)
self.class.createRoundedDollar(raw ** other.rationalize)
end
# Dollar Specific
def whole_dollar?
raw.round == raw
end
def has_change?
raw.round != raw
end
private
# Demands new value have 2 decimal points for pennies at most
def initialize(init_raw)
self.raw = Rational(init_raw)
raise "#{raw} has more than 2 digits for cents" unless (raw.round(2) == raw)
freeze
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment