loading...

Initialize Money::Currency on the test context

kazu9su profile image toooooooooomy ・2 min read

RubyMoney is a great gem to handle real-world money.
It provides the default setting, and we can overwrite it via business requirements.
On the production environment, if we set the setting up once(it could be in the initializer), no need to update it in many cases.
However, it could happen in the test context.

In this article, I explain how RubyMoney manages the currency setting and how to initialize it when we need it.

First of all, we can set the customized currency as below

cad = Money::Currency.find(:cad)

cad.name # => Canadian Dollar

Money::Currency.register(
  :priority            => cad.priority,
  :iso_code            => cad.iso_code,
  :name                => 'Something Different',
  :subunit             => cad.subunit,
  :subunit_to_unit     => cad.subunit_to_unit,
  :thousands_separator => cad.thousands_separator,
  :decimal_mark        => cad.decimal_mark
)

cad = Money::Currency.find(:cad)
cad.name # => Something Different

Money::Currency manages the currency setting as an instance variable called @table, then it should be ok to reset the instance variable.
But it provides the interface to access the table as a class method so that we should be able to reset the table like

 Money::Currency.instance_variable_set('@table', nil)
 Money::Currency.table # => reloading the initial setting

See? How easy it is! But wait, we still keep getting the overwritten setting.

cad = Money::Currency.find(:cad)
cad.name # => Something Different
Money::Currency.instance_variable_set('@table', nil)
Money::Currency.table
cad = Money::Currency.find(:cad)
cad.name # => Something Different. We still get the overwritten name!

Why does that happen?
That is because, if it is overwritten once, that setting should have consistency through a ruby process.
To handle it, the library manages the setting as a class variable, so that we do not need to care about the currency setting any more to develop the app.
Given that, we should initialize that class variable like below.

cad.name # => Something Different
Money::Currency.class_variable_set('@@instances', {})
Money::Currency.instance_variable_set('@table', nil)
Money::Currency.table
cad = Money::Currency.find(:cad)
cad.name # => Canadian Dollar

Yay! If you write it on the spec_helper or something, your tests don't depend on Money::Currency context.

The latest master of RubyMoney::Money provides API, Money::Currency.reset! to handle it!
This link is the PR of this feature.
If anyone wanna use it, pull the latest master, and there you go.
I hope the next version of RubyMoney::Money includes this update!

Discussion

pic
Editor guide