mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Removed the Ruby encoder and switched to using the JSON gem
Got all the tests passing again. Support for `encode_json` has been removed (and consequently the ability to encode `BigDecimal`s as numbers, as mentioned in the previous commit). Install the `activesupport-json_encoder` gem to get it back.
This commit is contained in:
parent
4d02296cfb
commit
80e7552073
3 changed files with 74 additions and 82 deletions
|
@ -62,40 +62,24 @@ 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
|
||||
|
@ -108,10 +92,6 @@ class Numeric
|
|||
def as_json(options = nil) #:nodoc:
|
||||
self
|
||||
end
|
||||
|
||||
def encode_json(encoder) #:nodoc:
|
||||
to_s
|
||||
end
|
||||
end
|
||||
|
||||
class Float
|
||||
|
@ -159,10 +139,6 @@ class Array
|
|||
def as_json(options = nil) #:nodoc:
|
||||
map { |v| options ? v.as_json(options.dup) : v.as_json }
|
||||
end
|
||||
|
||||
def encode_json(encoder) #:nodoc:
|
||||
"[#{map { |v| v.as_json.encode_json(encoder) } * ','}]"
|
||||
end
|
||||
end
|
||||
|
||||
class Hash
|
||||
|
@ -182,10 +158,6 @@ class Hash
|
|||
|
||||
Hash[subset.map { |k, v| [k.to_s, options ? v.as_json(options.dup) : v.as_json] }]
|
||||
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
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#encoding: us-ascii
|
||||
|
||||
require 'active_support/core_ext/object/json'
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
|
||||
|
@ -21,72 +19,94 @@ module ActiveSupport
|
|||
end
|
||||
|
||||
module Encoding #:nodoc:
|
||||
class Encoder
|
||||
class Encoder #:nodoc:
|
||||
attr_reader :options
|
||||
|
||||
def initialize(options = nil)
|
||||
@options = options || {}
|
||||
end
|
||||
|
||||
# Encode the given object into a JSON string
|
||||
def encode(value)
|
||||
value.as_json(options.dup).encode_json(self)
|
||||
stringify jsonify value.as_json(options.dup)
|
||||
end
|
||||
|
||||
def escape(string)
|
||||
Encoding.escape(string)
|
||||
end
|
||||
private
|
||||
# Rails does more escaping than the JSON gem natively does (we
|
||||
# escape \u2028 and \u2029 and optionally >, <, & to work around
|
||||
# certain browser problems).
|
||||
ESCAPED_CHARS = {
|
||||
"\u2028" => '\u2028',
|
||||
"\u2029" => '\u2029',
|
||||
'>' => '\u003e',
|
||||
'<' => '\u003c',
|
||||
'&' => '\u0026',
|
||||
}
|
||||
|
||||
ESCAPE_REGEX_WITH_HTML_ENTITIES = /[\u2028\u2029><&]/u
|
||||
ESCAPE_REGEX_WITHOUT_HTML_ENTITIES = /[\u2028\u2029]/u
|
||||
|
||||
# This class wraps all the strings we see and does the extra escaping
|
||||
class EscapedString < String
|
||||
def to_json(*)
|
||||
if Encoding.escape_html_entities_in_json
|
||||
super.gsub ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
|
||||
else
|
||||
super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Mark these as private so we don't leak encoding-specific constructs
|
||||
private_constant :ESCAPED_CHARS, :ESCAPE_REGEX_WITH_HTML_ENTITIES,
|
||||
:ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, :EscapedString
|
||||
|
||||
# Recursively turn the given object into a "jsonified" Ruby data structure
|
||||
# that the JSON gem understands - i.e. we want only Hash, Array, String,
|
||||
# Numeric, true, false and nil in the final tree. Calls #as_json on it if
|
||||
# it's not from one of these base types.
|
||||
#
|
||||
# This allows developers to implement #as_json withouth having to worry
|
||||
# about what base types of objects they are allowed to return and having
|
||||
# to remember calling #as_json recursively.
|
||||
#
|
||||
# By default, the options hash is not passed to the children data structures
|
||||
# to avoid undesiarable result. Develoers must opt-in by implementing
|
||||
# custom #as_json methods (e.g. Hash#as_json and Array#as_json).
|
||||
def jsonify(value)
|
||||
if value.is_a?(Hash)
|
||||
Hash[value.map { |k, v| [jsonify(k), jsonify(v)] }]
|
||||
elsif value.is_a?(Array)
|
||||
value.map { |v| jsonify(v) }
|
||||
elsif value.is_a?(String)
|
||||
EscapedString.new(value)
|
||||
elsif value.is_a?(Numeric)
|
||||
value
|
||||
elsif value == true
|
||||
true
|
||||
elsif value == false
|
||||
false
|
||||
elsif value == nil
|
||||
nil
|
||||
else
|
||||
jsonify value.as_json
|
||||
end
|
||||
end
|
||||
|
||||
# Encode a "jsonified" Ruby data structure using the JSON gem
|
||||
def stringify(jsonified)
|
||||
::JSON.generate(jsonified, quirks_mode: true, max_nesting: false)
|
||||
end
|
||||
end
|
||||
|
||||
ESCAPED_CHARS = {
|
||||
"\x00" => '\u0000', "\x01" => '\u0001', "\x02" => '\u0002',
|
||||
"\x03" => '\u0003', "\x04" => '\u0004', "\x05" => '\u0005',
|
||||
"\x06" => '\u0006', "\x07" => '\u0007', "\x0B" => '\u000B',
|
||||
"\x0E" => '\u000E', "\x0F" => '\u000F', "\x10" => '\u0010',
|
||||
"\x11" => '\u0011', "\x12" => '\u0012', "\x13" => '\u0013',
|
||||
"\x14" => '\u0014', "\x15" => '\u0015', "\x16" => '\u0016',
|
||||
"\x17" => '\u0017', "\x18" => '\u0018', "\x19" => '\u0019',
|
||||
"\x1A" => '\u001A', "\x1B" => '\u001B', "\x1C" => '\u001C',
|
||||
"\x1D" => '\u001D', "\x1E" => '\u001E', "\x1F" => '\u001F',
|
||||
"\010" => '\b',
|
||||
"\f" => '\f',
|
||||
"\n" => '\n',
|
||||
"\xe2\x80\xa8" => '\u2028',
|
||||
"\xe2\x80\xa9" => '\u2029',
|
||||
"\r" => '\r',
|
||||
"\t" => '\t',
|
||||
'"' => '\"',
|
||||
'\\' => '\\\\',
|
||||
'>' => '\u003E',
|
||||
'<' => '\u003C',
|
||||
'&' => '\u0026',
|
||||
"#{0xe2.chr}#{0x80.chr}#{0xa8.chr}" => '\u2028',
|
||||
"#{0xe2.chr}#{0x80.chr}#{0xa9.chr}" => '\u2029',
|
||||
}
|
||||
|
||||
class << self
|
||||
# If true, use ISO 8601 format for dates and times. Otherwise, fall back
|
||||
# to the Active Support legacy format.
|
||||
attr_accessor :use_standard_json_time_format
|
||||
|
||||
attr_accessor :escape_regex
|
||||
attr_reader :escape_html_entities_in_json
|
||||
|
||||
def escape_html_entities_in_json=(value)
|
||||
self.escape_regex = \
|
||||
if @escape_html_entities_in_json = value
|
||||
/\xe2\x80\xa8|\xe2\x80\xa9|[\x00-\x1F"\\><&]/
|
||||
else
|
||||
/\xe2\x80\xa8|\xe2\x80\xa9|[\x00-\x1F"\\]/
|
||||
end
|
||||
end
|
||||
|
||||
def escape(string)
|
||||
string = string.encode(::Encoding::UTF_8, :undef => :replace).force_encoding(::Encoding::BINARY)
|
||||
json = string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] }
|
||||
json = %("#{json}")
|
||||
json.force_encoding(::Encoding::UTF_8)
|
||||
json
|
||||
end
|
||||
# If true, encode >, <, & as escaped unicode sequences (e.g. > as \u003e)
|
||||
# as a safety measure.
|
||||
attr_accessor :escape_html_entities_in_json
|
||||
|
||||
# Deprecate CircularReferenceError
|
||||
def const_missing(name)
|
||||
|
|
|
@ -66,11 +66,11 @@ class TestJSONEncoding < ActiveSupport::TestCase
|
|||
[ BigDecimal('0.0')/BigDecimal('0.0'), %(null) ],
|
||||
[ BigDecimal('2.5'), %("#{BigDecimal('2.5').to_s}") ]]
|
||||
|
||||
StringTests = [[ 'this is the <string>', %("this is the \\u003Cstring\\u003E")],
|
||||
StringTests = [[ 'this is the <string>', %("this is the \\u003cstring\\u003e")],
|
||||
[ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ],
|
||||
[ 'http://test.host/posts/1', %("http://test.host/posts/1")],
|
||||
[ "Control characters: \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\342\200\250\342\200\251",
|
||||
%("Control characters: \\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000B\\f\\r\\u000E\\u000F\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F\\u2028\\u2029") ]]
|
||||
%("Control characters: \\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f\\u2028\\u2029") ]]
|
||||
|
||||
ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ],
|
||||
[ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]]
|
||||
|
|
Loading…
Reference in a new issue