I have been going through the Cldr docs and I don’t see a clear way to do this.
How can I configure Cldr so I can overwrite the to_string function for certain things?
In particular I want to write the formatter so money:
<%= @money %>
is displayed as "$20" if the amount is {:USD, 20} and displayed as "$20.10" if the amount is {:USD, "20.10"}.
I know I can use Money.to_string with formatting options but I want to keep it simple for other devs and have custom Cldr formatter.
Oh, interesting. I’ll look at adding that as an implementation on the next version of ex_money. I’m only just now getting back to web dev and hadn’t realised there is both the Html.Safe and Phoenix.HTML.Safe protocols (and maybe there isn’t a difference any more!)
D’Oh, should have read my own code first. This is the implementation:
defimpl Phoenix.HTML.Safe, for: Money do
def to_iodata(money) do
Phoenix.HTML.Safe.to_iodata(Money.to_string!(money))
end
end
Does it not do what you expect?
You can actually store formatting options in a Money.t if you need - that capability was primarily added to support this protocol. Of course if thats not enough you can certainly do your own implementation as you showed.
Example
iex> m = Money.new(:USD, 1234, format: :long)
#Money<:USD, 1234>
iex> Money.to_string m
{:ok, "1,234 US dollars"}
For illumination, here is exactly the code I ended up writing:
defimpl Phoenix.HTML.Safe, for: Money do
def to_iodata(%Money{amount: %Decimal{coef: coef, exp: 0}} = money) when is_integer(coef) do
money
|> Money.to_string!(fractional_digits: 0)
|> HTML.Safe.to_iodata()
end
def to_iodata(money) do
money
|> Money.to_string!()
|> HTML.Safe.to_iodata()
end
end
So you can see there are 2 functions.
The 2nd function is the “fallback” function and is identical to the existing Money source code.
The 1st function pattern-matches for “integer” money amounts.
The result of this is displaying money as “$20.34” when it is not an integer amount and as “$20” when it is an integer amount (there is no ".00" at the end).
UPDATE: this code actually breaks my deployments. I get an error from mix release:
** (Mix) Duplicated modules:
'Elixir.Phoenix.HTML.Safe.Money' specified in my_module and ex_money
I even get this error with elixirc_options: [ignore_module_conflict: true]] in my project in mix.exs.
As of commit 8c14f4f there is a compile-time option to configure whether or not the default protocol implementation is defined. The changelog entry is:
Add :define_phoenix_html_safe as a compile-time configuration option. The default is true. If set to a falsy value then the default protocol implementation will not be generated and users can define their own implementation.
Basically, in config.exs and friends:
config :ex_money,
define_phoenix_html_safe: false
The commit is on a development branch that will be published Lunar New Year’s Day (next Tuesday) and therefore can’t really be used easily as a GitHub dependency.
If the need becomes urgent I can do a backport to the current main branch and publish a release, but I’d prefer to wait until Tuesday if thats possible. Let me know?
It’s really cool that you decided to make this possible.
I wonder if maybe there is a more extensible approach?
What comes to mind is a compile-time configuration option :exclude_protocol_implementations with default []. If set to, e.g. [Gringotts.Money, Inspect, Jason.Encoder] then these 3 protocol implementations would not be defined.
For my case, I would then only need exclude_protocol_implementations: [Phoenix.HTML.Safe].
I think this approach will be easier for future customizations. Also the name exclude_protocol_implementations provides immediate insight into what is happening, even which file in the source code to check.
I left some comments on the commit, to illustrate what I’m thinking.
Thanks again!
P.S. This is not urgent at all. We are using a show_money view helper until something more elegant is possible.
I have pushed commit 72f78b4 to ex_money master branch implementing your suggestion. The changelog entry reads:
Enhancements
Adds configuration option :exclude_protocol_implementations to omit generating implementations for one or more protocols from the list Json.Encoder, Phoneix.HTML.Safe and Gringotts.Money. Thanks to @jgough-playoxygen for the suggestion.
If you have a chance to take it for a spin from GitHub that would be great. Pending your feedback I’ll then publish to hex.