Permalink
Browse files

JSON: split encoding and coercion

  • Loading branch information...
1 parent 4a78dae commit a69ee11968bfd3113e32feea26f7c39c0ba51d8a @jeremy jeremy committed Jun 8, 2009
@@ -868,8 +868,7 @@ def to_xml(options={})
attributes.to_xml({:root => self.class.element_name}.merge(options))
end
- # Returns a JSON string representing the model. Some configuration is
- # available through +options+.
+ # Coerces to a hash for JSON encoding.
#
# ==== Options
# The +options+ are passed to the +to_json+ method on each
@@ -893,8 +892,8 @@ def to_xml(options={})
#
# person.to_json(:except => ["first_name"])
# # => {"last_name": "Smith"}
- def to_json(options={})
- ActiveSupport::JSON.encode(attributes, options)
+ def as_json(options = nil)
+ attributes.as_json(options)
end
# For compatibility with ActiveSupport::JSON.encode
View
@@ -1,5 +1,7 @@
*2.3.3 (pending)*
+* JSON: +Object#to_json+ calls +as_json+ to coerce itself into something natively encodable like +Hash+, +Integer+, or +String+. Override +as_json+ instead of +to_json+ so you're JSON-library-agnostic. [Jeremy Kemper]
+
* Allow MemCacheStore to be initialized with a MemCache-like object instead of addresses and options [Bryan Helmkamp]
@@ -1,83 +1,2 @@
-module ActiveSupport
- # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format.
- mattr_accessor :use_standard_json_time_format
- # Look for and parse json strings that look like ISO 8601 times.
- mattr_accessor :parse_json_times
-
- module JSON
- # matches YAML-formatted dates
- DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
-
- module Encoding #:nodoc:
- mattr_accessor :escape_regex
-
- ESCAPED_CHARS = {
- "\010" => '\b',
- "\f" => '\f',
- "\n" => '\n',
- "\r" => '\r',
- "\t" => '\t',
- '"' => '\"',
- '\\' => '\\\\',
- '>' => '\u003E',
- '<' => '\u003C',
- '&' => '\u0026'
- }
-
- def self.escape(string)
- json = '"' + string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] }
- json.force_encoding('ascii-8bit') if respond_to?(:force_encoding)
- json.gsub(/([\xC0-\xDF][\x80-\xBF]|
- [\xE0-\xEF][\x80-\xBF]{2}|
- [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s|
- s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&')
- } + '"'
- end
- end
-
- class << self
- delegate :decode, :to => :backend
-
- def backend
- @backend || begin
- self.backend = "Yaml"
- @backend
- end
- end
-
- def backend=(name)
- if name.is_a?(Module)
- @backend = name
- else
- require "active_support/json/backends/#{name.to_s.downcase}.rb"
- @backend = ActiveSupport::JSON::Backends::const_get(name)
- end
- end
-
- def with_backend(name)
- old_backend, self.backend = backend, name
- yield
- ensure
- self.backend = old_backend
- end
- end
- end
-
- class << self
- attr_reader :escape_html_entities_in_json
-
- def escape_html_entities_in_json=(value)
- ActiveSupport::JSON::Encoding.escape_regex = \
- if value
- /[\010\f\n\r\t"\\><&]/
- else
- /[\010\f\n\r\t"\\]/
- end
- @escape_html_entities_in_json = value
- end
- end
-end
-
-ActiveSupport.escape_html_entities_in_json = true
-
+require 'active_support/json/decoding'
require 'active_support/json/encoding'
@@ -0,0 +1,35 @@
+require 'active_support/core_ext/module/attribute_accessors'
+
+module ActiveSupport
+ # Look for and parse json strings that look like ISO 8601 times.
+ mattr_accessor :parse_json_times
+
+ module JSON
+ class << self
+ delegate :decode, :to => :backend
+
+ def backend
+ @backend || begin
+ self.backend = "Yaml"
+ @backend
+ end
+ end
+
+ def backend=(name)
+ if name.is_a?(Module)
+ @backend = name
+ else
+ require "active_support/json/backends/#{name.to_s.downcase}.rb"
+ @backend = ActiveSupport::JSON::Backends::const_get(name)
+ end
+ end
+
+ def with_backend(name)
+ old_backend, self.backend = backend, name
+ yield
+ ensure
+ self.backend = old_backend
+ end
+ end
+ end
+end
@@ -1,21 +1,22 @@
class Date
- # Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the
- # ISO 8601 format is used.
+ # Coerces the date to a string for JSON encoding.
+ #
+ # ISO 8601 format is used if ActiveSupport::JSON::Encoding.use_standard_json_time_format is set.
#
# ==== Examples
#
- # # With ActiveSupport.use_standard_json_time_format = true
+ # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
# Date.new(2005,2,1).to_json
# # => "2005-02-01"
#
- # # With ActiveSupport.use_standard_json_time_format = false
+ # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
# Date.new(2005,2,1).to_json
# # => "2005/02/01"
- def to_json(options = nil)
- if ActiveSupport.use_standard_json_time_format
- %("#{strftime("%Y-%m-%d")}")
+ def as_json(options = nil)
+ if ActiveSupport::JSON::Encoding.use_standard_json_time_format
+ strftime("%Y-%m-%d")
else
- %("#{strftime("%Y/%m/%d")}")
+ strftime("%Y/%m/%d")
end
end
end
@@ -1,21 +1,22 @@
class DateTime
- # Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the
- # ISO 8601 format is used.
+ # Coerces the datetime to a string for JSON encoding.
+ #
+ # ISO 8601 format is used if ActiveSupport::JSON::Encoding.use_standard_json_time_format is set.
#
# ==== Examples
#
- # # With ActiveSupport.use_standard_json_time_format = true
+ # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
# DateTime.civil(2005,2,1,15,15,10).to_json
# # => "2005-02-01T15:15:10+00:00"
#
- # # With ActiveSupport.use_standard_json_time_format = false
+ # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
# DateTime.civil(2005,2,1,15,15,10).to_json
# # => "2005/02/01 15:15:10 +0000"
- def to_json(options = nil)
- if ActiveSupport.use_standard_json_time_format
- xmlschema.inspect
+ def as_json(options = nil)
+ if ActiveSupport::JSON::Encoding.use_standard_json_time_format
+ xmlschema
else
- strftime('"%Y/%m/%d %H:%M:%S %z"')
+ strftime('%Y/%m/%d %H:%M:%S %z')
end
end
end
@@ -1,18 +1,17 @@
module Enumerable
- # Returns a JSON string representing the enumerable. Any +options+
- # given will be passed on to its elements. For example:
- #
- # users = User.find(:all)
- # # => users.to_json(:only => :name)
- #
- # will pass the <tt>:only => :name</tt> option to each user.
- def to_json(options = nil) #:nodoc:
- to_a.to_json(options)
+ # Coerces the enumerable to an array for JSON encoding.
+ def as_json(options = nil) #:nodoc:
+ to_a
end
end
class Array
+ # Returns a JSON string representing the Array. +options+ are passed to each element.
def to_json(options = nil) #:nodoc:
"[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ','}]"
end
+
+ def as_json(options = nil) #:nodoc:
+ self
+ end
end
@@ -1,5 +1,7 @@
class FalseClass
- def to_json(options = nil) #:nodoc:
- 'false'
+ AS_JSON = ActiveSupport::JSON::Variable.new('false').freeze
+
+ def as_json(options = nil) #:nodoc:
+ AS_JSON
end
end
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/wrapper'
+
class Hash
# Returns a JSON string representing the hash.
#
@@ -29,20 +31,26 @@ class Hash
# allowing the posts association in the User model to be converted to JSON
# as well.
def to_json(options = nil) #:nodoc:
- hash_keys = self.keys
-
- if options
- if except = options[:except]
- hash_keys = hash_keys - Array.wrap(except)
- elsif only = options[:only]
- hash_keys = hash_keys & Array.wrap(only)
- end
- end
+ hash = as_json(options)
result = '{'
- result << hash_keys.map do |key|
- "#{ActiveSupport::JSON.encode(key.to_s)}:#{ActiveSupport::JSON.encode(self[key], options)}"
+ result << hash.map do |key, value|
+ "#{ActiveSupport::JSON.encode(key.to_s)}:#{ActiveSupport::JSON.encode(value, options)}"
end * ','
result << '}'
end
+
+ def as_json(options = nil) #:nodoc:
+ if options
+ if attrs = options[:except]
+ except(*Array.wrap(attrs))
+ elsif attrs = options[:only]
+ slice(*Array.wrap(attrs))
+ else
+ self
+ end
+ else
+ self
+ end
+ end
end
@@ -1,5 +1,7 @@
class NilClass
- def to_json(options = nil) #:nodoc:
- 'null'
+ AS_JSON = ActiveSupport::JSON::Variable.new('null').freeze
+
+ def as_json(options = nil) #:nodoc:
+ AS_JSON
end
end
@@ -2,6 +2,10 @@ class Numeric
def to_json(options = nil) #:nodoc:
to_s
end
+
+ def as_json(options = nil) #:nodoc:
+ self
+ end
end
class Float
@@ -1,6 +1,10 @@
class Object
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def to_json(options = nil)
- ActiveSupport::JSON.encode(instance_values, options)
+ ActiveSupport::JSON.encode(as_json(options))
+ end
+
+ def as_json(options = nil)
+ instance_values
end
end
@@ -2,4 +2,8 @@ class Regexp
def to_json(options = nil) #:nodoc:
inspect
end
+
+ def as_json(options = nil) #:nodoc:
+ self
+ end
end
@@ -2,4 +2,8 @@ class String
def to_json(options = nil) #:nodoc:
ActiveSupport::JSON::Encoding.escape(self)
end
+
+ def as_json(options = nil) #:nodoc:
+ self
+ end
end
@@ -1,5 +1,5 @@
class Symbol
- def to_json(options = nil) #:nodoc:
- ActiveSupport::JSON.encode(to_s, options)
+ def as_json(options = nil) #:nodoc:
+ to_s
end
end
@@ -1,21 +1,22 @@
class Time
- # Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the
- # ISO 8601 format is used.
+ # Coerces the time to a string for JSON encoding.
+ #
+ # ISO 8601 format is used if ActiveSupport::JSON::Encoding.use_standard_json_time_format is set.
#
# ==== Examples
#
- # # With ActiveSupport.use_standard_json_time_format = true
+ # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
# Time.utc(2005,2,1,15,15,10).to_json
# # => "2005-02-01T15:15:10Z"
#
- # # With ActiveSupport.use_standard_json_time_format = false
+ # # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
# Time.utc(2005,2,1,15,15,10).to_json
# # => "2005/02/01 15:15:10 +0000"
- def to_json(options = nil)
- if ActiveSupport.use_standard_json_time_format
- xmlschema.inspect
+ def as_json(options = nil)
+ if ActiveSupport::JSON::Encoding.use_standard_json_time_format
+ xmlschema
else
- %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}")
+ %(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
end
end
end
@@ -1,5 +1,7 @@
class TrueClass
- def to_json(options = nil) #:nodoc:
- 'true'
+ AS_JSON = ActiveSupport::JSON::Variable.new('true').freeze
+
+ def as_json(options = nil) #:nodoc:
+ AS_JSON
end
end
Oops, something went wrong.

0 comments on commit a69ee11

Please sign in to comment.