https://github.com/RubyMoney/money
Tip revision: faec0fbb7cb10646453c5ef6644289d83615b4fe authored by Shane Emmons on 06 March 2024, 12:57:40 UTC
feat: Add release notes for version 6.19.0
feat: Add release notes for version 6.19.0
Tip revision: faec0fb
currency_spec.rb
# encoding: utf-8
describe Money::Currency do
FOO = '{ "priority": 1, "iso_code": "FOO", "iso_numeric": "840", "name": "United States Dollar", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 1000, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": ",", "smallest_denomination": 1 }'
def register_foo(opts={})
foo_attrs = JSON.parse(FOO, symbolize_names: true)
# Pass an array of attribute names to 'skip' to remove them from the 'FOO'
# json before registering foo as a currency.
Array(opts[:skip]).each { |attr| foo_attrs.delete(attr) }
described_class.register(foo_attrs)
end
def unregister_foo
described_class.unregister(JSON.parse(FOO, symbolize_names: true))
end
describe "UnknownCurrency" do
it "is a subclass of ArgumentError" do
expect(described_class::UnknownCurrency < ArgumentError).to be true
end
end
describe ".find" do
before { register_foo }
after { unregister_foo }
it "returns currency matching given id" do
expected = described_class.new(:foo)
expect(described_class.find(:foo)).to be expected
expect(described_class.find(:FOO)).to be expected
expect(described_class.find("foo")).to be expected
expect(described_class.find("FOO")).to be expected
end
it "returns nil unless currency matching given id" do
expect(described_class.find("ZZZ")).to be_nil
end
end
describe ".find_by_iso_numeric" do
it "returns currency matching given numeric code" do
expect(described_class.find_by_iso_numeric(978)).to eq described_class.new(:eur)
expect(described_class.find_by_iso_numeric(208)).not_to eq described_class.new(:eur)
expect(described_class.find_by_iso_numeric('840')).to eq described_class.new(:usd)
expect(described_class.find_by_iso_numeric(51)).to eq described_class.new(:amd)
class Mock
def to_s
'208'
end
end
expect(described_class.find_by_iso_numeric(Mock.new)).to eq described_class.new(:dkk)
expect(described_class.find_by_iso_numeric(Mock.new)).not_to eq described_class.new(:usd)
end
it "returns nil if no currency has the given numeric code" do
expect(described_class.find_by_iso_numeric('non iso 4217 numeric code')).to be_nil
expect(described_class.find_by_iso_numeric(0)).to be_nil
end
it "returns nil when given empty input" do
expect(described_class.find_by_iso_numeric('')).to be_nil
expect(described_class.find_by_iso_numeric(nil)).to be_nil
end
end
describe ".wrap" do
it "returns nil if object is nil" do
expect(described_class.wrap(nil)).to be_nil
expect(described_class.wrap(described_class.new(:usd))).to eq described_class.new(:usd)
expect(described_class.wrap(:usd)).to eq described_class.new(:usd)
end
end
describe ".all" do
it "returns an array of currencies" do
expect(described_class.all).to include described_class.new(:usd)
end
it "includes registered currencies" do
register_foo
expect(described_class.all).to include described_class.new(:foo)
unregister_foo
end
it 'is sorted by priority' do
expect(described_class.all.first.priority).to eq 1
end
it "raises a MissingAttributeError if any currency has no priority" do
register_foo(skip: :priority)
expect{described_class.all}.to \
raise_error(described_class::MissingAttributeError, /foo.*priority/)
unregister_foo
end
end
describe ".register" do
after { described_class.unregister(iso_code: "XXX") if described_class.find("XXX") }
it "registers a new currency" do
described_class.register(
iso_code: "XXX",
name: "Golden Doubloon",
symbol: "%",
subunit_to_unit: 100
)
new_currency = described_class.find("XXX")
expect(new_currency).not_to be_nil
expect(new_currency.name).to eq "Golden Doubloon"
expect(new_currency.symbol).to eq "%"
end
specify ":iso_code must be present" do
expect {
described_class.register(name: "New Currency")
}.to raise_error(KeyError)
end
end
describe ".inherit" do
after do
described_class.unregister(iso_code: "XXX") if described_class.find("XXX")
described_class.unregister(iso_code: "YYY") if described_class.find("YYY")
end
it "inherit a new currency" do
described_class.register(
iso_code: "XXX",
name: "Golden Doubloon",
symbol: "%",
subunit_to_unit: 100
)
described_class.inherit("XXX",
iso_code: "YYY",
symbol: "@"
)
new_currency = described_class.find("YYY")
expect(new_currency).not_to be_nil
expect(new_currency.name).to eq "Golden Doubloon"
expect(new_currency.symbol).to eq "@"
expect(new_currency.subunit_to_unit).to eq 100
end
end
describe ".unregister" do
it "unregisters a currency" do
described_class.register(iso_code: "XXX")
expect(described_class.find("XXX")).not_to be_nil # Sanity check
described_class.unregister(iso_code: "XXX")
expect(described_class.find("XXX")).to be_nil
end
it "returns true iff the currency existed" do
described_class.register(iso_code: "XXX")
expect(described_class.unregister(iso_code: "XXX")).to be_truthy
expect(described_class.unregister(iso_code: "XXX")).to be_falsey
end
it "can be passed an ISO code" do
described_class.register(iso_code: "XXX")
described_class.register(iso_code: "YYZ")
# Test with string:
described_class.unregister("XXX")
expect(described_class.find("XXX")).to be_nil
# Test with symbol:
described_class.unregister(:yyz)
expect(described_class.find(:yyz)).to be_nil
end
end
describe ".each" do
it "yields each currency to the block" do
expect(described_class).to respond_to(:each)
currencies = []
described_class.each do |currency|
currencies.push(currency)
end
# Don't bother testing every single currency
expect(currencies[0]).to eq described_class.all[0]
expect(currencies[1]).to eq described_class.all[1]
expect(currencies[-1]).to eq described_class.all[-1]
end
end
it "implements Enumerable" do
expect(described_class).to respond_to(:all?)
expect(described_class).to respond_to(:each_with_index)
expect(described_class).to respond_to(:map)
expect(described_class).to respond_to(:select)
expect(described_class).to respond_to(:reject)
end
describe "#initialize" do
before { described_class._instances.clear }
it "lookups data from loaded config" do
currency = described_class.new("USD")
expect(currency.id).to eq :usd
expect(currency.priority).to eq 1
expect(currency.iso_code).to eq "USD"
expect(currency.iso_numeric).to eq "840"
expect(currency.name).to eq "United States Dollar"
expect(currency.decimal_mark).to eq "."
expect(currency.separator).to eq "."
expect(currency.thousands_separator).to eq ","
expect(currency.delimiter).to eq ","
expect(currency.smallest_denomination).to eq 1
end
it 'caches instances' do
currency = described_class.new("USD")
expect(described_class._instances.length).to eq 1
expect(described_class._instances["usd"].object_id).to eq currency.object_id
end
it "raises UnknownCurrency with unknown currency" do
expect { described_class.new("xxx") }.to raise_error(described_class::UnknownCurrency, /xxx/)
end
it 'returns old object for the same :key' do
expect(described_class.new("USD")).to be(described_class.new("USD"))
expect(described_class.new("USD")).to be(described_class.new(:usd))
expect(described_class.new("USD")).to be(described_class.new(:USD))
expect(described_class.new("USD")).to be(described_class.new('usd'))
expect(described_class.new("USD")).to be(described_class.new('Usd'))
end
it 'returns new object for the different :key' do
expect(described_class.new("USD")).to_not be(described_class.new("EUR"))
end
it 'is thread safe' do
ids = []
2.times.map{ Thread.new{ ids << described_class.new("USD").object_id }}.each(&:join)
expect(ids.uniq.length).to eq(1)
end
end
describe "#<=>" do
it "compares objects by priority" do
expect(described_class.new(:cad)).to be > described_class.new(:usd)
expect(described_class.new(:usd)).to be < described_class.new(:eur)
end
it "compares by id when priority is the same" do
described_class.register(iso_code: "ABD", priority: 15)
described_class.register(iso_code: "ABC", priority: 15)
described_class.register(iso_code: "ABE", priority: 15)
abd = described_class.find("ABD")
abc = described_class.find("ABC")
abe = described_class.find("ABE")
expect(abd).to be > abc
expect(abe).to be > abd
described_class.unregister("ABD")
described_class.unregister("ABC")
described_class.unregister("ABE")
end
context "when one of the currencies has no 'priority' set" do
it "compares by id" do
described_class.register(iso_code: "ABD") # No priority
abd = described_class.find(:abd)
usd = described_class.find(:usd)
expect(abd).to be < usd
described_class.unregister(iso_code: "ABD")
end
end
end
describe "#==" do
it "returns true if self === other" do
currency = described_class.new(:eur)
expect(currency).to eq currency
end
it "returns true if the id is equal ignorning case" do
expect(described_class.new(:eur)).to eq described_class.new(:eur)
expect(described_class.new(:eur)).to eq described_class.new(:EUR)
expect(described_class.new(:eur)).not_to eq described_class.new(:usd)
end
it "allows direct comparison of currencies and symbols/strings" do
expect(described_class.new(:eur)).to eq 'eur'
expect(described_class.new(:eur)).to eq 'EUR'
expect(described_class.new(:eur)).to eq :eur
expect(described_class.new(:eur)).to eq :EUR
expect(described_class.new(:eur)).not_to eq 'usd'
end
it "allows comparison with nil and returns false" do
expect(described_class.new(:eur)).not_to be_nil
end
end
describe "#eql?" do
it "returns true if #== returns true" do
expect(described_class.new(:eur).eql?(described_class.new(:eur))).to be true
expect(described_class.new(:eur).eql?(described_class.new(:usd))).to be false
end
end
describe "#hash" do
it "returns the same value for equal objects" do
expect(described_class.new(:eur).hash).to eq described_class.new(:eur).hash
expect(described_class.new(:eur).hash).not_to eq described_class.new(:usd).hash
end
it "can be used to return the intersection of Currency object arrays" do
intersection = [described_class.new(:eur), described_class.new(:usd)] & [described_class.new(:eur)]
expect(intersection).to eq [described_class.new(:eur)]
end
end
describe "#inspect" do
it "works as documented" do
expect(described_class.new(:usd).inspect).to eq %Q{#<Money::Currency id: usd, priority: 1, symbol_first: true, thousands_separator: ,, html_entity: $, decimal_mark: ., name: United States Dollar, symbol: $, subunit_to_unit: 100, exponent: 2, iso_code: USD, iso_numeric: 840, subunit: Cent, smallest_denomination: 1, format: >}
end
it "works as documented" do
expect(described_class.new(:aed).inspect).to eq %Q{#<Money::Currency id: aed, priority: 100, symbol_first: false, thousands_separator: ,, html_entity: , decimal_mark: ., name: United Arab Emirates Dirham, symbol: د.إ, subunit_to_unit: 100, exponent: 2, iso_code: AED, iso_numeric: 784, subunit: Fils, smallest_denomination: 25, format: %n %u>}
end
end
describe "#iso?" do
it "returns true for iso currency" do
expect(described_class.new(:eur).iso?).to be true
end
it "returns false if the currency is not iso" do
expect(described_class.new(:btc).iso?).to be false
end
end
describe "#to_s" do
it "works as documented" do
expect(described_class.new(:usd).to_s).to eq("USD")
expect(described_class.new(:eur).to_s).to eq("EUR")
end
end
describe "#to_str" do
it "works as documented" do
expect(described_class.new(:usd).to_str).to eq("USD")
expect(described_class.new(:eur).to_str).to eq("EUR")
end
end
describe "#to_sym" do
it "works as documented" do
expect(described_class.new(:usd).to_sym).to eq(:USD)
expect(described_class.new(:eur).to_sym).to eq(:EUR)
end
end
describe "#to_currency" do
it "works as documented" do
usd = described_class.new(:usd)
expect(usd.to_currency).to eq usd
end
it "doesn't create new symbols indefinitely" do
expect { described_class.new("bogus") }.to raise_exception(described_class::UnknownCurrency)
expect(Symbol.all_symbols.map{|s| s.to_s}).not_to include("bogus")
end
end
describe "#code" do
it "works as documented" do
expect(described_class.new(:usd).code).to eq "$"
expect(described_class.new(:azn).code).to eq "\u20BC"
end
end
describe "#exponent" do
it "conforms to iso 4217" do
expect(described_class.new(:jpy).exponent).to eq 0
expect(described_class.new(:usd).exponent).to eq 2
expect(described_class.new(:iqd).exponent).to eq 3
end
end
describe "#decimal_places" do
it "proper places for known currency" do
expect(described_class.new(:mru).decimal_places).to eq 1
expect(described_class.new(:usd).decimal_places).to eq 2
end
it "proper places for custom currency" do
register_foo
expect(described_class.new(:foo).decimal_places).to eq 3
unregister_foo
end
end
describe '#reset!' do
let(:modified_mark) { '&&' }
before do
cad = described_class.find(:cad)
described_class.register(
:priority => 100,
:iso_code => cad.iso_code,
:name => cad.name,
:subunit => cad.subunit,
:subunit_to_unit => cad.subunit_to_unit,
:thousands_separator => cad.thousands_separator,
:decimal_mark => modified_mark
)
end
it "resets modified currency" do
expect(described_class.find(:cad).decimal_mark).to eq modified_mark
described_class.reset!
expect(described_class.find(:cad).decimal_mark).not_to eq modified_mark
end
end
end