Class: Money
- Inherits:
-
Object
- Object
- Money
- Defined in:
- lib/simple_money/money.rb
Overview
Used to work with financial calculations. Tries to avoid the pitfalls of using Float by storing the value in cents. All calculations are floored.
Constant Summary collapse
- VALID_AS_VALUES =
The valid values for as
[:cents, :decimal]
- VALID_ROUNDING_METHOD_VALUES =
The valid rounding methods
[:away_from_zero, :toward_zero, :nearest_up, :nearest_down, :bankers, :up, :down]
- ROUNDING_METHOD_TRANSLATION =
Translations from SimpleMoney rounding methods to BigDecimal rounding methods.
{ :away_from_zero => BigDecimal::ROUND_UP, :toward_zero => BigDecimal::ROUND_DOWN, :nearest_up => BigDecimal::ROUND_HALF_UP, :nearest_down => BigDecimal::ROUND_HALF_DOWN, :bankers => BigDecimal::ROUND_HALF_EVEN, :up => BigDecimal::ROUND_CEILING, :down => BigDecimal::ROUND_FLOOR, }
Class Attribute Summary collapse
-
.default_as ⇒ Symbol
The default as used to create a new Money (defaults to :cents).
-
.default_currency ⇒ Object
The default currency used to create a new Money object (default to :usd).
-
.default_rounding_method ⇒ Symbol
The default rounding method used when calculations do not result in an Fixnum (defaults to :bankers).
-
.overflow ⇒ BigDecimal
The factional cents left over from any transactions that overflowed.
Instance Attribute Summary collapse
-
#cents ⇒ Integer
readonly
The value of the object in cents.
-
#currency ⇒ Currency::CurrencyStruct
readonly
was created using.
-
#rounding_method ⇒ Symbol
readonly
fractions of a cent.
Class Method Summary collapse
-
.reset_overflow ⇒ BigDecimal
Resets the overflow bucket to 0.
-
.round(n, rounding_method = default_rounding_method) ⇒ Fixnum
Returns n rounded to an integer using the given rounding method, or the default rounding method when none is provided.
-
.valid_as?(as) ⇒ true, false
Returns true if argument is a valid value for :as, otherwise false.
-
.valid_rounding_method?(rounding_method) ⇒ true, false
Returns true if argument is a valid rounding method, otherwise false.
Instance Method Summary collapse
-
#%(n) ⇒ Numeric, Money
Modulo self by a Money/Numeric; return the results as a new Numeric/Money.
-
#*(n) ⇒ Money
Multiply Money by a Numeric; return the results as a new Money object.
-
#+(n) ⇒ Money
Add two Money objects; return the results as a new Money object.
-
#-(n) ⇒ Money
Subtract two Money; return the results as a new Money object.
-
#/(n) ⇒ Numeric, Money
Divide self by a Money/Numeric; return the results as a new Numeric/Money.
-
#<=>(n) ⇒ Object
Compare self to n.
-
#==(n) ⇒ Object
Compare self to n.
-
#abs ⇒ Money
Return a new Money object using the absolute value of cents.
- #divmod(n) ⇒ Object
-
#initialize(n = 0, options = {}) ⇒ Money
constructor
Creates a new Money object.
-
#to_s(options = {}) ⇒ String
Returns cents formatted as a string, based on any options passed.
Constructor Details
#initialize(n = 0, options = {}) ⇒ Money
Creates a new Money object. If as is set to :cents, n will be coerced to a Fixnum. If as is set to :decimal, n will be coerced to a BigDecimal.
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
# File 'lib/simple_money/money.rb', line 234 def initialize(n = 0, = {}) = { :currency => self.class.default_currency, :rounding_method => self.class.default_rounding_method, :as => self.class.default_as }.merge() @currency = Currency[[:currency]] raise ArgumentError, "invalid `rounding_method`" unless ( self.class.valid_rounding_method? [:rounding_method] ) @rounding_method = [:rounding_method] raise ArgumentError, "invalid `as`" unless ( self.class.valid_as? [:as] ) @cents = case [:as] when :cents Money.round( BigDecimal(n.to_s), rounding_method ) when :decimal case currency.subunit_to_unit when 10, 100, 1000 Money.round( ( BigDecimal(n.to_s) * BigDecimal(currency.subunit_to_unit.to_s) ), rounding_method ) when 1 Money.round( BigDecimal(n.to_s), rounding_method ) when 5 unit = BigDecimal(n.to_s).floor * BigDecimal("5") subunit = ( (BigDecimal(n.to_s) % BigDecimal("1")) * BigDecimal("10") ) Money.round(unit + subunit) else raise Exception, "creation of Money objects with subunit_to_unit = `#{currency.subunit_to_unit}` is not implmented" end end end |
Class Attribute Details
.default_as ⇒ Symbol
Returns The default as used to create a new Money (defaults to :cents).
36 37 38 |
# File 'lib/simple_money/money.rb', line 36 def default_as @default_as end |
.default_currency ⇒ Object
The default currency used to create a new Money object (default to :usd).
113 114 115 |
# File 'lib/simple_money/money.rb', line 113 def default_currency @default_currency end |
.default_rounding_method ⇒ Symbol
Returns The default rounding method used when calculations do not result in an Fixnum (defaults to :bankers).
74 75 76 |
# File 'lib/simple_money/money.rb', line 74 def default_rounding_method @default_rounding_method end |
.overflow ⇒ BigDecimal
Returns The factional cents left over from any transactions that overflowed.
133 134 135 |
# File 'lib/simple_money/money.rb', line 133 def overflow @overflow end |
Instance Attribute Details
#cents ⇒ Integer (readonly)
Returns The value of the object in cents.
197 198 199 |
# File 'lib/simple_money/money.rb', line 197 def cents @cents end |
#currency ⇒ Currency::CurrencyStruct (readonly)
was created using.
207 208 209 |
# File 'lib/simple_money/money.rb', line 207 def currency @currency end |
#rounding_method ⇒ Symbol (readonly)
fractions of a cent.
202 203 204 |
# File 'lib/simple_money/money.rb', line 202 def rounding_method @rounding_method end |
Class Method Details
.reset_overflow ⇒ BigDecimal
Resets the overflow bucket to 0.
156 157 158 |
# File 'lib/simple_money/money.rb', line 156 def reset_overflow self.overflow = 0 end |
.round(n, rounding_method = default_rounding_method) ⇒ Fixnum
Returns n rounded to an integer using the given rounding method, or the default rounding method when none is provided. When rounding, the fractional cents are added to the overflow bucket.
175 176 177 178 179 180 181 182 183 184 185 186 187 |
# File 'lib/simple_money/money.rb', line 175 def round(n, rounding_method = default_rounding_method) raise ArgumentError, "invalid `rounding_method`" unless ( valid_rounding_method? rounding_method ) original = BigDecimal(n.to_s) rounded = original.round( 0, ROUNDING_METHOD_TRANSLATION[rounding_method] ) @overflow += original - rounded rounded.to_i end |
.valid_as?(as) ⇒ true, false
Returns true if argument is a valid value for :as, otherwise false.
67 68 69 |
# File 'lib/simple_money/money.rb', line 67 def valid_as?(as) VALID_AS_VALUES.include? as end |
.valid_rounding_method?(rounding_method) ⇒ true, false
Returns true if argument is a valid rounding method, otherwise false.
107 108 109 |
# File 'lib/simple_money/money.rb', line 107 def valid_rounding_method?(rounding_method) VALID_ROUNDING_METHOD_VALUES.include? rounding_method end |
Instance Method Details
#%(n) ⇒ Numeric, Money
Modulo self by a Money/Numeric; return the results as a new Numeric/Money.
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 |
# File 'lib/simple_money/money.rb', line 417 def %(n) case n when Money raise ArgumentError, "n is an incompatible currency" unless ( n.currency == currency ) BigDecimal(self.cents.to_s) % BigDecimal(n.cents.to_s) when Numeric Money.new( BigDecimal(self.cents.to_s) % BigDecimal(n.to_s), :as => :cents, :rounding_method => rounding_method, :currency => currency ) else raise ArgumentError, "n must be a Money or Numeric" end end |
#*(n) ⇒ Money
Multiply Money by a Numeric; return the results as a new Money object.
352 353 354 355 356 357 358 359 360 361 |
# File 'lib/simple_money/money.rb', line 352 def *(n) raise ArgumentError, "n must be a Numeric" unless n.is_a? Numeric Money.new( BigDecimal(self.cents.to_s) * BigDecimal(n.to_s), :as => :cents, :rounding_method => rounding_method, :currency => currency ) end |
#+(n) ⇒ Money
Add two Money objects; return the results as a new Money object.
297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/simple_money/money.rb', line 297 def +(n) raise ArgumentError, "n must be a Money" unless n.is_a? Money raise ArgumentError, "n is an incompatible currency" unless ( n.currency == currency ) Money.new( self.cents + n.cents, :as => :cents, :rounding_method => rounding_method, :currency => currency ) end |
#-(n) ⇒ Money
Subtract two Money; return the results as a new Money object.
325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/simple_money/money.rb', line 325 def -(n) raise ArgumentError, "n must be a Money" unless n.is_a? Money raise ArgumentError, "n is an incompatible currency" unless ( n.currency == currency ) Money.new( self.cents - n.cents, :as => :cents, :rounding_method => rounding_method, :currency => currency ) end |
#/(n) ⇒ Numeric, Money
Divide self by a Money/Numeric; return the results as a new Numeric/Money.
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
# File 'lib/simple_money/money.rb', line 379 def /(n) case n when Money raise ArgumentError, "n is an incompatible currency" unless ( n.currency == currency ) BigDecimal(self.cents.to_s) / BigDecimal(n.cents.to_s) when Numeric result, overflow = BigDecimal(self.cents.to_s).divmod(BigDecimal(n.to_s)) self.class.overflow = self.class.overflow + overflow Money.new( result, :as => :cents, :rounding_method => rounding_method, :currency => currency ) else raise ArgumentError, "n must be a Money or Numeric" end end |
#<=>(n) ⇒ Object
Compare self to n. When self < n, return -1. When self > n, return 1. When self == n, return 0.
490 491 492 493 494 495 496 497 |
# File 'lib/simple_money/money.rb', line 490 def <=>(n) raise ArgumentError, "n must be a Money" unless n.is_a? Money raise ArgumentError, "n is an incompatible currency" unless ( n.currency == currency ) self.cents <=> n.cents end |
#==(n) ⇒ Object
Compare self to n. When self == n, return true, otherwise false.
512 513 514 515 516 517 518 519 |
# File 'lib/simple_money/money.rb', line 512 def ==(n) raise ArgumentError, "n must be a Money" unless n.is_a? Money raise ArgumentError, "n is an incompatible currency" unless ( n.currency == currency ) self.cents == n.cents end |
#abs ⇒ Money
Return a new Money object using the absolute value of cents.
528 529 530 531 532 533 534 535 |
# File 'lib/simple_money/money.rb', line 528 def abs Money.new( self.cents.abs, :as => :cents, :rounding_method => rounding_method, :currency => currency ) end |
#divmod(n) ⇒ Object
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 |
# File 'lib/simple_money/money.rb', line 437 def divmod(n) case n when Money raise ArgumentError, "n is an incompatible currency" unless ( n.currency == currency ) a, b = BigDecimal(self.cents.to_s).divmod(BigDecimal(n.cents.to_s)) [ a, Money.new( b, :as => :cents, :rounding_method => rounding_method, :currency => currency ) ] when Numeric a, b = BigDecimal(self.cents.to_s).divmod(BigDecimal(n.to_s)) [ Money.new( a, :as => :cents, :rounding_method => rounding_method, :currency => currency ), Money.new( b, :as => :cents, :rounding_method => rounding_method, :currency => currency ) ] else raise ArgumentError, "n must be a Money or Numeric" end end |
#to_s(options = {}) ⇒ String
Returns cents formatted as a string, based on any options passed.
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 |
# File 'lib/simple_money/money.rb', line 552 def to_s( = {}) = { :as => :cents }.merge() raise ArgumentError, "invalid `as`" unless ( self.class.valid_as? [:as] ) case [:as] when :cents cents.to_s when :decimal if currency.subunit_to_unit == 1 return cents.to_s end unit, subunit = cents.divmod(currency.subunit_to_unit).map(&:to_s) subunit = (("0" * currency.decimal_places) + subunit) subunit = subunit[ (-1 * currency.decimal_places), currency.decimal_places ] "#{unit}.#{subunit}" end end |