rubymoney / monetize Goto Github PK
View Code? Open in Web Editor NEWA library for converting various objects into `Money` objects.
License: MIT License
A library for converting various objects into `Money` objects.
License: MIT License
Hi,
would it be possible, to include a method Monetize("12.34$")
that raises an error, when non parsable values are passed? I like this behaviour from the Ruby core, when you try to use
"foobar".to_f # => 0.0
Float("foobar") # => ArgumentError: invalid value for Float(): "foobar"
Sometimes I want to raise a validation error in my API, when the user submits nonsense. It's very confusing when nonsense is parsed to 0.0
;)
This behaviour can be seen at some core types like Float
, Integer
or Array
, so I think it would be a best practise to stick to this convention.
Seems like it would be easier to maintain/improve if you used Ruby's built in constructor:
data = "$199.99"
money = Monetize.new(data).to_money
If I install both this and money rails
Monetize.parse_collection
and
Monetize.parse
No longer function
I can get an equivalent tothe latter with .to_money but can't seem to find a replacement for parse_collection for dealing with ranges.
After gem install monetize
, a require 'monetize'
gives me:
NameError: uninitialized constant Monetize::Collection::Forwardable
from /usr/local/lib/ruby/gems/2.2.0/gems/monetize-1.2.0/lib/collection.rb:7:in `<class:Collection>'
.....
This looks to be a similar situation as, e.g. resque/resque#1128
If I do require 'forwardable'
before I require monetize, then it works fine.
So perhaps change (as is suggested in the resque comment) "extend Forwardable" to "extend ::Forwardable" ?
If it matters, this is with monetize 1.2.0 and ruby -v says "ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin14]"
Currently it is hard to tell if given string represents $0, some price, or is actually wrong currency string (use case: any type of validation / fuzzy matching).
Monetize.parse("lololo").to_f # 0.0
Monetize.parse('8,999,9').to_f # 89999.0
Maybe introduce parse!
method with very strict, white-listed algorithm?
Parsing currency symbols should be enabled by default. I missed that in the beginning, tested on USD and now having issues with wrong data in the DB.
What was the intense to make that false
by default?
surely (but not currently):
Monetize.parse("$(516.90)") == Money.new(-51690, "USD") # => true
This is a meta issue.
Comments on Issue #28 seem to be getting ignored.
If this is intentional - cool, can someone just say why?
Hi,
our Gemfile.lock entry for monetize is locked at 0.4.1, and it's not apparent to know what broke with the 1.0.0 release. Commit doesn't give the info 8df488b and there's no entry in the changelog either!
Please clarify.
Thanks much!
Victor
Monetize.parse seems to only look in front of the value and ignores anything after. It is common for EUR to be written after the value.
> Monetize.assume_from_symbol = true
=> true
> price = "13,23 €"
=> "13,23 €"
> Monetize.parse(price)
=> #<Money fractional:1323 currency:USD>
> price = "€ 13,56"
=> "€ 13,56"
> Monetize.parse(price)
=> #<Money fractional:1356 currency:EUR>
When Money.infinite_precision = true
, I would expect:
Monetize.extract_cents '1.234'
=> 123.4
Instead, I get:
Monetize.extract_cents '1.234'
=> 123
(This also applies to '1.234'.to_money
, which calls Monetize.extract_cents
AFAICT.)
Monetize.parse('2,50€')
=> Money fractional:250 currency:USD
require 'pp'
require 'monetize'
Monetize.assume_from_symbol = true
pp ( Monetize.parse("£100") + Monetize.parse('-£10.00'))
root@nodejs-dev-andy:/mnt/srv/www/checkout/current/tests# ruby x
#<Money fractional:-1000 currency:USD>
/usr/local/lib/ruby/gems/2.0.0/gems/money-6.2.1/lib/money/bank/variable_exchange.rb:98:in `exchange_with': No conversion rate known for 'USD' -> 'GBP' (Money::Bank::UnknownRate)
from /usr/local/lib/ruby/gems/2.0.0/gems/money-6.2.1/lib/money/money.rb:418:in `exchange_to'
from /usr/local/lib/ruby/gems/2.0.0/gems/money-6.2.1/lib/money/money/arithmetic.rb:95:in `+'
from x:11:in `<main>'
require 'pp'
require 'monetize'
# http://msdn.microsoft.com/en-gb/goglobal/bb688126.aspx
Monetize.assume_from_symbol = true
currencies = {
'UK' => { :from => '-£10.00', :to => '-£20.00' },
'FRANCE' => { :from => '-10,00 F', :to => '-20,00 F' },
'DENMARK' => { :from => 'kr-10,00', :to => 'kr-20,00' },
'NETHERLANDS' => { :from => '€ 10,00', :to => '€ 20,00' },
'US' => { :from => '($10.00)', :to => '($20.00)' },
}
currencies.each do |k,v|
from = Monetize.parse(v[:from])
to = Monetize.parse(v[:to])
puts " --- "
pp from
pp to
pp (from + to)
end
invalid usd output.
root@nodejs-dev-andy:/mnt/srv/www/checkout/current/tests# ruby x
---
#<Money fractional:-1000 currency:USD>
#<Money fractional:-2000 currency:USD>
#<Money fractional:-3000 currency:USD>
---
#<Money fractional:-1000 currency:USD>
#<Money fractional:-2000 currency:USD>
#<Money fractional:-3000 currency:USD>
---
#<Money fractional:-1000 currency:USD>
#<Money fractional:-2000 currency:USD>
#<Money fractional:-3000 currency:USD>
---
#<Money fractional:1000 currency:EUR>
#<Money fractional:2000 currency:EUR>
#<Money fractional:3000 currency:EUR>
---
#<Money fractional:1000 currency:USD>
#<Money fractional:2000 currency:USD>
#<Money fractional:3000 currency:USD>
if with a possitive currency this is correct...
--
#<Money fractional:1000 currency:GBP>
#<Money fractional:2000 currency:GBP>
#<Money fractional:3000 currency:GBP>
--
#<Money fractional:1000 currency:USD>
#<Money fractional:2000 currency:USD>
#<Money fractional:3000 currency:USD>
--
#<Money fractional:1000 currency:USD>
#<Money fractional:2000 currency:USD>
#<Money fractional:3000 currency:USD>
--
#<Money fractional:1000 currency:EUR>
#<Money fractional:2000 currency:EUR>
#<Money fractional:3000 currency:EUR>
--
#<Money fractional:1000 currency:USD>
#<Money fractional:2000 currency:USD>
#<Money fractional:3000 currency:USD>
Hi,
Just wondering why the gemspec dependency for money
is only on development?
If monetize
includes require 'money'
then shouldn't that be a dependency on the gem, even loosely with no version number?
Including monetize
i'd assume money
is included too?
After the 1.9.3
release, parsing any iso code ending with an R
returns currency as ZAR when assume_from_symbol
is on as demonstrated here:
irb(main):001:0> require 'monetize'
=> true
irb(main):002:0> 'EUR -13.00'.to_money
=> #<Money fractional:-1300 currency:EUR>
irb(main):003:0> Monetize.assume_from_symbol = true
=> true
irb(main):004:0> 'EUR -13.00'.to_money
=> #<Money fractional:-1300 currency:ZAR>
irb(main):005:0>
8bfb079 is the only code change in that release, so it has probably something to do with that regex. The last char on the iso code seems to be matching the symbol.
From what I can tell, there are two exceptions that .parse
(and #to_money
) might throw when passed a string value:
Monetize.parse('100 OMG')
# => Money::Currency::UnknownCurrency: Unknown currency 'omg'
Monetize.parse('100-500')
# => ArgumentError: Invalid currency amount (hyphen)
The problem is that for 99.99% of all other possible inputs, this gem will sanitize/normalize the value and return something, no matter how nonsensical. (A couple of my favorites: 3:00pm
=> $300
, 1/21/1989
=> $1,211,989
.) I honestly see this behavior as a feature, not a bug.
So it's common to just pass user input straight into this gem and let the gem sort it out. (App developers can and should still validate the original input value based on how strict they want to be, but that's on them to decide.) For the most part, this gem just works (as long as you don't trust it to validate your inputs).
What would be great is if for those 0.01% of times when someone enters something that will cause this gem to blow up, there were an option to swallow exceptions and just return nil
instead.
The alternative is to catch those two exceptions inside of application/business logic, which isn't the end of the world, but the above cases are so uncommon that I suspect most devs won't bother until something hits them in production. The money-rails
gem already handles these two exceptions, but arguably it wouldn't have to if such an option existed:
Thoughts?
Edit: Happy to produce a PR if there is any agreement on this.
Firstly, thank you for the gem, its amazing and helped us a lot on the project we're working on.
Are there any plans to update the gem for Ruby 2.6.x that has depreciated BigDecimal.new
and use BigDecimal()
instead?
when parsing 1.234
with a currency that has ,
as decimal_mark and .
as thousands_separator, the currency settings are ignored, and .
is considered to be the decimail_mark.
I patched the code locally in my rails project:
module Monetize
class << self
def extract_cents_with_decimals_addition(input, currency = Money.default_currency)
decimal_mark = I18n.t('number.currency.format.separator')
input = "#{input}#{decimal_mark}0" unless input.include?(decimal_mark)
extract_cents_without_decimals_addition(input, currency)
end
alias_method_chain :extract_cents, :decimals_addition
end
end```
Looks like this gem requires gem money to work
gems/monetize-0.1.4/lib/monetize.rb:3:in `require': cannot load such file -- money (LoadError)
Why don't we add 'money' to runtime dependencies?
Here's an example with Banker's Rounding mode:
Money.from_amount(1.545, :usd)
# => #<Money fractional:154 currency:USD>
Monetize.parse!('1.545 USD')
# => #<Money fractional:155 currency:USD>
Perhaps it's already fixed on master.
Hi,
The Usage section of the readme shows as its first example:
Monetize.parse("$100") == Money.new(100_00, "USD")
Monetize.parse("€100") == Money.new(100_00, "EUR")
Monetize.parse("£100") == Money.new(100_00, "GBP")
However these examples only work as shown if Monetize.assume_from_symbol = true
has previously been set. Without it, they all equal Money.new(100_00, "USD")
, with the currency only being picked up if it's specified as an ISO code:
$ irb
irb(main):001:0> require 'monetize'
=> true
irb(main):002:0> Monetize.parse("£100")
=> #<Money fractional:10000 currency:USD>
irb(main):003:0> Monetize.parse("GBP 100")
=> #<Money fractional:10000 currency:GBP>
This is a bit confusing, since the assume_from_symbol
option defaults to nil, isn't mentioned until after these examples, and then only in the context of String#to_money
. To me it reads as if the first examples are intended to work as shown without this option set.
All the best,
Simon
Looking at https://github.com/RubyMoney/monetize/blob/master/spec/monetize_spec.rb#L33-L35, these lines store the current default currency, set a new one, and then immediately restore the original.
I noticed this because some of the tests in the same group appear to be failing arbitrarily in the refactoring work I am doing in regards to #11
I'm guessing you want something more like
describe 'currency assumption' do
context 'opted in' do
before :all do
@original_currency = Money.default_currency
Monetize.assume_from_symbol = true
Money.default_currency = "EUR"
end
after :all do
Monetize.assume_from_symbol = false
Money.default_currency = @original_currency
end
it "parses formatted inputs with Euros passed as a symbol" do
expect(Monetize.parse("€5.95")).to eq Money.new(595, 'EUR')
end
it "parses formatted inputs with Euros passed as a symbol with a preceding space" do
expect(Monetize.parse(" €5.95 ")).to eq Money.new(595, 'EUR')
end
it "parses formatted inputs with British Pounds Sterling passed as a symbol" do
expect(Monetize.parse("£9.99")).to eq Money.new(999, 'GBP')
end
it "parses formatted inputs with South African Rand passed as a symbol" do
expect(Monetize.parse("R9.99")).to eq Money.new(999, 'ZAR')
end
it 'should assume default currency if not a recognised symbol' do
expect(Monetize.parse("L9.99")).to eq Money.new(999, 'USD')
end
end
Was trying to parse prices from amazon in Canada and they use CDN instead of CAD as the identifier.
To reproduce:
"CDN$ 5.00".to_money
Result:
Monetize::ParseError Exception: Unknown currency 'cdn'
(byebug) Monetize.parse('2,600').dollars.to_f
2600.0
(byebug) Monetize.parse('2.600').dollars.to_f
2.6
(byebug) Monetize.parse('EUR 2,600').dollars.to_f
2.6
(byebug) Monetize.parse('EUR 2.600').dollars.to_f
2.6
'not a number'.to_money
- results in zero money object, I believe it's a bug.
What do you think?
warning: loading in progress, circular require considered harmful - /home/n/.rvm/gems/ruby-2.4.1/gems/monetize-1.7.0/lib/monetize.rb
/home/n/.rvm/rubies/ruby-2.4.1/bin/ruby -w -I"lib:test" -I"/home/n/.rvm/gems/ruby-2.4.1/gems/rake-12.1.0/lib" "/home/n/.rvm/gems/ruby-2.4.1/gems/rake-12.1.0/lib/rake/rake_test_loader.rb" "test/test_account.rb"
/home/n/.rvm/gems/ruby-2.4.1/gems/monetize-1.7.0/lib/collection.rb:3: warning: loading in progress, circular require considered harmful - /home/n/.rvm/gems/ruby-2.4.1/gems/monetize-1.7.0/lib/monetize.rb
from /home/n/.rvm/gems/ruby-2.4.1/gems/rake-12.1.0/lib/rake/rake_test_loader.rb:5:in `<main>'
from /home/n/.rvm/gems/ruby-2.4.1/gems/rake-12.1.0/lib/rake/rake_test_loader.rb:5:in `select'
from /home/n/.rvm/gems/ruby-2.4.1/gems/rake-12.1.0/lib/rake/rake_test_loader.rb:17:in `block in <main>'
from /home/n/.rvm/gems/ruby-2.4.1/gems/rake-12.1.0/lib/rake/rake_test_loader.rb:17:in `require'
from /home/n/Documents/imazen/ks-deliver/test/test_account.rb:3:in `<top (required)>'
from /home/n/Documents/imazen/ks-deliver/test/test_account.rb:3:in `require_relative'
from /home/n/Documents/imazen/ks-deliver/ks.rb:6:in `<top (required)>'
from /home/n/Documents/imazen/ks-deliver/ks.rb:6:in `require'
from /home/n/.rvm/gems/ruby-2.4.1/gems/monetize-1.7.0/lib/monetize.rb:7:in `<top (required)>'
from /home/n/.rvm/gems/ruby-2.4.1/gems/monetize-1.7.0/lib/monetize.rb:7:in `require'
from /home/n/.rvm/gems/ruby-2.4.1/gems/monetize-1.7.0/lib/collection.rb:3:in `<top (required)>'
from /home/n/.rvm/gems/ruby-2.4.1/gems/monetize-1.7.0/lib/collection.rb:3:in `require'
Got an unexpected error when trying to parse a money string:
require 'monetize'
Monetize.parse("$10,000.00")
#=> TypeError: no implicit conversion of String into Integer
from money-6.13.7/lib/money/money.rb:294:in `round'
I'm using monetize 1.9.4 and ruby 2.7.0
EDIT: Messing around I quickly realized this is an issue with ruby 2.7, which the gem doesn't support!
I'm using the money-rails gem https://github.com/RubyMoney/money-rails
and after upgrading to ruby 2.4.0, I'm getting this deprecation warning when running tests.
/Users/chrisjeon/.rvm/gems/ruby-2.4.0/gems/monetize-1.4.0/lib/monetize.rb:87: warning: constant ::Fixnum is deprecated ] 47% Time: 00:00:30, ETA: 00:00:34
Screenshot attached.
Trying to play with the gem but my shell barks an error...
gem install monetize
Successfully installed monetize-0.4.0
Parsing documentation for monetize-0.4.0
Installing ri documentation for monetize-0.4.0
Done installing documentation for monetize after 0 seconds
1 gem installed
16:48 ~/Documents/workspace/ (master) m$ pry
[1] pry(main)> require 'monetize'
NameError: uninitialized constant Class::Set
from /Users/astro/.rvm/gems/ruby-2.1.2/gems/money-6.2.0/lib/money/currency.rb:130:in `stringify_keys'
Currently nil and empty string returns a Money with value 0 in the default currency.
2.2.0 :044 > Monetize.parse(nil)
=> #<Money fractional:0 currency:USD>
2.2.0 :045 > Monetize.parse('')
=> #<Money fractional:0 currency:USD>
Is this the expected behavior?
From a client perspective, it seems like returning a nil return would make more sense since $0 is a legitimate value and shouldn't be used as the default for error cases.
This might be a Money gem issue but I couldn't isolate it using only money gem in example.
Solidus uses this heavily and we noticed a couple failures related to a bump from monetize 1.8.0 to 1.9.0. See solidusio/solidus#2826 and nebulab/solidus#15. The failing examples in solidus build checks if a given amount is split properly among a collection of order line items. Example:
# frozen_string_literal: true
source 'https://rubygems.org'
gem 'monetize', '~> 1.8.0'
gem 'rspec'
# frozen_string_literal: true
require 'monetize'
require 'money/version'
RSpec.describe "Money#allocate #{Money::VERSION} monetize #{Monetize::VERSION}" do
let(:total) { 15 }
it 'rounds properly' do
weights = [0.5, 0.16666666666666666, 0.3333333333333333]
expect(
total.to_money.allocate(weights).map(&:to_money).map(&:to_d)
).to eq([7.5, 2.5, 5.0])
end
it 'also rounds properly' do
weights = [0.13333333333333333, 0.2, 0.6666666666666666]
expect(
total.to_money.allocate(weights).map(&:to_money).map(&:to_d)
).to eq([2, 3, 10])
end
end
Those two examples pass on 1.8.0 but the last one fail on 1.9.0. It seems to split the given amount as [2.01, 2.99, 10]
.
Hi guys, I found this problem when parsing VND string. Is this a bug, or is meant to work like this?
Monetize.parse("568,000 VND")
should be parsed into 56800000 cents but instead, parsed into 568 cents.
Current temporary solution:
# Monkeypatch the parse method as it passing string with VND wrongly
Monetize.module_eval do
def self.parse(input, currency = Money.default_currency, options = {})
input.delete!(",") if input.to_s.include?("VND")
parse! input, currency, options
rescue Monetize::Error
nil
end
end
Hey, I've been playing around with refactoring the extract_cents
method. I've still got some work to do (although it should still work and match the same API), but I thought you might want to see it before I spring it on you all at once. It's certainly got some problems, but I'm hoping it ends up somewhere good.
You can see my changes at https://github.com/awagener/monetize/compare/refactor_extract_cents
Thanks!
Amanda
When the currency.decimal_mark doesn't match the used decimal mark strange results occurs.
EUR's decimal_mark is ',' so everything works fine:
Money.default_currency = Money::Currency.new(:eur)
Monetize.extract_cents('1,0') # => 100
Monetize.extract_cents('1,00') # => 100
Monetize.extract_cents('1,000') # => 100
Monetize.extract_cents('1,0000') # => 100
USD's decimal_mark is '.' so parsing '1,000' is a problem:
Money.default_currency = Money::Currency.new(:usd)
Monetize.extract_cents('1,0') # => 100
Monetize.extract_cents('1,00') # => 100
Monetize.extract_cents('1,000') # => 100000
Monetize.extract_cents('1,0000') # => 100
It happens here: https://github.com/RubyMoney/monetize/blob/master/lib/monetize.rb#L203
I also seems strange to at all look at the currencies decimal_mark. This should depend on the locale.
Hi, I'm from Sri Lanka & our currency is LKR. So I pass LKR like this,
Monetize.parse([amount, "LKR" ].join)
But I'm getting is 444.00 ௹
which is 444.00 ௹
. LKR should display like Rs 444.00
How can I fix this issue??
I have some varying dollar amounts:
$10.12
$10.123
$10.1234
$10.12345
and so on..
Doing Monetize.parse("10.123")
returns 10.12
. It drops anything beyond two decimal places.
Is there a way to preserve the precision of the original string?
Would you be open to accepting a PR for parsing lists of values or ranges?
The API I'm thinking of would be something like this:
Monetize.parse("$100 - $200", multiple: true)
which would always return an instance of something like Monetize::Multiple
which would be an enumerable (having first, last, each, etc) containing Money objects. It would additionally have methods of
Monetize::Multiple#range?
Monetize::Multiple#list?
for reporting whether the parsed string was a range or list.
I wrote something similar just using raw string parsing (without the Money library) but I'd like to align it with Money (I made the noob mistake of parsing currency as floats): https://github.com/bensheldon/fuzzy_money/blob/master/spec/lib/fuzzy_money/price_spec.rb
I'm happy to submit it as a PR against your library or create another gem. Let me know what you'd prefer. Thanks!
Hi there,
When I use the Gem in a REPL, it works fine, but when I try to run from RSpec, I keep getting the error:
undefined method `default_currency' for Money:Class
This happens when calling Monetize.parse
I'm using monetize version 1.9.2
If I try to parse some nonsense, it returns 0. But if I give it 1-1
, it raises an error:
> Monetize.parse('@#$')
=> #<Money fractional:0 currency:USD>
> Monetize.parse('1-1')
ArgumentError: Invalid currency amount (hyphen)
I think, it should either always return 0 for invalid strings, or (better) always raise.
When trying to parse a dollar amount with an exponent value the exponent value is ignored.
Monetize.parse("USD 1E3").to_f
Results in 13.0
. Rather than 1000.0
.
Is this expected behavior?
jruby-1.7.17 :045 > Monetize.parse(10.33, 'USD')
=> #<Money fractional:1033 currency:USD>
jruby-1.7.17 :046 > Monetize.parse(10.33, 'JPY')
=> #<Money fractional:10 currency:JPY>
Parsing the same amount in USD and JPY gives a different result. I would have expected also JPY amount to be 1033 in cents.
Using
money 6.5.1
monetize 1.1.0
It would be neat to be able to declare money values idiomatically, as you would with Rails time.
Some examples:
100.dollar
1.dollar
50.cents
I think we should update the required version of Money here:
Line 22 in f4d05d3
Are there any plans to make a release with the latest master changes?
I have started implementing the detection of arbitrary currencies. So far, it seems to work and hasn't broken any original tests:
https://github.com/daniel-levin/monetize/commits/other-currencies
Since I don't own this project, there are a few things I want to discuss (with my initial thoughts):
money
gem instead of hard coding itmoney
stores the currencies in a JSON file and re-reads it every time you call Money::Currency.table
)I've noticed some unexpected behavior with hyphen input in version 1.1.0.
[11] pry(main)> "-foo".to_money("USD")
=> #<Money fractional:0 currency:USD>
[12] pry(main)> "--foo".to_money("USD")
ArgumentError: Invalid currency amount (hyphen)
from .rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/monetize-1.1.0/lib/monetize.rb:85:in `extract_cents'
I think this is happening because the gem is interpreting the first hyphen as a negative indicator - but the second hyphen as an invalid character (see https://github.com/RubyMoney/monetize/blob/master/lib/monetize.rb#L98). Considering that other special characters generate a zero amount (e.g. "$|}{&*#(*".to_money("USD")
), it seems more intuitive to have a hyphen counted as a valid character.
Can the hyphen check on https://github.com/RubyMoney/monetize/blob/master/lib/monetize.rb#L104 be removed, or is it serving another purpose?
I get deprecation warnings from Monetize when testing in Rails 5.1.4.
/Users/my_username/.rbenv/versions/2.4.2/lib/ruby/gems/2.4.0/gems/monetize-1.4.0/lib/monetize.rb:87: warning: constant ::Fixnum is deprecated
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.