Skip to content
Browse files

Merge branch 'master' of github.com:rails/rails

* 'master' of github.com:rails/rails:
  Fix release instructions
  Raise an error when AS::JSON.decode is called with options
  Moved all JSON core extensions into core_ext/object/json
  • Loading branch information...
2 parents 1d75efe + 8c36ead commit e83989b5a79c264966ce07e5f4a1e160399505f6 @tenderlove tenderlove committed Oct 30, 2013
View
2 RELEASING_RAILS.rdoc
@@ -113,7 +113,7 @@ what to do in case anything goes wrong:
$ git tag -m'tagging rc release' v3.0.10.rc1
$ git push
$ git push --tags
- $ for i in $(ls dist); do gem push $i; done
+ $ for i in $(ls pkg); do gem push $i; done
=== Send Rails release announcements
View
4 activesupport/CHANGELOG.md
@@ -1,3 +1,7 @@
+* Calling ActiveSupport::JSON.decode with unsupported options now raises an error.
+
+ *Godfrey Chan*
+
* Support :unless_exist in FileStore
*Michael Grosser*
View
2 activesupport/lib/active_support/core_ext/object.rb
@@ -8,7 +8,7 @@
require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/object/instance_variables'
-require 'active_support/core_ext/object/to_json'
+require 'active_support/core_ext/object/json'
require 'active_support/core_ext/object/to_param'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/object/with_options'
View
216 activesupport/lib/active_support/core_ext/object/json.rb
@@ -0,0 +1,216 @@
+# Hack to load json gem first so we can overwrite its to_json.
+require 'json'
+
+# 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
+ else
+ instance_values
+ end
+ end
+end
+
+class Struct #:nodoc:
+ def as_json(options = nil)
+ Hash[members.zip(values)]
+ 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 breaks parsing the JSON. E.g. JSON.parse('[NaN]').
+ 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:
+ # use encoder as a proxy to call as_json on all elements, to protect from circular references
+ encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
+ map { |v| encoder.as_json(v, options) }
+ end
+
+ def encode_json(encoder) #:nodoc:
+ # we assume here that the encoder has already run as_json on self and the elements, so we run encode_json directly
+ "[#{map { |v| v.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
+
+ # use encoder as a proxy to call as_json on all values in the subset, to protect from circular references
+ encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
+ Hash[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }]
+ end
+
+ def encode_json(encoder) #:nodoc:
+ # values are encoded with use_options = false, because we don't want hash representations from ActiveModel to be
+ # processed once again with as_json with options, as this could cause unexpected results (i.e. missing fields);
+
+ # on the other hand, we need to run as_json on the elements, because the model representation may contain fields
+ # like Time/Date in their original (not jsonified) form, etc.
+
+ "{#{map { |k,v| "#{encoder.encode(k.to_s)}:#{encoder.encode(v, false)}" } * ','}}"
+ end
+end
+
+class Time
+ def as_json(options = nil) #:nodoc:
+ 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 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
+ 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
View
30 activesupport/lib/active_support/core_ext/object/to_json.rb
@@ -1,27 +1,5 @@
-# Hack to load json gem first so we can overwrite its to_json.
-begin
- require 'json'
-rescue LoadError
-end
+ActiveSupport::Deprecation.warn 'You have required `active_support/core_ext/object/to_json`. ' \
+ 'This file will be removed in Rails 4.2. You should require `active_support/core_ext/object/json` ' \
+ 'instead.'
-# 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
-
-module Process
- class Status
- def as_json(options = nil)
- { :exitstatus => exitstatus, :pid => pid }
- end
- end
-end
+require 'active_support/core_ext/object/json'
View
9 activesupport/lib/active_support/json/decoding.rb
@@ -14,7 +14,14 @@ class << self
# ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
# => {"team" => "rails", "players" => "36"}
def decode(json, options = {})
- data = ::JSON.parse(json, options.merge(create_additions: false, quirks_mode: true))
+ if options.present?
+ raise ArgumentError, "In Rails 4.1, ActiveSupport::JSON.decode no longer " \
+ "accepts an options hash for MultiJSON. MultiJSON reached its end of life " \
+ "and has been removed."
+ end
+
+ data = ::JSON.parse(json, quirks_mode: true)
+
if ActiveSupport.parse_json_times
convert_dates_from(data)
else
View
196 activesupport/lib/active_support/json/encoding.rb
@@ -1,6 +1,6 @@
#encoding: us-ascii
-require 'active_support/core_ext/object/to_json'
+require 'active_support/core_ext/object/json'
require 'active_support/core_ext/module/delegation'
require 'bigdecimal'
@@ -148,197 +148,3 @@ def escape(string)
end
end
end
-
-class Object
- def as_json(options = nil) #:nodoc:
- if respond_to?(:to_hash)
- to_hash
- else
- instance_values
- end
- end
-end
-
-class Struct #:nodoc:
- def as_json(options = nil)
- Hash[members.zip(values)]
- 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 breaks parsing the JSON. E.g. JSON.parse('[NaN]').
- 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:
- # use encoder as a proxy to call as_json on all elements, to protect from circular references
- encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
- map { |v| encoder.as_json(v, options) }
- end
-
- def encode_json(encoder) #:nodoc:
- # we assume here that the encoder has already run as_json on self and the elements, so we run encode_json directly
- "[#{map { |v| v.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
-
- # use encoder as a proxy to call as_json on all values in the subset, to protect from circular references
- encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options)
- Hash[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }]
- end
-
- def encode_json(encoder) #:nodoc:
- # values are encoded with use_options = false, because we don't want hash representations from ActiveModel to be
- # processed once again with as_json with options, as this could cause unexpected results (i.e. missing fields);
-
- # on the other hand, we need to run as_json on the elements, because the model representation may contain fields
- # like Time/Date in their original (not jsonified) form, etc.
-
- "{#{map { |k,v| "#{encoder.encode(k.to_s)}:#{encoder.encode(v, false)}" } * ','}}"
- end
-end
-
-class Time
- def as_json(options = nil) #:nodoc:
- 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 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
- else
- strftime('%Y/%m/%d %H:%M:%S %z')
- end
- end
-end
View
9 activesupport/test/core_ext/object/json_test.rb
@@ -0,0 +1,9 @@
+require 'abstract_unit'
+
+class JsonTest < ActiveSupport::TestCase
+ # See activesupport/test/json/encoding_test.rb for JSON encoding tests
+
+ def test_deprecated_require_to_json_rb
+ assert_deprecated { require 'active_support/core_ext/object/to_json' }
+ end
+end
View
6 activesupport/test/json/decoding_test.rb
@@ -98,10 +98,8 @@ def test_failed_json_decoding
assert_raise(ActiveSupport::JSON.parse_error) { ActiveSupport::JSON.decode(%()) }
end
- def test_cannot_force_json_unmarshalling
- encodeded = %q({"json_class":"TestJSONDecoding::Foo"})
- decodeded = {"json_class"=>"TestJSONDecoding::Foo"}
- assert_equal decodeded, ActiveSupport::JSON.decode(encodeded, create_additions: true)
+ def test_cannot_pass_unsupported_options
+ assert_raise(ArgumentError) { ActiveSupport::JSON.decode("", create_additions: true) }
end
end
View
8 activesupport/test/json/encoding_test.rb
@@ -1,4 +1,5 @@
# encoding: utf-8
+require 'securerandom'
require 'abstract_unit'
require 'active_support/core_ext/string/inflections'
require 'active_support/json'
@@ -96,6 +97,13 @@ def sorted_json(json)
end
end
+ def test_process_status
+ # There doesn't seem to be a good way to get a handle on a Process::Status object without actually
+ # creating a child process, hence this to populate $?
+ system("not_a_real_program_#{SecureRandom.hex}")
+ assert_equal %({"exitstatus":#{$?.exitstatus},"pid":#{$?.pid}}), ActiveSupport::JSON.encode($?)
+ end
+
def test_hash_encoding
assert_equal %({\"a\":\"b\"}), ActiveSupport::JSON.encode(:a => :b)
assert_equal %({\"a\":1}), ActiveSupport::JSON.encode('a' => 1)
View
2 activesupport/test/ordered_hash_test.rb
@@ -1,6 +1,6 @@
require 'abstract_unit'
require 'active_support/json'
-require 'active_support/core_ext/object/to_json'
+require 'active_support/core_ext/object/json'
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/array/extract_options'
View
6 guides/source/active_support_core_extensions.md
@@ -421,11 +421,9 @@ NOTE: Defined in `active_support/core_ext/object/with_options.rb`.
### JSON support
-Active Support provides a better implementation of `to_json` than the +json+ gem ordinarily provides for Ruby objects. This is because some classes, like +Hash+ and +OrderedHash+ needs special handling in order to provide a proper JSON representation.
+Active Support provides a better implementation of `to_json` than the +json+ gem ordinarily provides for Ruby objects. This is because some classes, like +Hash+, +OrderedHash+ and +Process::Status+ needs special handling in order to provide a proper JSON representation.
-Active Support also provides an implementation of `as_json` for the <tt>Process::Status</tt> class.
-
-NOTE: Defined in `active_support/core_ext/object/to_json.rb`.
+NOTE: Defined in `active_support/core_ext/object/json.rb`.
### Instance Variables

0 comments on commit e83989b

Please sign in to comment.
Something went wrong with that request. Please try again.