Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-currency support: Money + Currency class improvements #553

Merged
merged 2 commits into from
Mar 18, 2024

Conversation

zachgoll
Copy link
Collaborator

This PR is a precursor to #543, which was growing too big and tackling too many things at once.

Overview

Below are some explanations of changes and sample usage for documentation purposes.

Money lib

Adds Money, Money::Currency, and Money::Arithmetic to lib as these represent generic concepts that could apply to any app that deals with money.

  • Money packages up all the information required to represent a monetary value (amount, currency)
  • Money::Currency stores information about all global currencies and can provide a list of these through Money::Currency.all or Money::Currency.popular
  • Money::Arithmetic allows us to add, subtract, multiply, divide, and compare monetary values without any special syntax. In other words, we can do Money.new(0) == Money.new(0) or Money.new(100) > Money.new(5)

Why not money and money-rails?

There are two main reasons I decided to go with a custom implementation:

  1. The money and money-rails gems are somewhat opinionated about a project's money storage strategy. Throughout most of its API, it assumes that money is being stored in minor currency units. Our project does not do that. We store in Decimal{19,4}, which was chosen to make the data a lot more intuitive (1 less layer of indirection). Furthermore, the money-rails gem integrates at the model level (with migration helpers), which as we saw with a first attempt at using it, caused unnecessary confusion for new contributors.
  2. The overall implementation here is fairly simple, so we get the benefit of owning our implementation without a huge maintenance cost.

Monetizable Concern + Form Helper

In an attempt to keep things simple and intuitive, as an alternative to the money-rails strategy that "magically" turns fields like Account.balance into Money instances, I've created a Monetizable concern with a straightforward API:

class Account
  include Monetizable

  monetize :balance # creates `balance_money` getter method, which returns instance of Money
end

This unobtrusively adds a field called balance_money, which returns Money.new(balance, currency).

Furthermore, I've created a form helper with the following API:

<%= form_with model: @transaction do |f| %>
  <%= f.money_field :amount_money %>
  <%= f.submit %>
<% end %>

f.money_field will read amount_money and create an :amount and :currency input that is updated on submission.

The overall goal here is to give the developer flexibility when working with money and making it explicit when a field deals with money and when it doesn't.

@zachgoll zachgoll mentioned this pull request Mar 18, 2024
4 tasks
@zachgoll zachgoll merged commit fe2fa0e into main Mar 18, 2024
4 checks passed
@zachgoll zachgoll deleted the money-and-currency-class branch March 18, 2024 15:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant