Skip to content

Instantly share code, notes, and snippets.

@jeremy
Created June 6, 2009 01:16
Show Gist options
  • Save jeremy/124616 to your computer and use it in GitHub Desktop.
Save jeremy/124616 to your computer and use it in GitHub Desktop.
# Users: JSON.encode(whatever)
#
# Implementors: override dump_json(options) returning a Hash, Array, etc
# or: override encode_json(encoder) using the encoder API to return a JSON string
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/instance_variables'
module ActiveSupport
module JSON
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def self.encode(value, options = nil)
Encoder.new(options).encode(value)
end
class Encoder
class CircularReferenceError < StandardError; end
attr_reader :options
def initialize(options)
@options = options
@seen = []
end
def encode(value)
check_for_circular_references(value) do
value.dump_json(options).encode_json(self)
end
end
def escape(string)
Encoding.escape(string)
end
private
def check_for_circular_references(value)
if @seen.any? { |object| object.equal?(value) }
raise CircularReferenceError, 'object references itself'
end
@seen.unshift value
yield
ensure
@seen.shift
end
end
end
end
class Object
def dump_json(options) self end
def encode_json(encoder) encoder.encode(instance_values) end
end
# A string that returns itself as its JSON-encoded form.
class ActiveSupport::JSON::Variable < String
def encode_json(encoder) self end
end
class TrueClass
JSON_VALUE = ActiveSupport::JSON::Variable.new('true').freeze
def dump_json(options) JSON_VALUE end
end
class FalseClass
JSON_VALUE = ActiveSupport::JSON::Variable.new('false').freeze
def dump_json(options) JSON_VALUE end
end
class NilClass
JSON_VALUE = ActiveSupport::JSON::Variable.new('null').freeze
def dump_json(options) JSON_VALUE end
end
class String
def encode_json(encoder) encoder.escape(self) end
end
class Symbol
def dump_json(options) to_s end
end
class Numeric
def encode_json(encoder) to_s end
end
class Regexp
def encode_json(encoder) inspect end
end
module Enumerable
def encode_json(encoder) "[#{map { |v| encoder.encode(v) } * ','}]" end
end
class Hash
def dump_json(options)
if options
if attrs = options[:only]
slice(*Array.wrap(attrs))
elsif attrs = options[:except]
except(*Array.wrap(attrs))
end
else
super
end
end
def encode_json(encoder)
"{#{map { |k,v| "#{encoder.encode(k.to_s)}:#{encoder.encode(v)}" } * ','}}"
end
end
class Time
def dump_json(options)
if ActiveSupport.use_standard_json_time_format
xmlschema
else
%(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
end
end
end
class Date
def dump_json(options)
if ActiveSupport.use_standard_json_time_format
strftime("%Y-%m-%d")
else
strftime("%Y/%m/%d")
end
end
end
class DateTime
def dump_json(options)
if ActiveSupport.use_standard_json_time_format
xmlschema
else
strftime('%Y/%m/%d %H:%M:%S %z')
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment