Skip to content
This repository

[PATCH] Represent currencies as Currency object #4

Closed
weppos opened this Issue April 15, 2010 · 28 comments

4 participants

Simone Carletti Shane Emmons Hongli Lai Patrik Stenmark
Simone Carletti
Owner

In my money fork I added the ability to represent currencies with Money::Currency.
http://github.com/weppos/money

Additional changes:

  • definition for almost all existing currencies
  • changed the documentation to reflect the Currency feature
  • added Money#symbol, Money#delimiter and Money#separator convenient methods
  • added deprecation warning whenever applicable

All commits are submitted along with a corresponding test suite.

I hope you would merge my changes to the mainstream repository.
I worked hard to ensure backward-compatibility with existing API.

-- Simone

Shane Emmons
Owner

Thanks for the patch. I'll take a look at it and see about incorporating it into trunk.

Shane Emmons
Owner

I have no problem merging this if you make one change. The Brazilian Real should always have a space between the symbol and the amount. You can see we had to patch this with the following commit 898d462. Once that is updated, I will merge your changes into trunk.

Simone Carletti
Owner

Great!

I updated the repository. Make sure to destroy/recreate the remote source if you already fetched it because I used a rebase strategy to clean the commit history.

Thanks for your quick feedback!

Shane Emmons
Owner

I've merged your changes into trunk. I've sent a message to Hong Lai, and as long as he doesn't object, I will release a new version of the gem.

Simone Carletti
Owner

Excellent!

I wish that all projects have this reactivity.
Looking forward to upgrading the Gem dependency list in my app. ;)

Thank you!

-- Simone

Hongli Lai

Looks good, excellent work! Thanks for contributing. The only thing that I don't understand is the priority thing in Currency. It'd be great if you can document that in more detail. Other than that, feel free to release.

Simone Carletti
Owner

No problem.

I added a few more documentation in the following 2 changesets in my fork.
http://github.com/weppos/money/compare/cce64fa...07d18b2

The changes should apply cleanly in your fork queue. If not, let me know, I'll rebase them on your repos.

Shane Emmons
Owner

The updated gem has been released!

Patrik Stenmark

I think patch broke the composed_of example for using the money gem with rails.

In my model I have:

composed_of :price,
  :class_name => "Money",
  :mapping => [%w(cents cents), %w(currency currency)]

Saving a variant object works, but when I load it back from the database, I get a YAML string instead:

 v2.currency # => "--- !ruby/object:Money::Currency \nid: :sek\niso_code: SEK\nname: Swedish Krona\npriority: 100\nsubunit: !binary |\n  w5ZyZQ==\n\nsubunit_to_unit: \"100\"\nsymbol: kr\n"

I have found two solutions to this, the first is to add serialize :currency to my model. The other way is to add a currency_iso_code method to the Money class and use that as a mapping instead.

Simone Carletti
Owner

In my Rails app I extended the Money class with two additional methods

def currency_as_string
  currency.to_s
end

def currency_as_string=(value)
  currency = Currency.wrap(value)
end

And here's my model definition

composed_of :order_price,
            :class_name   => "Money",
            :mapping      => [%w( order_price_cents cents ), %w( order_price_currency currency_as_string )]
Shane Emmons
Owner

can you submit a patch to the documentation with you methods?

Simone Carletti
Owner

I have a doubt about the usefulness of my patch. The two methods above are strictly focused on ActiveRecord composite feature.

Whilst the first might be partially useful for the Money library itself, the second one is technically wrong. In fact, it allows you to change the internal value for currency causing an inconsistent object status.
Ideally, if we want to implement both methods, currency_as_string= should probably be a wrapper for the existing exchange_to method.

What do you think?

Shane Emmons
Owner

I'm having a hard time deciding what to do here. I don't want to put ActiveRecord specific code into the library, and I don't see the use of currency_as_string, and especially currency_as_string=, outside of this.

I'm not very familiar with ActiveRecord, but is there anyway to use the :converter option in composed_of to make this work? Perhaps cents and currency should be stored in separate columns in the db?

