diff options
author | Joshua Peek <josh@joshpeek.com> | 2009-05-29 16:06:21 -0500 |
---|---|---|
committer | Joshua Peek <josh@joshpeek.com> | 2009-05-29 16:06:21 -0500 |
commit | 69742ca8fa05509f7d7c5512cb6d8e002ecb3ab3 (patch) | |
tree | 044c2131cc87d21ee54027511aae2b7f2d2ae26a /activesupport/lib/active_support/json | |
parent | 5f3f100ce2d689480da85abc88e5e940cf90189e (diff) | |
parent | 5ec2c7dc29b36d85b2658465b8a979deb0529d7e (diff) | |
download | rails-69742ca8fa05509f7d7c5512cb6d8e002ecb3ab3.tar.gz rails-69742ca8fa05509f7d7c5512cb6d8e002ecb3ab3.tar.bz2 rails-69742ca8fa05509f7d7c5512cb6d8e002ecb3ab3.zip |
Merge branch 'master' into active_model
Conflicts:
activemodel/lib/active_model/core.rb
activemodel/test/cases/state_machine/event_test.rb
activemodel/test/cases/state_machine/state_transition_test.rb
activerecord/lib/active_record/validations.rb
activerecord/test/cases/validations/i18n_validation_test.rb
activeresource/lib/active_resource.rb
activeresource/test/abstract_unit.rb
Diffstat (limited to 'activesupport/lib/active_support/json')
18 files changed, 289 insertions, 251 deletions
diff --git a/activesupport/lib/active_support/json/backends/jsongem.rb b/activesupport/lib/active_support/json/backends/jsongem.rb new file mode 100644 index 0000000000..649e6301d1 --- /dev/null +++ b/activesupport/lib/active_support/json/backends/jsongem.rb @@ -0,0 +1,41 @@ +require 'json' unless defined?(JSON) + +module ActiveSupport + module JSON + ParseError = ::JSON::ParserError unless const_defined?(:ParseError) + + module Backends + module JSONGem + extend self + + # Parses a JSON string or IO and convert it into an object + def decode(json) + if json.respond_to?(:read) + json = json.read + end + data = ::JSON.parse(json) + if ActiveSupport.parse_json_times + convert_dates_from(data) + else + data + end + end + + private + def convert_dates_from(data) + case data + when DATE_REGEX + DateTime.parse(data) + when Array + data.map! { |d| convert_dates_from(d) } + when Hash + data.each do |key, value| + data[key] = convert_dates_from(value) + end + else data + end + end + end + end + end +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb new file mode 100644 index 0000000000..667016f45d --- /dev/null +++ b/activesupport/lib/active_support/json/backends/yaml.rb @@ -0,0 +1,88 @@ +require 'active_support/core_ext/string/starts_ends_with' + +module ActiveSupport + module JSON + unless const_defined?(:ParseError) + class ParseError < StandardError + end + end + + module Backends + module Yaml + extend self + + # Parses a JSON string or IO and converts it into an object + def decode(json) + if json.respond_to?(:read) + json = json.read + end + YAML.load(convert_json_to_yaml(json)) + rescue ArgumentError => e + raise ParseError, "Invalid JSON string" + end + + protected + # Ensure that ":" and "," are always followed by a space + def convert_json_to_yaml(json) #:nodoc: + require 'strscan' unless defined? ::StringScanner + scanner, quoting, marks, pos, times = ::StringScanner.new(json), false, [], nil, [] + while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/) + case char = scanner[1] + when '"', "'" + if !quoting + quoting = char + pos = scanner.pos + elsif quoting == char + if json[pos..scanner.pos-2] =~ DATE_REGEX + # found a date, track the exact positions of the quotes so we can remove them later. + # oh, and increment them for each current mark, each one is an extra padded space that bumps + # the position in the final YAML output + total_marks = marks.size + times << pos+total_marks << scanner.pos+total_marks + end + quoting = false + end + when ":","," + marks << scanner.pos - 1 unless quoting + end + end + + if marks.empty? + json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do + ustr = $1 + if ustr.start_with?('u') + [ustr[1..-1].to_i(16)].pack("U") + elsif ustr == '\\' + '\\\\' + else + ustr + end + end + else + left_pos = [-1].push(*marks) + right_pos = marks << scanner.pos + scanner.rest_size + output = [] + left_pos.each_with_index do |left, i| + scanner.pos = left.succ + output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do + ustr = $1 + if ustr.start_with?('u') + [ustr[1..-1].to_i(16)].pack("U") + elsif ustr == '\\' + '\\\\' + else + ustr + end + end + end + output = output * " " + + times.each { |i| output[i-1] = ' ' } + output.gsub!(/\\\//, '/') + output + end + end + end + end + end +end diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb deleted file mode 100644 index c66500aa9c..0000000000 --- a/activesupport/lib/active_support/json/decoding.rb +++ /dev/null @@ -1,84 +0,0 @@ -autoload :YAML, 'yaml' unless defined? YAML -autoload :StringScanner, 'strscan' unless defined? StringScanner - -require 'active_support/core_ext/string/starts_ends_with' - -module ActiveSupport - module JSON - class ParseError < StandardError - end - - class << self - # Converts a JSON string into a Ruby object. - def decode(json) - YAML.load(convert_json_to_yaml(json)) - rescue ArgumentError => e - raise ParseError, "Invalid JSON string" - end - - protected - # 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})?)?)$/ - - # Ensure that ":" and "," are always followed by a space - def convert_json_to_yaml(json) #:nodoc: - scanner, quoting, marks, pos, times = StringScanner.new(json), false, [], nil, [] - while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/) - case char = scanner[1] - when '"', "'" - if !quoting - quoting = char - pos = scanner.pos - elsif quoting == char - if json[pos..scanner.pos-2] =~ DATE_REGEX - # found a date, track the exact positions of the quotes so we can remove them later. - # oh, and increment them for each current mark, each one is an extra padded space that bumps - # the position in the final YAML output - total_marks = marks.size - times << pos+total_marks << scanner.pos+total_marks - end - quoting = false - end - when ":","," - marks << scanner.pos - 1 unless quoting - end - end - - if marks.empty? - json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do - ustr = $1 - if ustr.start_with?('u') - [ustr[1..-1].to_i(16)].pack("U") - elsif ustr == '\\' - '\\\\' - else - ustr - end - end - else - left_pos = [-1].push(*marks) - right_pos = marks << scanner.pos + scanner.rest_size - output = [] - left_pos.each_with_index do |left, i| - scanner.pos = left.succ - output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do - ustr = $1 - if ustr.start_with?('u') - [ustr[1..-1].to_i(16)].pack("U") - elsif ustr == '\\' - '\\\\' - else - ustr - end - end - end - output = output * " " - - times.each { |i| output[i-1] = ' ' } - output.gsub!(/\\\//, '/') - output - end - end - end - end -end diff --git a/activesupport/lib/active_support/json/encoders/date.rb b/activesupport/lib/active_support/json/encoders/date.rb index cc84de1388..9adb3c20e2 100644 --- a/activesupport/lib/active_support/json/encoders/date.rb +++ b/activesupport/lib/active_support/json/encoders/date.rb @@ -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. - # - # ==== Examples - # - # # With ActiveSupport.use_standard_json_time_format = true - # Date.new(2005,2,1).to_json - # # => "2005-02-01" - # - # # With ActiveSupport.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")}") - else - %("#{strftime("%Y/%m/%d")}") + private + # Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the + # ISO 8601 format is used. + # + # ==== Examples + # + # # With ActiveSupport.use_standard_json_time_format = true + # Date.new(2005,2,1).to_json + # # => "2005-02-01" + # + # # With ActiveSupport.use_standard_json_time_format = false + # Date.new(2005,2,1).to_json + # # => "2005/02/01" + def rails_to_json(*) + if ActiveSupport.use_standard_json_time_format + %("#{strftime("%Y-%m-%d")}") + else + %("#{strftime("%Y/%m/%d")}") + end end - end end diff --git a/activesupport/lib/active_support/json/encoders/date_time.rb b/activesupport/lib/active_support/json/encoders/date_time.rb index 6c85824105..3a29292b24 100644 --- a/activesupport/lib/active_support/json/encoders/date_time.rb +++ b/activesupport/lib/active_support/json/encoders/date_time.rb @@ -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. - # - # ==== Examples - # - # # With ActiveSupport.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 - # 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 - else - strftime('"%Y/%m/%d %H:%M:%S %z"') + private + # Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the + # ISO 8601 format is used. + # + # ==== Examples + # + # # With ActiveSupport.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 + # DateTime.civil(2005,2,1,15,15,10).to_json + # # => "2005/02/01 15:15:10 +0000" + def rails_to_json(*) + if ActiveSupport.use_standard_json_time_format + xmlschema.inspect + else + strftime('"%Y/%m/%d %H:%M:%S %z"') + end end - end end diff --git a/activesupport/lib/active_support/json/encoders/enumerable.rb b/activesupport/lib/active_support/json/encoders/enumerable.rb index 881b1d62c1..898990a59c 100644 --- a/activesupport/lib/active_support/json/encoders/enumerable.rb +++ b/activesupport/lib/active_support/json/encoders/enumerable.rb @@ -1,12 +1,13 @@ 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 = {}) #:nodoc: - "[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ', '}]" - end + private + # 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 rails_to_json(options = nil, *args) #:nodoc: + "[#{map { |value| ActiveSupport::JSON.encode(value, options, *args) } * ','}]" + end end diff --git a/activesupport/lib/active_support/json/encoders/false_class.rb b/activesupport/lib/active_support/json/encoders/false_class.rb index bf0844334b..eb975fe542 100644 --- a/activesupport/lib/active_support/json/encoders/false_class.rb +++ b/activesupport/lib/active_support/json/encoders/false_class.rb @@ -1,5 +1,6 @@ class FalseClass - def to_json(options = nil) #:nodoc: - 'false' - end + private + def rails_to_json(*) + 'false' + end end diff --git a/activesupport/lib/active_support/json/encoders/hash.rb b/activesupport/lib/active_support/json/encoders/hash.rb index d87b880743..4771484843 100644 --- a/activesupport/lib/active_support/json/encoders/hash.rb +++ b/activesupport/lib/active_support/json/encoders/hash.rb @@ -1,48 +1,51 @@ require 'active_support/core_ext/array/wrap' class Hash - # Returns a JSON string representing the hash. - # - # Without any +options+, the returned JSON string will include all - # the hash keys. For example: - # - # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json - # # => {"name": "Konata Izumi", "1": 2, "age": 16} - # - # The keys in the JSON string are unordered due to the nature of hashes. - # - # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the - # attributes included, and will accept 1 or more hash keys to include/exclude. - # - # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:only => [:name, 'age']) - # # => {"name": "Konata Izumi", "age": 16} - # - # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:except => 1) - # # => {"name": "Konata Izumi", "age": 16} - # - # The +options+ also filter down to any hash values. This is particularly - # useful for converting hashes containing ActiveRecord objects or any object - # that responds to options in their <tt>to_json</tt> method. For example: - # - # users = User.find(:all) - # { :users => users, :count => users.size }.to_json(:include => :posts) - # - # would pass the <tt>:include => :posts</tt> option to <tt>users</tt>, - # allowing the posts association in the User model to be converted to JSON - # as well. - def to_json(options = {}) #:nodoc: - hash_keys = self.keys + private + # Returns a JSON string representing the hash. + # + # Without any +options+, the returned JSON string will include all + # the hash keys. For example: + # + # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json + # # => {"name": "Konata Izumi", "1": 2, "age": 16} + # + # The keys in the JSON string are unordered due to the nature of hashes. + # + # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the + # attributes included, and will accept 1 or more hash keys to include/exclude. + # + # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:only => [:name, 'age']) + # # => {"name": "Konata Izumi", "age": 16} + # + # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:except => 1) + # # => {"name": "Konata Izumi", "age": 16} + # + # The +options+ also filter down to any hash values. This is particularly + # useful for converting hashes containing ActiveRecord objects or any object + # that responds to options in their <tt>to_json</tt> method. For example: + # + # users = User.find(:all) + # { :users => users, :count => users.size }.to_json(:include => :posts) + # + # would pass the <tt>:include => :posts</tt> option to <tt>users</tt>, + # allowing the posts association in the User model to be converted to JSON + # as well. + def rails_to_json(options = nil, *args) #:nodoc: + hash_keys = self.keys - if except = options[:except] - hash_keys = hash_keys - Array.wrap(except) - elsif only = options[:only] - hash_keys = hash_keys & Array.wrap(only) - end + 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 - result = '{' - result << hash_keys.map do |key| - "#{ActiveSupport::JSON.encode(key.to_s)}: #{ActiveSupport::JSON.encode(self[key], options)}" - end * ', ' - result << '}' - end + result = '{' + result << hash_keys.map do |key| + "#{ActiveSupport::JSON.encode(key.to_s)}:#{ActiveSupport::JSON.encode(self[key], options, *args)}" + end * ',' + result << '}' + end end diff --git a/activesupport/lib/active_support/json/encoders/nil_class.rb b/activesupport/lib/active_support/json/encoders/nil_class.rb index 4763471ac4..8c51dba384 100644 --- a/activesupport/lib/active_support/json/encoders/nil_class.rb +++ b/activesupport/lib/active_support/json/encoders/nil_class.rb @@ -1,5 +1,6 @@ class NilClass - def to_json(options = nil) #:nodoc: - 'null' - end + private + def rails_to_json(*) + 'null' + end end diff --git a/activesupport/lib/active_support/json/encoders/numeric.rb b/activesupport/lib/active_support/json/encoders/numeric.rb index 38713fb369..c7cd0df1d7 100644 --- a/activesupport/lib/active_support/json/encoders/numeric.rb +++ b/activesupport/lib/active_support/json/encoders/numeric.rb @@ -1,5 +1,6 @@ class Numeric - def to_json(options = nil) #:nodoc: - to_s - end + private + def rails_to_json(*) + to_s + end end diff --git a/activesupport/lib/active_support/json/encoders/object.rb b/activesupport/lib/active_support/json/encoders/object.rb index 0475967aee..9cc12d91ac 100644 --- a/activesupport/lib/active_support/json/encoders/object.rb +++ b/activesupport/lib/active_support/json/encoders/object.rb @@ -2,7 +2,12 @@ require 'active_support/core_ext/object/instance_variables' class Object # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. - def to_json(options = {}) - ActiveSupport::JSON.encode(instance_values, options) + def to_json(options = nil) + ActiveSupport::JSON.encode(self, options) end + + private + def rails_to_json(*args) + ActiveSupport::JSON.encode(instance_values, *args) + end end diff --git a/activesupport/lib/active_support/json/encoders/regexp.rb b/activesupport/lib/active_support/json/encoders/regexp.rb index b6116b70b5..ee42db4d02 100644 --- a/activesupport/lib/active_support/json/encoders/regexp.rb +++ b/activesupport/lib/active_support/json/encoders/regexp.rb @@ -1,5 +1,6 @@ class Regexp - def to_json(options = nil) #:nodoc: - inspect - end + private + def rails_to_json(*) + inspect + end end diff --git a/activesupport/lib/active_support/json/encoders/string.rb b/activesupport/lib/active_support/json/encoders/string.rb index 5ef797955a..4a6b21c1c0 100644 --- a/activesupport/lib/active_support/json/encoders/string.rb +++ b/activesupport/lib/active_support/json/encoders/string.rb @@ -1,36 +1,6 @@ -module ActiveSupport - module JSON - module Encoding - mattr_accessor :escape_regex - - ESCAPED_CHARS = { - "\010" => '\b', - "\f" => '\f', - "\n" => '\n', - "\r" => '\r', - "\t" => '\t', - '"' => '\"', - '\\' => '\\\\', - '>' => '\u003E', - '<' => '\u003C', - '&' => '\u0026' - } - end - end -end - -ActiveSupport.escape_html_entities_in_json = true - class String - def to_json(options = nil) #:nodoc: - json = '"' + gsub(ActiveSupport::JSON::Encoding.escape_regex) { |s| - ActiveSupport::JSON::Encoding::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 + private + def rails_to_json(*) + ActiveSupport::JSON::Encoding.escape(self) + end end diff --git a/activesupport/lib/active_support/json/encoders/symbol.rb b/activesupport/lib/active_support/json/encoders/symbol.rb index 485112f97c..d575350a4e 100644 --- a/activesupport/lib/active_support/json/encoders/symbol.rb +++ b/activesupport/lib/active_support/json/encoders/symbol.rb @@ -1,5 +1,6 @@ class Symbol - def to_json(options = {}) #:nodoc: - ActiveSupport::JSON.encode(to_s, options) - end + private + def rails_to_json(*args) + ActiveSupport::JSON.encode(to_s, *args) + end end diff --git a/activesupport/lib/active_support/json/encoders/time.rb b/activesupport/lib/active_support/json/encoders/time.rb index f45a0059e8..d434b9aace 100644 --- a/activesupport/lib/active_support/json/encoders/time.rb +++ b/activesupport/lib/active_support/json/encoders/time.rb @@ -1,21 +1,24 @@ +require 'active_support/core_ext/time/conversions' + 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. - # - # ==== Examples - # - # # With ActiveSupport.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 - # 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 - else - %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") + private + # Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the + # ISO 8601 format is used. + # + # ==== Examples + # + # # With ActiveSupport.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 + # Time.utc(2005,2,1,15,15,10).to_json + # # => "2005/02/01 15:15:10 +0000" + def rails_to_json(*) + if ActiveSupport.use_standard_json_time_format + xmlschema.inspect + else + %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") + end end - end end diff --git a/activesupport/lib/active_support/json/encoders/true_class.rb b/activesupport/lib/active_support/json/encoders/true_class.rb index 037d812b3f..bc25a6db78 100644 --- a/activesupport/lib/active_support/json/encoders/true_class.rb +++ b/activesupport/lib/active_support/json/encoders/true_class.rb @@ -1,5 +1,6 @@ class TrueClass - def to_json(options = nil) #:nodoc: - 'true' - end + private + def rails_to_json(*) + 'true' + end end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index aaaa3cdfd2..5fefe5b88b 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -4,11 +4,13 @@ module ActiveSupport end # Converts a Ruby object into a JSON string. - def self.encode(value, options = {}) - seen = (options[:seen] ||= []) - raise CircularReferenceError, 'object references itself' if seen.include?(value) + def self.encode(value, options = nil, seen = nil) + seen ||= [] + if seen.any? { |object| object.equal?(value) } + raise CircularReferenceError, 'object references itself' + end seen << value - value.send(:to_json, options) + value.__send__(:rails_to_json, options, seen) ensure seen.pop end diff --git a/activesupport/lib/active_support/json/variable.rb b/activesupport/lib/active_support/json/variable.rb index 7fd23b0a9e..daa7449b71 100644 --- a/activesupport/lib/active_support/json/variable.rb +++ b/activesupport/lib/active_support/json/variable.rb @@ -2,9 +2,10 @@ module ActiveSupport module JSON # A string that returns itself as its JSON-encoded form. class Variable < String - def to_json(options=nil) - self - end + private + def rails_to_json(*) + self + end end end end |