Search code examples
rubyjsonruby-on-rails-3.2ruby-1.9.3enumerable

Enumerable changes my `to_json` behavior


I have a rails application and a class I've wrote as a part of it (not an ActiveRecord or anything..). The data is stored in simple instance variables (string, integers, arrays...)

When I invoke to_json on an instance of it I get what I expect to. A JSON object, containing all instance variables as JSON objects too.

However, when I add include Enumerable to the class definition, the behavior of to_json changes and I get an empty object: "[]"

Any idea why is that? Does Enumerable somehow defined or undefines something that affects to_json?

Thanks!


Solution

  • So, what happens is:

    Rails loads in ActiveSupport. ActiveSupport injects (monkey patches) these as_json methods into several classes and modules, including Enumerable:

    module Enumerable
      def as_json(options = nil) #:nodoc:
        to_a.as_json(options)
      end
    end
    

    You're probably returning nothing for the each method Enumerable requires you to have, so to_a returns [], and an empty array gets converted into the String "[]".

    What you can do here, is, to bind your object into a non-enumerable inherited class, and use its .as_json method.

    Like this:

    class A
      def as_json(*)
        Object.instance_method(:as_json).bind(self).call
      end  
    end
    

    Demo:

    ➜ pry
    require 'active_support/all'
    => true
    
    class A
      def initialize
        @a = 1
      end  
    end  
    => nil
    
    A.new.to_json
    => "{\"a\":1}"
    
    class A
      include Enumerable
      def each
      end
    end
    => nil
    
    A.new.to_json
    => "[]"
    
    class A
      def as_json(*)
        Object.instance_method(:as_json).bind(self).call
      end  
    end  
    => nil
    
    A.new.to_json
    => "{\"a\":1}"