BigDecimal and YAML don't get along
My Rails 1.2 upgrade could have gone a lot better. At first everything seemed fine. Then I tried setting up a new test environment (which involves using ar_fixtures). The fixtures were created fine, but when I tried to test with them, I got the following.
TypeError: BigDecimal can't be coerced into BigDecimal
So after perusing I noticed that the Rails 1.2+ar_fixtures is outputting an empty BigDecimal object (I think it used to just output 0.0). Well.. that seems legit.. but it’s not working. After doing some extensive googling I don’t seem to uncover many other people having the problem, but I do find this bug reporting the same bug. Low and behold this is a Ruby core library! The ticket has been open for two months, so I think surely it doesn’t still exist… I try it out with irb and get the following:
irb -r 'yaml' -r 'bigdecimal'
irb(main):001:0> YAML::load( BigDecimal.new('1.1').to_yaml )
TypeError: BigDecimal can't be coerced into BigDecimal
from /usr/local/lib/ruby/1.8/irb.rb:298:in `inspect'
from /usr/local/lib/ruby/1.8/irb.rb:298:in `output_value'
from /usr/local/lib/ruby/1.8/irb.rb:151:in `eval_input'
from /usr/local/lib/ruby/1.8/irb.rb:259:in `signal_status'
from /usr/local/lib/ruby/1.8/irb.rb:147:in `eval_input'
from /usr/local/lib/ruby/1.8/irb/ruby-lex.rb:244:in `each_top_level_statement'
from /usr/local/lib/ruby/1.8/irb/ruby-lex.rb:230:in `loop'
from /usr/local/lib/ruby/1.8/irb/ruby-lex.rb:230:in `each_top_level_statement'
from /usr/local/lib/ruby/1.8/irb/ruby-lex.rb:229:in `catch'
from /usr/local/lib/ruby/1.8/irb/ruby-lex.rb:229:in `each_top_level_statement'
from /usr/local/lib/ruby/1.8/irb.rb:146:in `eval_input'
from /usr/local/lib/ruby/1.8/irb.rb:70:in `start'
from /usr/local/lib/ruby/1.8/irb.rb:69:in `catch'
from /usr/local/lib/ruby/1.8/irb.rb:69:in `start'
from /usr/local/bin/irb:13
Maybe IRB bug!!After going through the source I end up in C libraries and patching is certainly an extreme measure here. So it seems I have to live with the bug. On a hunch I search/replace all of the empty BigDecimal objects to 0.0 and it finally works. But… what’s up with this? This looks like a rather serious bug. I would think Matz himself would be in there fixing it after two months! Maybe I’ll take a shot at fixing it myself…
So… if you get bit by the “BigDecimal can’t be coerced into BigDecimal” bug you can get around it by converting it to string:
irb(main):002:0> YAML::load( BigDecimal.new('1.1').to_s.to_yaml )
=> "0.11E1"In my case I have to search and replace these fixtures post export, with:
contents = ''
if Event.to_fixture &&
File.open('test/fixtures/events.yml', 'r') {|fin| contents << fin.read;} &&
File.open('test/fixtures/events.yml', 'w') {|fout| fout << contents.gsub('!ruby/object:BigDecimal {}', '0.0'); fout.flush}
puts 'Event fixtures created'
else
puts 'Failed to create Event fixtures'
endYou can probably figure out most workarounds from that.
Trackbacks
Use the following link to trackback from your own site:
http://blog.chrispcritter.com/trackbacks?article_id=13
-
With Rails 1.2 there was the introduction of the native Decimal-support in the database. As floats are a bad choice for precise calculations I have used the Decimal-columns for a long time. Switching to Rails 1.2 enabled the use of this columns in...
-
Rails 1.2 introduced native Decimal-support in ActiveRecord. This enabled us to use decimal-columns in migrations without the need to drop back to SQL. Also the Decimal-columns are now returned as BigDecimal-objects by ActiveRecord. During the mig...
-
I can't add your feed to Feedburner. How I do this?
-
I want see AlexaRank of your site and buy links. How I do this?
Great post! I monkey patched BigDecimal#to_yaml to get it to behave. It seems to also reproduce @ the original accuracy (the reason to use BigDecimal in the first place?)
class BigDecimal # this works around http://code.whytheluckystiff.net/syck/ticket/24 until it gets fixed.. alias :_original_to_yaml :to_yaml def to_yaml (opts={},&block) to_s.to_yaml(opts,&block) end endshould have previewed..
Thanks for the patch Paetor, that will help out!
Hi
Where should I put
class BigDecimal # this works around http://code.whytheluckystiff.net/syck/ticket/24 until it gets fixed.. alias :originaltoyaml :toyaml def to_yaml (opts={},&block) tos.toyaml(opts,&block) end end
Assuming you’re using Rails, I would just drop it in the lib path. Somethiing like: lib/big_decimal_yml_fix.rb. That should be loaded before the rest of your application. If not, you could explicitly require it at the top of environment.rb to ensure it loads before everything else.
Hi chrisp
Copied yamlbigdecimal_fix.rb(Mike Raidels fix) to project\lib and added require ‘yamlbigdecimal_fix to environment.rb.
stop/start web server and the problem went away
Thanx :-)
Thanx very much, saved me a lot of searchin’!
Thanks for the very useful code, just to say I had to put ‘require bigdecimal’ at the top of bigdecimalyml_fix.rb otherwise running rake tasks failed with ‘BigDecimal already defined’. Apparently this happens when the fix definition is encountered before the standard library one, and adding the require stops that happening.
Joining the crew of thankful :) Turns out we are still using ruby 1.8.4 on production, so this bug just showed up.
Seo Service India
Thanks a lot for sharing….this is amazing!!!!!
Joining the crew of thankful :) Turns out we are still using ruby 1.8.4 on production, so this bug just showed up.
Wholesale Brand Name Clothing
you could explicitly require it at the top of environment.rb to ensure it loads before everything else.
Wholesalers