controlling when currency is embedded #29

odigity opened this Issue Jul 18, 2012 · 6 comments


None yet
3 participants

odigity commented Jul 18, 2012

Based on the docs, it seems that when using mongoid, money-rails will automatically store a hash with amount and currency to represent the money object. That's great in some instances, and I will be using it that way for some things, but I also need to use it another way.

I want to implement bookkeeping accounts, where each account is denominated in a single currency, and will contain a list of debit and credit amounts. Storing the currency alongside each amount is redundant, since the entire account is in a uniform currency. It wouldn't break anything to have it there, but it seems inelegant.

However, even if I modify money-rails to let me tell it not to store the currency alongside the amount in those instances, it still would be nice to have the existing interface work in the usual way, so given a record from the account (which contains a money object), you could evaluate record.amount.currency and it would defer to the account's currency attribute. Of course, that may be way beyond the scope of this gem...

What I'm really trying to say is: Do you have any design suggestions? :)

odigity commented Jul 18, 2012

Forgot to mention - I discovered this tutorial on multi-currency accounting this morning, and it's absolutely wonderful. A great read for anyone looking to fully exploit money's currency support in their accounting software:


alup commented Jul 20, 2012

@odigity Thank you for sharing your thoughts here (and thank you for pointing to this useful URL - though I didn't read it yet :P).

Personally, I would like to support a unified API to both ORMS/ODMS (activerecord and mongoid for now), reading Mongoid should have a way to avoid storing/persisting both money object cents and currency.

If you take a look at the activerecord implementation, this is achieved by using only a cents column and having static versions of currencies (per column, per table or globally for the whole rails app). This should somehow be implemented for Mongoid too.

From next release, v0.5.0, we'll focus on enhancing Mongoid support, so you are welcome to contribute to this effort.

odigity commented Jul 20, 2012

Re: Setting default currency at field/table/app levels - It's probably a good idea to add that for mongoid users for the consistency of your API (though I won't be using that feature in this case).

Re: Intelligent persistance of currency value - The challenge with mongoid is that you can't just check to see whether the table has a currency column in order to decide whether to store the currency, because mongoid doesn't have a schema. :)

You can look at the field definitions of the mongoid document class (mongoid has excellent metadata access for introspection). So, you can make it so if the user has defined a currency field, you store the currency, but that doesn't really fit the mongodb style for several reasons:

  1. The whole advantage of mongodb is that you can make complex documents, with embedded sub-documents. I may be wrong (I'm still a noob), but the mongo-ish way to store Money field would be as an embedded object, with both the value and currency information, as a hash. Breaking a Money object's values (cents & currency) into two separate fields (MySQL-style) instead of keeping them joined as a unified object is not mongo-ish.

  2. Looking for a defined field name (like currency) prevents you from being able to store two Money objects in the same document:

class MyWallet
include Mongoid::Document
field :total_cash, type: Money
field :total_checks, type: Money

So, if we want a way to tell Money to store itself sans Currency, it shouldn't be based on a column-detection method.

Perhaps you can introduce a pair of Mongoid data types to be used for defining fields, instead of just one?

class MyWallet
include Mongoid::Document
field :domestic_cash, type: Money
field :foreign_travelling_leftovers, type: DenominatedMoney

That would give you the information you need to make the decision of how to serialize the Money object when it comes time to store/retrieve it.

In the meantime, I've written my own accessors that handle storing/retrieving the raw cents to the mongoid field, and converting it to/from Money and Currency objects before handing them off to the user (of the gem writing), so I have total control by doing it myself. Probably good enough for now.

It'll be on github soon, if anyone wants to look at the code as an example.

odigity commented Jul 20, 2012

Ok, I decided to be brave and look at the code to see if I can help.

One thing I noticed is that the current mongoid-v2 implementation is storing the iso_code to represent the currency, which is an all-caps string.

One issue I ran into today when implementing my own serialization is getting the validates_inclusion_of method (from ActiveModel, exposed via Mongoid) to work in order to validate that a currency is valid. I used Money::Currency.table.keys to populate the inclusion list (which I found in the Money docs), but that generates a list of lowercase symbols instead of uppercase strings - the same type of value you use in the field (instead of iso_code). So I decided to store the id instead of the iso_code to make that work.

I don't know how to do it, but it might make sense to write a custom ActiveModel validation method for Money for validating currency - that way you maintain the flexibility to store whichever value you like, and also prevent the user from having to even know about Money::Currency.table.keys.

On a related note, I'd like to amend my suggestion from the previous comment. There should be three Mongoid field types defined - one for the quantity, one for the unit, and one for the combination. Maybe something like Money, Money::Currency, and Money::Quantity?

So, the user could define a mongoid document like this:

class MyAccount
include Mongoid::Document
field :balance, type: Money::Quantity

Do just store the cents without the currency. Or:

class MyAccount
include Mongoid::Document
field :currency, type: Money::Currency
has_many :transactions

To just store the currency, and all the transaction objects will just need to store the quantity.

odigity commented Jul 21, 2012

Thought about it some more, and decided it doesn't make sense to try to support my situation in the core. The only information Mongoid v2 gives to the deserialization function is the data you put in, so there's no way to tell it what to use as a default currency, or point it at another field somewhere to pull the currency info from.

For people in my situation, the solution must be custom. So I withdraw my suggestion for a Money::Quantity mongoid field type.


semmons99 commented Nov 28, 2013

no activity, closing

@semmons99 semmons99 closed this Nov 28, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment