rails to_json


by Ara T. Howard

rails, including rails3, has this lovely bit of code related to providing a "to_json" method on built-ins:


# Hack to load json gem first so we can overwrite its to_json.
    begin
      require 'json'
    rescue LoadError
    end

    # The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
    # their default behavior. That said, we need to define the basic to_json method in all of them,
    # otherwise they will always use to_json gem implementation, which is backwards incompatible in
    # several cases (for instance, the JSON implementation for Hash does not work) with inheritance
    # and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
    [Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
      klass.class_eval <<-RUBY, __FILE__, __LINE__
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def to_json(options = nil)
ActiveSupport::JSON.encode(self, options)
end
RUBY
    end

what this effectively does is clobber the "tojson" method the json gem provides with one that activesupport feels is better. the comment about json being broken is out of date and this was hack to begin with. in my case i really like this sort of thing in my /api controller:


  def to_json(object)
    if Rails.env.production?
      JSON.generate(object)
    else
      JSON.pretty_generate(object)
    end
  end

since it makes debugging a shit-ton easier. here's how i did it in a rails3 app -

step one was to make rails3 support the "preinitializer" concept that old-skool rails used to have:


    ### file: config/preinitializer.rb

    dirname = File.dirname(File.expand_path(__FILE__))
    glob = File.join(dirname, 'preinitializers', '**/**.rb')
    preinitializers = Dir.glob(glob)

    preinitializers.each{|preinitializer| Kernel.load(preinitializer)}

next, i added this preinitializer:


### file: config/preinitializers/un_fuck_to_json.rb

    begin
      require 'rubygems'
    rescue LoadError
    end

    begin
      require 'json'
    rescue LoadError
    end

    [Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
      klass.class_eval do
        alias_method('__to_json__', 'to_json') if method_defined?('to_json')
      end
    end

finally, this initializer:


### file: config/preinitializers/un_fuck_to_json.rb

    begin
      require 'rubygems'
    rescue LoadError
    end

    begin
      require 'json'
    rescue LoadError
    end

    [Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
      klass.class_eval do
        alias_method('__to_json__', 'to_json') if method_defined?('to_json')
      end
    end

in english what this does is

  • make rails support code that runs before all else
  • during this phase suck in the json gem and remember how it does things
  • nuke the rails' way and restore the json method of to_json