This is not a thread about best practices representing money. We all know
in general[*] you want an exact type. I have personally used arbitrary
precision when enough, rationals when I have had requirements for exact
division, and also integers.
Rather, this is a thread about the examples in the documentation of
#number_to_currency. Let's focus the discussion on this topic.
Generally speaking, examples in documentation should be *realistic* (ie,
class User < AR::Base in Rails communicates better than class C <
AR::Base), and should promote *best practices* and *idiomatic code*
implictly. Thet have to be *easily understandable* too.
This helper is in a gray area, because you need to write literals to show
how it behaves, and that is not realistic at all. Applications have their
money quantities stored somewhere.
If you use a rational literal with a suffix:
number_to_currency(1234567890.50r) # => $1,234,567,890.50
number_to_currency(1234567890.506r) # => $1,234,567,890.51
number_to_currency(1234567890.506r, precision: 3) # =>
$1,234,567,890.506
number_to_currency(1234567890.506r, locale: :fr) # => 1 234 567 890,51
€
the number of WTFs in readers is going to be greater than any positive
integer. Why is it passing a rational literal? Do I need to pass a rational?
If we use #to_d
number_to_currency("1234567890.50".to_d) # =>
$1,234,567,890.50
number_to_currency("1234567890.506".to_d) # =>
$1,234,567,890.51
number_to_currency("1234567890.506".to_d, precision: 3) # =>
$1,234,567,890.506
number_to_currency("1234567890.506".to_d, locale: :fr) # => 1 234 567
890,51 €
readers are going to be equally puzzled. That is obscuring the
understanding of the example, which is to compare calls and comments, left
and right, and see how the options alter the output. That is the point.
number_to_currency(1234567890.50) # =>
$1,234,567,890.50
number_to_currency(1234567890.506) # =>
$1,234,567,890.51
number_to_currency(1234567890.506, precision: 3) # =>
$1,234,567,890.506
number_to_currency(1234567890.506, locale: :fr) # => 1 234 567
890,51 €
That one has less noise for my taste, allows the reader to process the
examples in a snap, and serves better to the purpose of this sample code.
It does not convey a best practice, that is true, and that is where you
have to choose a trade-off. Looking at the three options, I prefer
compromising towards readability, the third one.
Then you compensate with a warning like the one I suggested before, and
done.
Xavier
[*] Let me also say that there are use cases for floats. If you have a
personal app to track your expenses, or whatever use case you have in which
you just don't care about the possibility of a missing cent here or there,
you are fine with floats.