Skip to content

Commit

Permalink
Merge pull request RubyMoney#495 from epidemian/from-amount
Browse files Browse the repository at this point in the history
Add Money.from_amount
  • Loading branch information
semmons99 committed Feb 7, 2015
2 parents 3479b96 + c1fdab7 commit 20edf99
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
`.ca_dollar`, and `.euro`.
- Add helper methods for British pounds: `Money.pound_sterling` and
`Money.gbp`.
- Add `Money.from_amount` to create money from a value in units instead of
fractional units.

## 6.5.1
- Fix format for BYR currency
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ Money.new(1000, "USD") - Money.new(200, "USD") == Money.new(800, "USD")
Money.new(1000, "USD") / 5 == Money.new(200, "USD")
Money.new(1000, "USD") * 5 == Money.new(5000, "USD")

# Unit to subunit conversions
Money.from_amount(5, "USD") == Money.new(500, "USD") # 5 USD
Money.from_amount(5, "JPY") == Money.new(5, "JPY") # 5 JPY
Money.from_amount(5, "TND") == Money.new(5000, "TND") # 5 TND

# Currency conversions
some_code_to_setup_exchange_rates
Money.new(1000, "USD").exchange_to("EUR") == Money.new(some_value, "EUR")
Expand Down
24 changes: 23 additions & 1 deletion lib/money/money.rb
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,35 @@ def self.disallow_currency_conversion!
self.default_bank = Bank::SingleCurrency.instance
end

# Creates a new Money object of value given in the +unit+ of the given
# +currency+.
#
# @param [Numeric] amount The numerical value of the money.
# @param [Currency, String, Symbol] currency The currency format.
# @param [Money::Bank::*] bank The exchange bank to use.
#
# @example
# Money.from_amount(23.45, "USD") # => #<Money fractional:2345 currency:USD>
# Money.from_amount(23.45, "JPY") # => #<Money fractional:23 currency:JPY>
#
# @return [Money]
#
# @see #initialize
def self.from_amount(amount, currency = default_currency, bank = default_bank)
Numeric === amount or raise ArgumentError, "'amount' must be numeric"
currency = Currency.wrap(currency)
value = BigDecimal.new(amount.to_s) * currency.subunit_to_unit
value = value.round unless infinite_precision
new(value, currency, bank)
end

# Creates a new Money object of value given in the
# +fractional unit+ of the given +currency+.
#
# Alternatively you can use the convenience
# methods like {Money.ca_dollar} and {Money.us_dollar}.
#
# @param [Object] obj Either The fractional value of the money,
# @param [Object] obj Either the fractional value of the money,
# a Money object, or a currency. (If passed a currency as the first
# argument, a Money will be created in that currency with fractional value
# = 0.
Expand Down
56 changes: 56 additions & 0 deletions spec/money_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,62 @@
end
end

describe ".from_amount" do
it "accepts numeric values" do
expect(Money.from_amount(1, "USD")).to eq Money.new(1_00, "USD")
expect(Money.from_amount(1.0, "USD")).to eq Money.new(1_00, "USD")
expect(Money.from_amount("1".to_d, "USD")).to eq Money.new(1_00, "USD")
end

it "raises ArgumentError with unsupported argument" do
expect { Money.from_amount("1") }.to raise_error(ArgumentError)
expect { Money.from_amount(Object.new) }.to raise_error(ArgumentError)
end

it "converts given amount to subunits according to currency" do
expect(Money.from_amount(1, "USD")).to eq Money.new(1_00, "USD")
expect(Money.from_amount(1, "TND")).to eq Money.new(1_000, "TND")
expect(Money.from_amount(1, "JPY")).to eq Money.new(1, "JPY")
end

it "rounds the given amount to subunits" do
expect(Money.from_amount(4.444, "USD").amount).to eq "4.44".to_d
expect(Money.from_amount(5.555, "USD").amount).to eq "5.56".to_d
expect(Money.from_amount(444.4, "JPY").amount).to eq "444".to_d
expect(Money.from_amount(555.5, "JPY").amount).to eq "556".to_d
end

it "accepts an optional currency" do
expect(Money.from_amount(1).currency).to eq Money.default_currency
jpy = Money::Currency.wrap("JPY")
expect(Money.from_amount(1, jpy).currency).to eq jpy
expect(Money.from_amount(1, "JPY").currency).to eq jpy
end

it "accepts an optional bank" do
expect(Money.from_amount(1).bank).to eq Money.default_bank
bank = double "bank"
expect(Money.from_amount(1, "USD", bank).bank).to eq bank
end

context "infinite_precision = true" do
before do
Money.infinite_precision = true
end

after do
Money.infinite_precision = false
end

it "does not round the given amount to subunits" do
expect(Money.from_amount(4.444, "USD").amount).to eq "4.444".to_d
expect(Money.from_amount(5.555, "USD").amount).to eq "5.555".to_d
expect(Money.from_amount(444.4, "JPY").amount).to eq "444.4".to_d
expect(Money.from_amount(555.5, "JPY").amount).to eq "555.5".to_d
end
end
end

%w[cents pence].each do |units|
describe "##{units}" do
it "is a synonym of #fractional" do
Expand Down

0 comments on commit 20edf99

Please sign in to comment.