diff options
Diffstat (limited to 'activesupport/lib/active_support/core_ext/object/json.rb')
-rw-r--r-- | activesupport/lib/active_support/core_ext/object/json.rb | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/activesupport/lib/active_support/core_ext/object/json.rb b/activesupport/lib/active_support/core_ext/object/json.rb new file mode 100644 index 0000000000..898c3f4307 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/json.rb @@ -0,0 +1,214 @@ +# Hack to load json gem first so we can overwrite its to_json. +require 'json' +require 'bigdecimal' +require 'active_support/core_ext/big_decimal/conversions' # for #to_s +require 'active_support/core_ext/hash/except' +require 'active_support/core_ext/hash/slice' +require 'active_support/core_ext/object/instance_variables' +require 'time' +require 'active_support/core_ext/time/conversions' +require 'active_support/core_ext/date_time/conversions' +require 'active_support/core_ext/date/conversions' + +# 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 do + # 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 + end +end + +class Object + def as_json(options = nil) #:nodoc: + if respond_to?(:to_hash) + to_hash.as_json(options) + else + instance_values.as_json(options) + end + end +end + +class Struct #:nodoc: + def as_json(options = nil) + Hash[members.zip(values)].as_json(options) + end +end + +class TrueClass + def as_json(options = nil) #:nodoc: + self + end + + def encode_json(encoder) #:nodoc: + to_s + end +end + +class FalseClass + def as_json(options = nil) #:nodoc: + self + end + + def encode_json(encoder) #:nodoc: + to_s + end +end + +class NilClass + def as_json(options = nil) #:nodoc: + self + end + + def encode_json(encoder) #:nodoc: + 'null' + end +end + +class String + def as_json(options = nil) #:nodoc: + self + end + + def encode_json(encoder) #:nodoc: + encoder.escape(self) + end +end + +class Symbol + def as_json(options = nil) #:nodoc: + to_s + end +end + +class Numeric + def as_json(options = nil) #:nodoc: + self + end + + def encode_json(encoder) #:nodoc: + to_s + end +end + +class Float + # Encoding Infinity or NaN to JSON should return "null". The default returns + # "Infinity" or "NaN" which are not valid JSON. + def as_json(options = nil) #:nodoc: + finite? ? self : nil + end +end + +class BigDecimal + # A BigDecimal would be naturally represented as a JSON number. Most libraries, + # however, parse non-integer JSON numbers directly as floats. Clients using + # those libraries would get in general a wrong number and no way to recover + # other than manually inspecting the string with the JSON code itself. + # + # That's why a JSON string is returned. The JSON literal is not numeric, but + # if the other end knows by contract that the data is supposed to be a + # BigDecimal, it still has the chance to post-process the string and get the + # real value. + # + # Use <tt>ActiveSupport.use_standard_json_big_decimal_format = true</tt> to + # override this behavior. + def as_json(options = nil) #:nodoc: + if finite? + ActiveSupport.encode_big_decimal_as_string ? to_s : self + else + nil + end + end +end + +class Regexp + def as_json(options = nil) #:nodoc: + to_s + end +end + +module Enumerable + def as_json(options = nil) #:nodoc: + to_a.as_json(options) + end +end + +class Range + def as_json(options = nil) #:nodoc: + to_s + end +end + +class Array + def as_json(options = nil) #:nodoc: + map { |v| v.as_json(options && options.dup) } + end + + def encode_json(encoder) #:nodoc: + "[#{map { |v| v.as_json.encode_json(encoder) } * ','}]" + end +end + +class Hash + def as_json(options = nil) #:nodoc: + # create a subset of the hash by applying :only or :except + subset = if options + if attrs = options[:only] + slice(*Array(attrs)) + elsif attrs = options[:except] + except(*Array(attrs)) + else + self + end + else + self + end + + Hash[subset.map { |k, v| [k.to_s, v.as_json(options && options.dup)] }] + end + + def encode_json(encoder) #:nodoc: + "{#{map { |k,v| "#{k.as_json.encode_json(encoder)}:#{v.as_json.encode_json(encoder)}" } * ','}}" + end +end + +class Time + def as_json(options = nil) #:nodoc: + if ActiveSupport.use_standard_json_time_format + xmlschema(3) + else + %(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}) + end + end +end + +class Date + def as_json(options = nil) #:nodoc: + if ActiveSupport.use_standard_json_time_format + strftime("%Y-%m-%d") + else + strftime("%Y/%m/%d") + end + end +end + +class DateTime + def as_json(options = nil) #:nodoc: + if ActiveSupport.use_standard_json_time_format + xmlschema(3) + else + strftime('%Y/%m/%d %H:%M:%S %z') + end + end +end + +class Process::Status + def as_json(options = nil) + { :exitstatus => exitstatus, :pid => pid } + end +end |