Created
September 16, 2015 02:59
-
-
Save jubishop/9d63b4544a0784cf3ba0 to your computer and use it in GitHub Desktop.
Dollar class for dealing with dollars and cents without damnable floating points.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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