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

Summing Moneys #282

Open
olivier-thatch opened this issue Feb 13, 2024 · 2 comments
Open

Summing Moneys #282

olivier-thatch opened this issue Feb 13, 2024 · 2 comments

Comments

@olivier-thatch
Copy link

We ran into an interesting bug today. We had code that looked like this:

foos.sum(&:amount) == expected_total

where foos is a collection that has an amount property that returns a Money, and expected_total is also a Money.

This works fine, except when foos is empty. In that case, the default value 0 (an Integer) is used, and as it turns out:

0 == Money.new(0, "USD") # => false

The fix is easy enough, just provide an explicit initial value:

foos.sum(Money.new(0, "USD"), &:amount) == expected_total

but this still seems like a very easy way for developers to shoot themselves in the foot when dealing with Money. I was wondering if Shopify (or others) have dealt with this issue before and have any recommendations for avoiding this issue.

One thought I had was a Rubocop rule that requires an explicit initial value for all #sum calls, but that seems overkill and annoying since it works fine when dealing with non-Money numerics.

@elfassy
Copy link
Contributor

elfassy commented Apr 9, 2024

Interesting. I don't think we should have 0 == Money.new(0, "USD") # => true. This could lead to other wrong assumptions. The only thing I can think of (other than the rubocop rule you mentioned) is creating a helper to do sums

# Before
foos.sum(Money.new(0, "USD"), &:amount)

# After
Money.sum(foos, &:amount)

Devs would still know to use it instead of the regular sum, so would likely also require a rubocop rule to enforce. Open to other suggestions

@olivier-thatch
Copy link
Author

Unfortunately I don't think this could be enforced via Rubocop, since Rubocop cannot statically know that foos.sum(&:amount) returns a Money.

There's probably no great solution here... Maybe Money#==(other) could emit a warning when other is a Numeric? This would keep the current behavior but make the issue a bit more obvious.

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

No branches or pull requests

2 participants