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'
end

You can probably figure out most workarounds from that.

Posted by chrisp Sun, 11 Feb 2007 08:07:00 GMT


Trackbacks

Use the following link to trackback from your own site:
http://blog.chrispcritter.com/trackbacks?article_id=13

  1. 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...
  2. 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...
  3. Feedburner MrRudy
    I can't add your feed to Feedburner. How I do this?
  4. AlexaRank MrRobby
    I want see AlexaRank of your site and buy links. How I do this?

Comments

Leave a response

  1. Paetor 2 days later:

    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 end

  2. Paetor 2 days later:

    should have previewed..

    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
    end
  3. chrisp 6 days later:

    Thanks for the patch Paetor, that will help out!

  4. cch@karensoft.com.my 6 months later:

    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

  5. chrisp 6 months later:

    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.

  6. cch@karensoft.com.my 6 months later:

    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 :-)

  7. axe 10 months later:

    Thanx very much, saved me a lot of searchin’!

  8. Leo about 1 year later:

    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.

  9. Konstantin about 1 year later:

    Joining the crew of thankful :) Turns out we are still using ruby 1.8.4 on production, so this bug just showed up.

  10. Helmore over 3 years later:

    Seo Service India

  11. face blog over 3 years later:

    Thanks a lot for sharing….this is amazing!!!!!

  12. ShAaNiG over 3 years later:

    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

  13. ShAaNiG over 3 years later:

    you could explicitly require it at the top of environment.rb to ensure it loads before everything else.

    Wholesalers

Leave a comment