Friday 3 September 2010

TypeErrors in to_json make me briefly sad

TypeError Exception: wrong argument type JSON::Pure::Generator::State (expected Data)


Sometimes, the ability to monkey-patch things in Ruby is great. It leads people to add globally useful functionality, like a to_json method for all objects.

Sometimes though, you're scratching an itch somebody else has scratched already. And when you do, you mess with people's verbs, so you'd better make sure you do it consistently.

It turns out that ActiveSupport and json(_pure) have both scratched that to_json itch. And they've done it in incompatible ways. Specifically, if you are in the middle of an ActiveSupport to_json call and you call one added by the json gem, you end up passing it the wrong arguments. I'm far from the only person to run into this, and people solve it in different ways.

I'm not in a position to Just Pick One, because couchrest and facebooker depend on the json gem, and other stuff depends on ActiveSupport. My problem is specifically when serializing Arrays, but I don't like the idea (suggested in the linked thread) of klumping together a hybrid Array/Enumerable mixin that will shamble about intercepting each() calls. But a bit of poking at specifics reveals that my issue is maybe more restricted than I thought:


>?> [].to_json
=> "[]"
>> ["1"].to_json
=> "[\"1\"]"
>> [1].to_json
TypeError: wrong argument type JSON::Pure::Generator::State (expected Data)
from /opt/local/lib/ruby/gems/1.8/gems/json-1.4.6/lib/json/pure/generator.rb:318:in `to_json'
from /opt/local/lib/ruby/gems/1.8/gems/json-1.4.6/lib/json/pure/generator.rb:318:in `json_transform'
from /opt/local/lib/ruby/gems/1.8/gems/json-1.4.6/lib/json/pure/generator.rb:315:in `each'
from /opt/local/lib/ruby/gems/1.8/gems/json-1.4.6/lib/json/pure/generator.rb:315:in `json_transform'
from /opt/local/lib/ruby/gems/1.8/gems/json-1.4.6/lib/json/pure/generator.rb:302:in `to_json'
from (irb):12
>> 1.to_json
TypeError: wrong argument type JSON::Pure::Generator::State (expected Data)
from (irb):13:in `to_json'
from (irb):13


It turns out it's only happening when I'm dealing with a Fixnum. Maybe this will turn out to be a simple thing...

In the activesupport gem directory, there are a whole bunch of files that do the monkeypatching of various classes to support to_json. One of them, numeric.rb, is full of entries like this:


class Integer
def to_json(options = nil) #:nodoc:
to_s
end
end


Could it be that ActiveSupport just needs to add one for Fixnum, since the json gem seems to have put one in so that ActiveSupport's calls don't just fall through to (the version added by ActiveSupport::JSON to) Object?

Actually, yes.

Putting


class Fixnum
def to_json(options = nil)
to_s
end
end


In my initializer setup makes it all go away.

5 comments:

  1. Thank you, you just saved me a huge amount of pain and suffering :)

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. I orignally thought the Fixnum fix got mine, but it was slightly different. In my case, removing some explicit
    require 'json/pure'
    statements at the top of a couple of files in my Rails app caused everything to behave.

    ReplyDelete
  4. Thanks for the tip, just saved me a ton of time. Have you considered submitting a patch to Rails?

    ReplyDelete