From 9b7f4dc3d2394cb35e0a2eb81dc9163528062274 Mon Sep 17 00:00:00 2001 From: Shaisolaris <66793913+Shaisolaris@users.noreply.github.com> Date: Sun, 11 Oct 2026 22:55:09 -0700 Subject: [PATCH 1/2] Fix Currency.unregister before the currency table is loaded --- CHANGELOG.md | 1 + lib/money/currency.rb | 8 ++++++-- spec/money/currency_spec.rb | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 519ff8fabe..fef91dedf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Fix `Money::Currency.unregister` raising `NoMethodError` when called before the currency table was loaded, for example in an initializer right after `require "money"` - Fix `Money::Allocation.generate` converting a Rational with unlimited `to_d` precision. It now uses `Money.conversion_precision` - Fix `Money::Currency.reset!` keeping the cached key set, so a currency registered before the reset was still accepted by `Money::Currency.new` and raised `NoMethodError` instead of `UnknownCurrency` - Fix `Money::Bank::VariableExchange#import_rates` parsing `:json` with `JSON.load`, which instantiates any class named by a `json_class` key; it now uses `JSON.parse` diff --git a/lib/money/currency.rb b/lib/money/currency.rb index d4e96be88e..3517a20436 100644 --- a/lib/money/currency.rb +++ b/lib/money/currency.rb @@ -204,8 +204,12 @@ def unregister(curr) else curr.downcase.to_sym end - existed = @table.delete(key) - @stringified_keys = nil if existed + existed = table.delete(key) + if existed + @@mutex.synchronize { _instances.delete(key.to_s) } + @stringified_keys = nil + clear_iso_numeric_cache + end existed ? true : false end # rubocop:enable Naming/PredicateMethod diff --git a/spec/money/currency_spec.rb b/spec/money/currency_spec.rb index 1b809d7f22..b163a11386 100644 --- a/spec/money/currency_spec.rb +++ b/spec/money/currency_spec.rb @@ -136,6 +136,32 @@ def to_s end end + describe ".unregister" do + after { described_class.reset! } + + it "works before the currency table has been loaded" do + described_class.instance_variable_set(:@table, nil) + + expect(described_class.unregister(:usd)).to be true + expect(described_class.find(:usd)).to be_nil + expect(described_class.find(:eur)).to eq described_class.new(:eur) + end + + it "returns false for an unknown currency before the table has been loaded" do + described_class.instance_variable_set(:@table, nil) + + expect(described_class.unregister(:abc)).to be false + end + + it "drops the currency from the iso_numeric lookup" do + expect(described_class.find_by_iso_numeric(978)).to eq described_class.new(:eur) + + described_class.unregister(:eur) + + expect(described_class.find_by_iso_numeric(978)).to be_nil + end + end + describe ".wrap" do it "returns nil if object is nil" do expect(described_class.wrap(nil)).to be_nil From 599a2e3bed17b8705203a30ec0371f164f09ff1f Mon Sep 17 00:00:00 2001 From: Shaisolaris <66793913+Shaisolaris@users.noreply.github.com> Date: Mon, 12 Oct 2026 21:08:48 +0000 Subject: [PATCH 2/2] Move the new unregister specs into the existing describe block RSpec/RepeatedExampleGroupDescription flagged the second describe ".unregister" block. --- spec/money/currency_spec.rb | 52 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/spec/money/currency_spec.rb b/spec/money/currency_spec.rb index b163a11386..2823e6a0ef 100644 --- a/spec/money/currency_spec.rb +++ b/spec/money/currency_spec.rb @@ -136,32 +136,6 @@ def to_s end end - describe ".unregister" do - after { described_class.reset! } - - it "works before the currency table has been loaded" do - described_class.instance_variable_set(:@table, nil) - - expect(described_class.unregister(:usd)).to be true - expect(described_class.find(:usd)).to be_nil - expect(described_class.find(:eur)).to eq described_class.new(:eur) - end - - it "returns false for an unknown currency before the table has been loaded" do - described_class.instance_variable_set(:@table, nil) - - expect(described_class.unregister(:abc)).to be false - end - - it "drops the currency from the iso_numeric lookup" do - expect(described_class.find_by_iso_numeric(978)).to eq described_class.new(:eur) - - described_class.unregister(:eur) - - expect(described_class.find_by_iso_numeric(978)).to be_nil - end - end - describe ".wrap" do it "returns nil if object is nil" do expect(described_class.wrap(nil)).to be_nil @@ -267,6 +241,32 @@ def to_s described_class.unregister(:yyz) expect(described_class.find(:yyz)).to be_nil end + + context "when the currency table changes" do + after { described_class.reset! } + + it "works before the currency table has been loaded" do + described_class.instance_variable_set(:@table, nil) + + expect(described_class.unregister(:usd)).to be true + expect(described_class.find(:usd)).to be_nil + expect(described_class.find(:eur)).to eq described_class.new(:eur) + end + + it "returns false for an unknown currency before the table has been loaded" do + described_class.instance_variable_set(:@table, nil) + + expect(described_class.unregister(:abc)).to be false + end + + it "drops the currency from the iso_numeric lookup" do + expect(described_class.find_by_iso_numeric(978)).to eq described_class.new(:eur) + + described_class.unregister(:eur) + + expect(described_class.find_by_iso_numeric(978)).to be_nil + end + end end # rubocop:disable Style/MapIntoArray