Patrik Stenmark

The :converter option is only used when the assigned object is of another class than specified (Money in this case). It can be used to make variant.price = 100 be converted to Money.new(10000, 'SEK') for example, but if I send a Money object the converter is not run.

In my example above I have cents and currency in separate columns, what happens is that AR runs something like write_attribute(:currency, money_object.send(:currency)). In 2.2, currency would have been a string, which gets saved properly, but since it's an object in 2.3, AR magically serializes it to YAML before saving it.

Simone Carletti
Owner

:converter and :constructor have a different goal. When you create a composite column called my_column, ActiveRecord creates a virtual my_column attribute with its own getter and setter methods.

You use :converter and :constructor to instruct how to cast an external object to fit the composite rule.
For example

composed_of :order_price,
            :class_name   => "Money",
            :mapping      => [%w( order_price_cents cents ), %w( order_price_currency currency_as_string )],
            :constructor  => Proc.new { |cents, currency| (cents.blank? || currency.blank?) ? nil : Money.new(cents, currency) },
            :converter    => Proc.new { |value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError, "Can't convert #{value.class} to Money") }

record = Record.new
record.order_price = "$40"
# here the converter is called, equal to
# record.order_price = "$40".to_money
record.order_price = Object.new
# here the converter is called again
# raise ArgumentError, "Can't convert Object to Money"

ActiveRecord expects the values in the mapping hash to be compatible. If you set

:mapping      => [%w( order_price_cents cents ), %w( order_price_currency currency )],

It won't work because it tries to serialize the currency object instead of calling the #to_s method on it (I still have to understand the reason behind this choice. AFAIK, it may be an ActiveRecord bug). At least this is the issue I experiences so far.

I agree, I wouldn't change the Money Gem to fit an ActiveRecord use case. This is exactly the reason why I haven't included the patch before.
At least, we can extend the documentation to include references to this ticket and a quick solution.

Shane Emmons
Owner

Go ahead and write up the documentation and I'll merge it into trunk and release it.

Simone Carletti
Owner
weppos commented May 07, 2010

http://github.com/weppos/money/compare/3d48d49a94950dcb297d...0dd2f8e78032d0054ee1

I also committed 3 other changes. Feel free to review and merge them if you agree with the changes.

Hongli Lai

Looks good. I see a typo though:
"+ # Alternativly you can use the convinience methods like"

Should be "Alternatively you can use the convenience methods like"

I'm not so happy with the ActiveRecord breakage. API breakages should result in a major version bump (e.g. 3.0.0) but the issue has been discovered after the release so no use in crying over spilled milk.

The ActiveRecord instructions should definitely be updated. You've written a new "Money 2.3 and ActiveRecord" section, but why isn't it part of the "Ruby on Rails" section? The "Ruby on Rails" section still contains incorrect code.

Shane Emmons
Owner

I was thinking perhaps we should bump the major version once we get everything sorted out.

Simone Carletti
Owner
weppos commented May 07, 2010

@ FooBarWidget

Fixed the typo, and reorganized the Rails section
http://github.com/weppos/money/compare/3d48d49a94950dcb297d...e0c7273

Hongli Lai

Yes agreed with the major version bump.

@weppos: looks good. You should document that a breakage occurred in 2.3.0 though, and that previous versions used #currency and didn't need the patch.

Hongli Lai

I just noticed that the Github link in README.rdoc doesn't work very well. It links to this issue but the browser doesn't scroll to the correct comment. Can you document the issue directly in the README so that readers don't have to wade through all of our comments in search for the right one?

Shane Emmons
Owner

I merged weppos' changes into trunk and bumped the version. Take a look at the documentation and make sure we don't need anymore changes, then I'll release the gem.

Shane Emmons
Owner

I also created two pages on the wiki to detail information about this issue.

Hongli Lai

Looks good.

Shane Emmons
Owner

The gem has been released and the documentation has been updated.

Shane Emmons
Owner

#currency_as_string and #currency_as_string= have been added to Money as of version 3.0.3

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.