mirror of
https://github.com/github/rails.git
synced 2026-01-13 00:28:26 -05:00
Compare commits
21 Commits
2.3.14.git
...
upstream-e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec99b40364 | ||
|
|
84420c7f12 | ||
|
|
c57e85fd13 | ||
|
|
2eca011798 | ||
|
|
f6cf01337f | ||
|
|
0ad86343c6 | ||
|
|
42524c2bf1 | ||
|
|
46f1ddbff9 | ||
|
|
b18f5c9af1 | ||
|
|
18e9b2ffc9 | ||
|
|
9ec3637bc5 | ||
|
|
ba9248e6e3 | ||
|
|
a27559cddf | ||
|
|
e786726603 | ||
|
|
a1d2a22047 | ||
|
|
d43ecd5b32 | ||
|
|
61359bf6ad | ||
|
|
a2beda1177 | ||
|
|
52c895d565 | ||
|
|
74f90612ec | ||
|
|
a6eb61b7e4 |
@@ -31,7 +31,6 @@ rescue LoadError
|
||||
end
|
||||
end
|
||||
|
||||
gem 'rack', '~> 1.1'
|
||||
require 'rack'
|
||||
require 'action_controller/cgi_ext'
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ module ActionView
|
||||
# escape_once("<< Accept & Checkout")
|
||||
# # => "<< Accept & Checkout"
|
||||
def escape_once(html)
|
||||
ActiveSupport::Multibyte.clean(html.to_s).gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] }
|
||||
ERB::Util.html_escape_once(html)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -8,8 +8,8 @@ require 'yaml'
|
||||
require 'stringio'
|
||||
require 'test/unit'
|
||||
|
||||
gem 'mocha', '>= 0.9.7'
|
||||
require 'mocha'
|
||||
gem 'mocha', '>= 0.13.1'
|
||||
require 'mocha/setup'
|
||||
|
||||
begin
|
||||
require 'ruby-debug'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
require 'rubygems'
|
||||
require 'test/unit'
|
||||
|
||||
gem 'mocha', '>= 0.9.3'
|
||||
require 'mocha'
|
||||
gem 'mocha', '>= 0.13.1'
|
||||
require 'mocha/setup'
|
||||
|
||||
require 'active_model'
|
||||
require 'active_model/state_machine'
|
||||
|
||||
@@ -49,7 +49,7 @@ module ActiveRecord
|
||||
alias_method :proxy_respond_to?, :respond_to?
|
||||
alias_method :proxy_extend, :extend
|
||||
delegate :to_param, :to => :proxy_target
|
||||
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
|
||||
instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^respond_to_missing|^object_id$)/ }
|
||||
|
||||
def initialize(owner, reflection)
|
||||
@owner, @reflection = owner, reflection
|
||||
|
||||
@@ -34,17 +34,12 @@ module ActiveRecord
|
||||
options[:readonly] = finding_with_ambiguous_select?(options[:select] || @reflection.options[:select])
|
||||
options[:select] ||= (@reflection.options[:select] || '*')
|
||||
end
|
||||
|
||||
|
||||
def count_records
|
||||
load_target.size
|
||||
end
|
||||
|
||||
def insert_record(record, force = true, validate = true)
|
||||
if has_primary_key?
|
||||
raise ActiveRecord::ConfigurationError,
|
||||
"Primary key is not allowed in a has_and_belongs_to_many join table (#{@reflection.options[:join_table]})."
|
||||
end
|
||||
|
||||
if record.new_record?
|
||||
if force
|
||||
record.save!
|
||||
|
||||
@@ -80,9 +80,11 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
unless instance_method_already_implemented?("#{name}=")
|
||||
if create_time_zone_conversion_attribute?(name, column)
|
||||
if self.serialized_attributes[name]
|
||||
define_write_method_for_serialized_attribute(name)
|
||||
elsif create_time_zone_conversion_attribute?(name, column)
|
||||
define_write_method_for_time_zone_conversion(name)
|
||||
else
|
||||
else
|
||||
define_write_method(name.to_sym)
|
||||
end
|
||||
end
|
||||
@@ -184,7 +186,20 @@ module ActiveRecord
|
||||
def define_write_method(attr_name)
|
||||
evaluate_attribute_method attr_name, "def #{attr_name}=(new_value);write_attribute('#{attr_name}', new_value);end", "#{attr_name}="
|
||||
end
|
||||
|
||||
|
||||
# Defined for all serialized attributes. Disallows assigning already serialized YAML.
|
||||
def define_write_method_for_serialized_attribute(attr_name)
|
||||
method_body = <<-EOV
|
||||
def #{attr_name}=(value)
|
||||
if value.is_a?(String) and value =~ /^---/
|
||||
raise ActiveRecordError, "You tried to assign already serialized content to #{attr_name}. This is disabled due to security issues."
|
||||
end
|
||||
write_attribute(:#{attr_name}, value)
|
||||
end
|
||||
EOV
|
||||
evaluate_attribute_method attr_name, method_body, "#{attr_name}="
|
||||
end
|
||||
|
||||
# Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
|
||||
# This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
|
||||
def define_write_method_for_time_zone_conversion(attr_name)
|
||||
|
||||
@@ -1897,7 +1897,11 @@ module ActiveRecord #:nodoc:
|
||||
# end
|
||||
self.class_eval <<-EOS, __FILE__, __LINE__ + 1
|
||||
def self.#{method_id}(*args)
|
||||
options = args.extract_options!
|
||||
options = if args.length > #{attribute_names.size}
|
||||
args.extract_options!
|
||||
else
|
||||
{}
|
||||
end
|
||||
attributes = construct_attributes_from_arguments(
|
||||
[:#{attribute_names.join(',:')}],
|
||||
args
|
||||
@@ -2333,17 +2337,17 @@ module ActiveRecord #:nodoc:
|
||||
# And for value objects on a composed_of relationship:
|
||||
# { :address => Address.new("123 abc st.", "chicago") }
|
||||
# # => "address_street='123 abc st.' and address_city='chicago'"
|
||||
def sanitize_sql_hash_for_conditions(attrs, default_table_name = quoted_table_name)
|
||||
def sanitize_sql_hash_for_conditions(attrs, default_table_name = quoted_table_name, top_level = true)
|
||||
attrs = expand_hash_conditions_for_aggregates(attrs)
|
||||
|
||||
conditions = attrs.map do |attr, value|
|
||||
table_name = default_table_name
|
||||
|
||||
unless value.is_a?(Hash)
|
||||
if not value.is_a?(Hash)
|
||||
attr = attr.to_s
|
||||
|
||||
# Extract table name from qualified attribute names.
|
||||
if attr.include?('.')
|
||||
if attr.include?('.') and top_level
|
||||
attr_table_name, attr = attr.split('.', 2)
|
||||
attr_table_name = connection.quote_table_name(attr_table_name)
|
||||
else
|
||||
@@ -2351,8 +2355,10 @@ module ActiveRecord #:nodoc:
|
||||
end
|
||||
|
||||
attribute_condition("#{attr_table_name}.#{connection.quote_column_name(attr)}", value)
|
||||
elsif top_level
|
||||
sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s), false)
|
||||
else
|
||||
sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s))
|
||||
raise ActiveRecord::StatementInvalid
|
||||
end
|
||||
end.join(' AND ')
|
||||
|
||||
|
||||
@@ -195,6 +195,7 @@ module ActiveRecord
|
||||
def log_info(sql, name, ms)
|
||||
if @logger && @logger.debug?
|
||||
name = '%s (%.1fms)' % [name || 'SQL', ms]
|
||||
sql.force_encoding 'binary' if sql.respond_to?(:force_encoding)
|
||||
@logger.debug(format_log_entry(name, sql.squeeze(' ')))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1499,6 +1499,12 @@ class BasicsTest < ActiveRecord::TestCase
|
||||
assert_nil topic.content
|
||||
end
|
||||
|
||||
def test_should_raise_exception_on_assigning_aready_serialized_content
|
||||
topic = Topic.new
|
||||
serialized_content = %w[foo bar].to_yaml
|
||||
assert_raise(ActiveRecord::ActiveRecordError) { topic.content = serialized_content }
|
||||
end
|
||||
|
||||
def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
|
||||
myobj = MyObject.new('value1', 'value2')
|
||||
topic = Topic.new(:content => myobj)
|
||||
|
||||
@@ -363,6 +363,22 @@ class FinderTest < ActiveRecord::TestCase
|
||||
}
|
||||
end
|
||||
|
||||
def test_hash_condition_find_with_improper_nested_hashes
|
||||
assert_raise(ActiveRecord::StatementInvalid) {
|
||||
Company.find(:first, :conditions => { :name => { :companies => { :id => 1 }}})
|
||||
}
|
||||
end
|
||||
|
||||
def test_hash_condition_find_with_dot_in_nested_column_name
|
||||
assert_raise(ActiveRecord::StatementInvalid) {
|
||||
Company.find(:first, :conditions => { :name => { "companies.id" => 1 }})
|
||||
}
|
||||
end
|
||||
|
||||
def test_hash_condition_find_with_dot_in_column_name_okay
|
||||
assert Company.find(:first, :conditions => { "companies.id" => 1 })
|
||||
end
|
||||
|
||||
def test_hash_condition_find_with_escaped_characters
|
||||
Company.create("name" => "Ain't noth'n like' \#stuff")
|
||||
assert Company.find(:first, :conditions => { :name => "Ain't noth'n like' \#stuff" })
|
||||
|
||||
@@ -63,7 +63,11 @@ module ActiveSupport
|
||||
# If a newline is necessary then create a new message ending with a newline.
|
||||
# Ensures that the original message is not mutated.
|
||||
message = "#{message}\n" unless message[-1] == ?\n
|
||||
buffer << message
|
||||
if message.respond_to?(:force_encoding)
|
||||
buffer << message.force_encoding(Encoding.default_external)
|
||||
else
|
||||
buffer << message
|
||||
end
|
||||
auto_flush
|
||||
message
|
||||
end
|
||||
|
||||
@@ -20,7 +20,8 @@ class MissingSourceFile < LoadError #:nodoc:
|
||||
REGEXPS = [
|
||||
[/^no such file to load -- (.+)$/i, 1],
|
||||
[/^Missing \w+ (file\s*)?([^\s]+.rb)$/i, 2],
|
||||
[/^Missing API definition file in (.+)$/i, 1]
|
||||
[/^Missing API definition file in (.+)$/i, 1],
|
||||
[/^cannot load such file -- (.+)$/i, 1]
|
||||
] unless defined?(REGEXPS)
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# encoding: utf-8
|
||||
require 'active_support/core_ext/string/encoding'
|
||||
|
||||
class Object
|
||||
# An object is blank if it's false, empty, or a whitespace string.
|
||||
# For example, "", " ", +nil+, [], and {} are blank.
|
||||
@@ -63,9 +66,26 @@ class Hash #:nodoc:
|
||||
alias_method :blank?, :empty?
|
||||
end
|
||||
|
||||
class String #:nodoc:
|
||||
class String
|
||||
# 0x3000: fullwidth whitespace
|
||||
NON_WHITESPACE_REGEXP = %r![^\s#{[0x3000].pack("U")}]!
|
||||
|
||||
# A string is blank if it's empty or contains whitespaces only:
|
||||
#
|
||||
# "".blank? # => true
|
||||
# " ".blank? # => true
|
||||
# " ".blank? # => true
|
||||
# " something here ".blank? # => false
|
||||
#
|
||||
def blank?
|
||||
self !~ /\S/
|
||||
return true if empty?
|
||||
|
||||
# 1.8 does not takes [:space:] properly
|
||||
if encoding_aware?
|
||||
self !~ /[^[:space:]]/
|
||||
else
|
||||
self !~ NON_WHITESPACE_REGEXP
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
11
activesupport/lib/active_support/core_ext/string/encoding.rb
Normal file
11
activesupport/lib/active_support/core_ext/string/encoding.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
class String
|
||||
if defined?(Encoding) && "".respond_to?(:encode)
|
||||
def encoding_aware?
|
||||
true
|
||||
end
|
||||
else
|
||||
def encoding_aware?
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,8 +2,10 @@ require 'erb'
|
||||
|
||||
class ERB
|
||||
module Util
|
||||
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"' }
|
||||
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''' }
|
||||
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
|
||||
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+));)/
|
||||
JSON_ESCAPE_REGEXP = /[&"><]/
|
||||
|
||||
# A utility method for escaping HTML tag characters.
|
||||
# This method is also aliased as <tt>h</tt>.
|
||||
@@ -14,12 +16,23 @@ class ERB
|
||||
# ==== Example:
|
||||
# puts html_escape("is a > 0 & a < 10?")
|
||||
# # => is a > 0 & a < 10?
|
||||
def html_escape(s)
|
||||
s = s.to_s
|
||||
if s.html_safe?
|
||||
s
|
||||
else
|
||||
s.to_s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/</, "<").html_safe
|
||||
if RUBY_VERSION > '1.9'
|
||||
def html_escape(s)
|
||||
s = s.to_s
|
||||
if s.html_safe?
|
||||
s
|
||||
else
|
||||
s.gsub(/[&"'><]/, HTML_ESCAPE).html_safe
|
||||
end
|
||||
end
|
||||
else
|
||||
def html_escape(s)
|
||||
s = s.to_s
|
||||
if s.html_safe?
|
||||
s
|
||||
else
|
||||
s.gsub(/[&"'><]/){ |x| HTML_ESCAPE[x] }.html_safe
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,21 +42,50 @@ class ERB
|
||||
module_function :html_escape
|
||||
module_function :h
|
||||
|
||||
# A utility method for escaping HTML entities in JSON strings.
|
||||
# This method is also aliased as <tt>j</tt>.
|
||||
# A utility method for escaping HTML without affecting existing escaped entities.
|
||||
#
|
||||
# In your ERb templates, use this method to escape any HTML entities:
|
||||
# <%=j @person.to_json %>
|
||||
# html_escape_once('1 < 2 & 3')
|
||||
# # => "1 < 2 & 3"
|
||||
#
|
||||
# ==== Example:
|
||||
# puts json_escape("is a > 0 & a < 10?")
|
||||
# # => is a \u003E 0 \u0026 a \u003C 10?
|
||||
def json_escape(s)
|
||||
s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
|
||||
# html_escape_once('<< Accept & Checkout')
|
||||
# # => "<< Accept & Checkout"
|
||||
if RUBY_VERSION > '1.9'
|
||||
def html_escape_once(s)
|
||||
result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
|
||||
s.html_safe? ? result.html_safe : result
|
||||
end
|
||||
else
|
||||
def html_escape_once(s)
|
||||
result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP) { |special| HTML_ESCAPE[special] }
|
||||
s.html_safe? ? result.html_safe : result
|
||||
end
|
||||
end
|
||||
|
||||
module_function :html_escape_once
|
||||
|
||||
# A utility method for escaping HTML entities in JSON strings
|
||||
# using \uXXXX JavaScript escape sequences for string literals:
|
||||
#
|
||||
# json_escape('is a > 0 & a < 10?')
|
||||
# # => is a \u003E 0 \u0026 a \u003C 10?
|
||||
#
|
||||
# Note that after this operation is performed the output is not
|
||||
# valid JSON. In particular double quotes are removed:
|
||||
#
|
||||
# json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}')
|
||||
# # => {name:john,created_at:2010-04-28T01:39:31Z,id:1}
|
||||
if RUBY_VERSION > '1.9'
|
||||
def json_escape(s)
|
||||
result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
|
||||
s.html_safe? ? result.html_safe : result
|
||||
end
|
||||
else
|
||||
def json_escape(s)
|
||||
result = s.to_s.gsub(JSON_ESCAPE_REGEXP) { |special| JSON_ESCAPE[special] }
|
||||
s.html_safe? ? result.html_safe : result
|
||||
end
|
||||
end
|
||||
|
||||
alias j json_escape
|
||||
module_function :j
|
||||
module_function :json_escape
|
||||
end
|
||||
end
|
||||
@@ -54,7 +96,7 @@ class Object
|
||||
end
|
||||
end
|
||||
|
||||
class Fixnum
|
||||
class Numeric
|
||||
def html_safe?
|
||||
true
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# encoding: utf-8
|
||||
require 'singleton'
|
||||
require 'iconv'
|
||||
require 'iconv' if RUBY_VERSION < '1.9'
|
||||
require 'kconv'
|
||||
|
||||
module ActiveSupport
|
||||
@@ -283,8 +283,7 @@ module ActiveSupport
|
||||
if RUBY_VERSION >= '1.9'
|
||||
undef_method :transliterate
|
||||
def transliterate(string)
|
||||
warn "Ruby 1.9 doesn't support Unicode normalization yet"
|
||||
string.dup
|
||||
string.encode("us-ascii", :undef => :replace, :invalid => :replace, :replace => "")
|
||||
end
|
||||
|
||||
# The iconv transliteration code doesn't function correctly
|
||||
|
||||
644
activesupport/lib/active_support/json/backends/okjson.rb
Normal file
644
activesupport/lib/active_support/json/backends/okjson.rb
Normal file
@@ -0,0 +1,644 @@
|
||||
module ActiveSupport
|
||||
# Include OkJson as a replacement for the Yaml backend
|
||||
# encoding: UTF-8
|
||||
#
|
||||
# Copyright 2011, 2012 Keith Rarick
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
# See https://github.com/kr/okjson for updates.
|
||||
|
||||
require 'stringio'
|
||||
|
||||
# Some parts adapted from
|
||||
# http://golang.org/src/pkg/json/decode.go and
|
||||
# http://golang.org/src/pkg/utf8/utf8.go
|
||||
module OkJson
|
||||
Upstream = 'LTD7LBKLZWFF7OZK'
|
||||
extend self
|
||||
|
||||
|
||||
# Decodes a json document in string s and
|
||||
# returns the corresponding ruby value.
|
||||
# String s must be valid UTF-8. If you have
|
||||
# a string in some other encoding, convert
|
||||
# it first.
|
||||
#
|
||||
# String values in the resulting structure
|
||||
# will be UTF-8.
|
||||
def decode(s)
|
||||
ts = lex(s)
|
||||
v, ts = textparse(ts)
|
||||
if ts.length > 0
|
||||
raise Error, 'trailing garbage'
|
||||
end
|
||||
v
|
||||
end
|
||||
|
||||
|
||||
# Parses a "json text" in the sense of RFC 4627.
|
||||
# Returns the parsed value and any trailing tokens.
|
||||
# Note: this is almost the same as valparse,
|
||||
# except that it does not accept atomic values.
|
||||
def textparse(ts)
|
||||
if ts.length < 0
|
||||
raise Error, 'empty'
|
||||
end
|
||||
|
||||
typ, _, val = ts[0]
|
||||
case typ
|
||||
when '{' then objparse(ts)
|
||||
when '[' then arrparse(ts)
|
||||
else
|
||||
raise Error, "unexpected #{val.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Parses a "value" in the sense of RFC 4627.
|
||||
# Returns the parsed value and any trailing tokens.
|
||||
def valparse(ts)
|
||||
if ts.length < 0
|
||||
raise Error, 'empty'
|
||||
end
|
||||
|
||||
typ, _, val = ts[0]
|
||||
case typ
|
||||
when '{' then objparse(ts)
|
||||
when '[' then arrparse(ts)
|
||||
when :val,:str then [val, ts[1..-1]]
|
||||
else
|
||||
raise Error, "unexpected #{val.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Parses an "object" in the sense of RFC 4627.
|
||||
# Returns the parsed value and any trailing tokens.
|
||||
def objparse(ts)
|
||||
ts = eat('{', ts)
|
||||
obj = {}
|
||||
|
||||
if ts[0][0] == '}'
|
||||
return obj, ts[1..-1]
|
||||
end
|
||||
|
||||
k, v, ts = pairparse(ts)
|
||||
obj[k] = v
|
||||
|
||||
if ts[0][0] == '}'
|
||||
return obj, ts[1..-1]
|
||||
end
|
||||
|
||||
loop do
|
||||
ts = eat(',', ts)
|
||||
|
||||
k, v, ts = pairparse(ts)
|
||||
obj[k] = v
|
||||
|
||||
if ts[0][0] == '}'
|
||||
return obj, ts[1..-1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Parses a "member" in the sense of RFC 4627.
|
||||
# Returns the parsed values and any trailing tokens.
|
||||
def pairparse(ts)
|
||||
(typ, _, k), ts = ts[0], ts[1..-1]
|
||||
if typ != :str
|
||||
raise Error, "unexpected #{k.inspect}"
|
||||
end
|
||||
ts = eat(':', ts)
|
||||
v, ts = valparse(ts)
|
||||
[k, v, ts]
|
||||
end
|
||||
|
||||
|
||||
# Parses an "array" in the sense of RFC 4627.
|
||||
# Returns the parsed value and any trailing tokens.
|
||||
def arrparse(ts)
|
||||
ts = eat('[', ts)
|
||||
arr = []
|
||||
|
||||
if ts[0][0] == ']'
|
||||
return arr, ts[1..-1]
|
||||
end
|
||||
|
||||
v, ts = valparse(ts)
|
||||
arr << v
|
||||
|
||||
if ts[0][0] == ']'
|
||||
return arr, ts[1..-1]
|
||||
end
|
||||
|
||||
loop do
|
||||
ts = eat(',', ts)
|
||||
|
||||
v, ts = valparse(ts)
|
||||
arr << v
|
||||
|
||||
if ts[0][0] == ']'
|
||||
return arr, ts[1..-1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def eat(typ, ts)
|
||||
if ts[0][0] != typ
|
||||
raise Error, "expected #{typ} (got #{ts[0].inspect})"
|
||||
end
|
||||
ts[1..-1]
|
||||
end
|
||||
|
||||
|
||||
# Scans s and returns a list of json tokens,
|
||||
# excluding white space (as defined in RFC 4627).
|
||||
def lex(s)
|
||||
ts = []
|
||||
while s.length > 0
|
||||
typ, lexeme, val = tok(s)
|
||||
if typ == nil
|
||||
raise Error, "invalid character at #{s[0,10].inspect}"
|
||||
end
|
||||
if typ != :space
|
||||
ts << [typ, lexeme, val]
|
||||
end
|
||||
s = s[lexeme.length..-1]
|
||||
end
|
||||
ts
|
||||
end
|
||||
|
||||
|
||||
# Scans the first token in s and
|
||||
# returns a 3-element list, or nil
|
||||
# if s does not begin with a valid token.
|
||||
#
|
||||
# The first list element is one of
|
||||
# '{', '}', ':', ',', '[', ']',
|
||||
# :val, :str, and :space.
|
||||
#
|
||||
# The second element is the lexeme.
|
||||
#
|
||||
# The third element is the value of the
|
||||
# token for :val and :str, otherwise
|
||||
# it is the lexeme.
|
||||
def tok(s)
|
||||
case s[0]
|
||||
when ?{ then ['{', s[0,1], s[0,1]]
|
||||
when ?} then ['}', s[0,1], s[0,1]]
|
||||
when ?: then [':', s[0,1], s[0,1]]
|
||||
when ?, then [',', s[0,1], s[0,1]]
|
||||
when ?[ then ['[', s[0,1], s[0,1]]
|
||||
when ?] then [']', s[0,1], s[0,1]]
|
||||
when ?n then nulltok(s)
|
||||
when ?t then truetok(s)
|
||||
when ?f then falsetok(s)
|
||||
when ?" then strtok(s)
|
||||
when Spc then [:space, s[0,1], s[0,1]]
|
||||
when ?\t then [:space, s[0,1], s[0,1]]
|
||||
when ?\n then [:space, s[0,1], s[0,1]]
|
||||
when ?\r then [:space, s[0,1], s[0,1]]
|
||||
else numtok(s)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def nulltok(s); s[0,4] == 'null' ? [:val, 'null', nil] : [] end
|
||||
def truetok(s); s[0,4] == 'true' ? [:val, 'true', true] : [] end
|
||||
def falsetok(s); s[0,5] == 'false' ? [:val, 'false', false] : [] end
|
||||
|
||||
|
||||
def numtok(s)
|
||||
m = /-?([1-9][0-9]+|[0-9])([.][0-9]+)?([eE][+-]?[0-9]+)?/.match(s)
|
||||
if m && m.begin(0) == 0
|
||||
if m[3] && !m[2]
|
||||
[:val, m[0], Integer(m[1])*(10**Integer(m[3][1..-1]))]
|
||||
elsif m[2]
|
||||
[:val, m[0], Float(m[0])]
|
||||
else
|
||||
[:val, m[0], Integer(m[0])]
|
||||
end
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def strtok(s)
|
||||
m = /"([^"\\]|\\["\/\\bfnrt]|\\u[0-9a-fA-F]{4})*"/.match(s)
|
||||
if ! m
|
||||
raise Error, "invalid string literal at #{abbrev(s)}"
|
||||
end
|
||||
[:str, m[0], unquote(m[0])]
|
||||
end
|
||||
|
||||
|
||||
def abbrev(s)
|
||||
t = s[0,10]
|
||||
p = t['`']
|
||||
t = t[0,p] if p
|
||||
t = t + '...' if t.length < s.length
|
||||
'`' + t + '`'
|
||||
end
|
||||
|
||||
|
||||
# Converts a quoted json string literal q into a UTF-8-encoded string.
|
||||
# The rules are different than for Ruby, so we cannot use eval.
|
||||
# Unquote will raise an error if q contains control characters.
|
||||
def unquote(q)
|
||||
q = q[1...-1]
|
||||
a = q.dup # allocate a big enough string
|
||||
rubydoesenc = false
|
||||
# In ruby >= 1.9, a[w] is a codepoint, not a byte.
|
||||
if a.class.method_defined?(:force_encoding)
|
||||
a.force_encoding('UTF-8')
|
||||
rubydoesenc = true
|
||||
end
|
||||
r, w = 0, 0
|
||||
while r < q.length
|
||||
c = q[r]
|
||||
case true
|
||||
when c == ?\\
|
||||
r += 1
|
||||
if r >= q.length
|
||||
raise Error, "string literal ends with a \"\\\": \"#{q}\""
|
||||
end
|
||||
|
||||
case q[r]
|
||||
when ?",?\\,?/,?'
|
||||
a[w] = q[r]
|
||||
r += 1
|
||||
w += 1
|
||||
when ?b,?f,?n,?r,?t
|
||||
a[w] = Unesc[q[r]]
|
||||
r += 1
|
||||
w += 1
|
||||
when ?u
|
||||
r += 1
|
||||
uchar = begin
|
||||
hexdec4(q[r,4])
|
||||
rescue RuntimeError => e
|
||||
raise Error, "invalid escape sequence \\u#{q[r,4]}: #{e}"
|
||||
end
|
||||
r += 4
|
||||
if surrogate? uchar
|
||||
if q.length >= r+6
|
||||
uchar1 = hexdec4(q[r+2,4])
|
||||
uchar = subst(uchar, uchar1)
|
||||
if uchar != Ucharerr
|
||||
# A valid pair; consume.
|
||||
r += 6
|
||||
end
|
||||
end
|
||||
end
|
||||
if rubydoesenc
|
||||
a[w] = '' << uchar
|
||||
w += 1
|
||||
else
|
||||
w += ucharenc(a, w, uchar)
|
||||
end
|
||||
else
|
||||
raise Error, "invalid escape char #{q[r]} in \"#{q}\""
|
||||
end
|
||||
when c == ?", c < Spc
|
||||
raise Error, "invalid character in string literal \"#{q}\""
|
||||
else
|
||||
# Copy anything else byte-for-byte.
|
||||
# Valid UTF-8 will remain valid UTF-8.
|
||||
# Invalid UTF-8 will remain invalid UTF-8.
|
||||
# In ruby >= 1.9, c is a codepoint, not a byte,
|
||||
# in which case this is still what we want.
|
||||
a[w] = c
|
||||
r += 1
|
||||
w += 1
|
||||
end
|
||||
end
|
||||
a[0,w]
|
||||
end
|
||||
|
||||
|
||||
# Encodes unicode character u as UTF-8
|
||||
# bytes in string a at position i.
|
||||
# Returns the number of bytes written.
|
||||
def ucharenc(a, i, u)
|
||||
case true
|
||||
when u <= Uchar1max
|
||||
a[i] = (u & 0xff).chr
|
||||
1
|
||||
when u <= Uchar2max
|
||||
a[i+0] = (Utag2 | ((u>>6)&0xff)).chr
|
||||
a[i+1] = (Utagx | (u&Umaskx)).chr
|
||||
2
|
||||
when u <= Uchar3max
|
||||
a[i+0] = (Utag3 | ((u>>12)&0xff)).chr
|
||||
a[i+1] = (Utagx | ((u>>6)&Umaskx)).chr
|
||||
a[i+2] = (Utagx | (u&Umaskx)).chr
|
||||
3
|
||||
else
|
||||
a[i+0] = (Utag4 | ((u>>18)&0xff)).chr
|
||||
a[i+1] = (Utagx | ((u>>12)&Umaskx)).chr
|
||||
a[i+2] = (Utagx | ((u>>6)&Umaskx)).chr
|
||||
a[i+3] = (Utagx | (u&Umaskx)).chr
|
||||
4
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def hexdec4(s)
|
||||
if s.length != 4
|
||||
raise Error, 'short'
|
||||
end
|
||||
(nibble(s[0])<<12) | (nibble(s[1])<<8) | (nibble(s[2])<<4) | nibble(s[3])
|
||||
end
|
||||
|
||||
|
||||
def subst(u1, u2)
|
||||
if Usurr1 <= u1 && u1 < Usurr2 && Usurr2 <= u2 && u2 < Usurr3
|
||||
return ((u1-Usurr1)<<10) | (u2-Usurr2) + Usurrself
|
||||
end
|
||||
return Ucharerr
|
||||
end
|
||||
|
||||
|
||||
def surrogate?(u)
|
||||
Usurr1 <= u && u < Usurr3
|
||||
end
|
||||
|
||||
|
||||
def nibble(c)
|
||||
case true
|
||||
when ?0 <= c && c <= ?9 then c.ord - ?0.ord
|
||||
when ?a <= c && c <= ?z then c.ord - ?a.ord + 10
|
||||
when ?A <= c && c <= ?Z then c.ord - ?A.ord + 10
|
||||
else
|
||||
raise Error, "invalid hex code #{c}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Encodes x into a json text. It may contain only
|
||||
# Array, Hash, String, Numeric, true, false, nil.
|
||||
# (Note, this list excludes Symbol.)
|
||||
# X itself must be an Array or a Hash.
|
||||
# No other value can be encoded, and an error will
|
||||
# be raised if x contains any other value, such as
|
||||
# Nan, Infinity, Symbol, and Proc, or if a Hash key
|
||||
# is not a String.
|
||||
# Strings contained in x must be valid UTF-8.
|
||||
def encode(x)
|
||||
case x
|
||||
when Hash then objenc(x)
|
||||
when Array then arrenc(x)
|
||||
else
|
||||
raise Error, 'root value must be an Array or a Hash'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def valenc(x)
|
||||
case x
|
||||
when Hash then objenc(x)
|
||||
when Array then arrenc(x)
|
||||
when String then strenc(x)
|
||||
when Numeric then numenc(x)
|
||||
when true then "true"
|
||||
when false then "false"
|
||||
when nil then "null"
|
||||
else
|
||||
raise Error, "cannot encode #{x.class}: #{x.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def objenc(x)
|
||||
'{' + x.map{|k,v| keyenc(k) + ':' + valenc(v)}.join(',') + '}'
|
||||
end
|
||||
|
||||
|
||||
def arrenc(a)
|
||||
'[' + a.map{|x| valenc(x)}.join(',') + ']'
|
||||
end
|
||||
|
||||
|
||||
def keyenc(k)
|
||||
case k
|
||||
when String then strenc(k)
|
||||
else
|
||||
raise Error, "Hash key is not a string: #{k.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def strenc(s)
|
||||
t = StringIO.new
|
||||
t.putc(?")
|
||||
r = 0
|
||||
|
||||
# In ruby >= 1.9, s[r] is a codepoint, not a byte.
|
||||
rubydoesenc = s.class.method_defined?(:encoding)
|
||||
|
||||
while r < s.length
|
||||
case s[r]
|
||||
when ?" then t.print('\\"')
|
||||
when ?\\ then t.print('\\\\')
|
||||
when ?\b then t.print('\\b')
|
||||
when ?\f then t.print('\\f')
|
||||
when ?\n then t.print('\\n')
|
||||
when ?\r then t.print('\\r')
|
||||
when ?\t then t.print('\\t')
|
||||
else
|
||||
c = s[r]
|
||||
case true
|
||||
when rubydoesenc
|
||||
begin
|
||||
c.ord # will raise an error if c is invalid UTF-8
|
||||
t.write(c)
|
||||
rescue
|
||||
t.write(Ustrerr)
|
||||
end
|
||||
when Spc <= c && c <= ?~
|
||||
t.putc(c)
|
||||
else
|
||||
n = ucharcopy(t, s, r) # ensure valid UTF-8 output
|
||||
r += n - 1 # r is incremented below
|
||||
end
|
||||
end
|
||||
r += 1
|
||||
end
|
||||
t.putc(?")
|
||||
t.string
|
||||
end
|
||||
|
||||
|
||||
def numenc(x)
|
||||
if ((x.nan? || x.infinite?) rescue false)
|
||||
raise Error, "Numeric cannot be represented: #{x}"
|
||||
end
|
||||
"#{x}"
|
||||
end
|
||||
|
||||
|
||||
# Copies the valid UTF-8 bytes of a single character
|
||||
# from string s at position i to I/O object t, and
|
||||
# returns the number of bytes copied.
|
||||
# If no valid UTF-8 char exists at position i,
|
||||
# ucharcopy writes Ustrerr and returns 1.
|
||||
def ucharcopy(t, s, i)
|
||||
n = s.length - i
|
||||
raise Utf8Error if n < 1
|
||||
|
||||
c0 = s[i].ord
|
||||
|
||||
# 1-byte, 7-bit sequence?
|
||||
if c0 < Utagx
|
||||
t.putc(c0)
|
||||
return 1
|
||||
end
|
||||
|
||||
raise Utf8Error if c0 < Utag2 # unexpected continuation byte?
|
||||
|
||||
raise Utf8Error if n < 2 # need continuation byte
|
||||
c1 = s[i+1].ord
|
||||
raise Utf8Error if c1 < Utagx || Utag2 <= c1
|
||||
|
||||
# 2-byte, 11-bit sequence?
|
||||
if c0 < Utag3
|
||||
raise Utf8Error if ((c0&Umask2)<<6 | (c1&Umaskx)) <= Uchar1max
|
||||
t.putc(c0)
|
||||
t.putc(c1)
|
||||
return 2
|
||||
end
|
||||
|
||||
# need second continuation byte
|
||||
raise Utf8Error if n < 3
|
||||
|
||||
c2 = s[i+2].ord
|
||||
raise Utf8Error if c2 < Utagx || Utag2 <= c2
|
||||
|
||||
# 3-byte, 16-bit sequence?
|
||||
if c0 < Utag4
|
||||
u = (c0&Umask3)<<12 | (c1&Umaskx)<<6 | (c2&Umaskx)
|
||||
raise Utf8Error if u <= Uchar2max
|
||||
t.putc(c0)
|
||||
t.putc(c1)
|
||||
t.putc(c2)
|
||||
return 3
|
||||
end
|
||||
|
||||
# need third continuation byte
|
||||
raise Utf8Error if n < 4
|
||||
c3 = s[i+3].ord
|
||||
raise Utf8Error if c3 < Utagx || Utag2 <= c3
|
||||
|
||||
# 4-byte, 21-bit sequence?
|
||||
if c0 < Utag5
|
||||
u = (c0&Umask4)<<18 | (c1&Umaskx)<<12 | (c2&Umaskx)<<6 | (c3&Umaskx)
|
||||
raise Utf8Error if u <= Uchar3max
|
||||
t.putc(c0)
|
||||
t.putc(c1)
|
||||
t.putc(c2)
|
||||
t.putc(c3)
|
||||
return 4
|
||||
end
|
||||
|
||||
raise Utf8Error
|
||||
rescue Utf8Error
|
||||
t.write(Ustrerr)
|
||||
return 1
|
||||
end
|
||||
|
||||
|
||||
class Utf8Error < ::StandardError
|
||||
end
|
||||
|
||||
|
||||
class Error < ::StandardError
|
||||
end
|
||||
|
||||
|
||||
Utagx = 0x80 # 1000 0000
|
||||
Utag2 = 0xc0 # 1100 0000
|
||||
Utag3 = 0xe0 # 1110 0000
|
||||
Utag4 = 0xf0 # 1111 0000
|
||||
Utag5 = 0xF8 # 1111 1000
|
||||
Umaskx = 0x3f # 0011 1111
|
||||
Umask2 = 0x1f # 0001 1111
|
||||
Umask3 = 0x0f # 0000 1111
|
||||
Umask4 = 0x07 # 0000 0111
|
||||
Uchar1max = (1<<7) - 1
|
||||
Uchar2max = (1<<11) - 1
|
||||
Uchar3max = (1<<16) - 1
|
||||
Ucharerr = 0xFFFD # unicode "replacement char"
|
||||
Ustrerr = "\xef\xbf\xbd" # unicode "replacement char"
|
||||
Usurrself = 0x10000
|
||||
Usurr1 = 0xd800
|
||||
Usurr2 = 0xdc00
|
||||
Usurr3 = 0xe000
|
||||
|
||||
Spc = ' '[0]
|
||||
Unesc = {?b=>?\b, ?f=>?\f, ?n=>?\n, ?r=>?\r, ?t=>?\t}
|
||||
end
|
||||
|
||||
module JSON
|
||||
module Backends
|
||||
module OkJson
|
||||
ParseError = ::ActiveSupport::OkJson::Error
|
||||
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 = ActiveSupport::OkJson.decode(json)
|
||||
if ActiveSupport.parse_json_times
|
||||
convert_dates_from(data)
|
||||
else
|
||||
data
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def convert_dates_from(data)
|
||||
case data
|
||||
when nil
|
||||
nil
|
||||
when DATE_REGEX
|
||||
begin
|
||||
DateTime.parse(data)
|
||||
rescue ArgumentError
|
||||
data
|
||||
end
|
||||
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
|
||||
@@ -7,79 +7,12 @@ module ActiveSupport
|
||||
ParseError = ::StandardError
|
||||
extend 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"
|
||||
raise "The Yaml backend has been deprecated due to security risks, you should set ActiveSupport::JSON.backend = 'OkJson'"
|
||||
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
|
||||
# overwrite them with spaces later.
|
||||
times << pos << scanner.pos
|
||||
end
|
||||
quoting = false
|
||||
end
|
||||
when ":",","
|
||||
marks << scanner.pos - 1 unless quoting
|
||||
when "\\"
|
||||
scanner.skip(/\\/)
|
||||
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
|
||||
chunk = scanner.peek(right_pos[i] - scanner.pos + 1)
|
||||
# overwrite the quotes found around the dates with spaces
|
||||
while times.size > 0 && times[0] <= right_pos[i]
|
||||
chunk[times.shift - scanner.pos - 1] = ' '
|
||||
end
|
||||
chunk.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
|
||||
output << chunk
|
||||
end
|
||||
output = output * " "
|
||||
|
||||
output.gsub!(/\\\//, '/')
|
||||
output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ module ActiveSupport
|
||||
|
||||
module JSON
|
||||
# Listed in order of preference.
|
||||
DECODERS = %w(Yajl Yaml)
|
||||
DECODERS = %w(Yajl OkJson)
|
||||
|
||||
class << self
|
||||
attr_reader :parse_error
|
||||
|
||||
@@ -5,8 +5,8 @@ require 'active_support/testing/deprecation'
|
||||
require 'active_support/testing/declarative'
|
||||
|
||||
begin
|
||||
gem 'mocha', ">= 0.9.7"
|
||||
require 'mocha'
|
||||
gem 'mocha', ">= 0.13.1"
|
||||
require 'mocha/setup'
|
||||
rescue LoadError
|
||||
# Fake Mocha::ExpectationError so we can rescue it in #run. Bleh.
|
||||
Object.const_set :Mocha, Module.new
|
||||
|
||||
@@ -47,10 +47,31 @@ module TZInfo
|
||||
def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
|
||||
DateTime.new!(ajd, of, sg)
|
||||
end
|
||||
else
|
||||
elsif DateTime.respond_to? :new0
|
||||
def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
|
||||
DateTime.new0(ajd, of, sg)
|
||||
end
|
||||
else
|
||||
HALF_DAYS_IN_DAY = rational_new!(1, 2)
|
||||
|
||||
def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
|
||||
# Convert from an Astronomical Julian Day number to a civil Julian Day number.
|
||||
jd = ajd + of + HALF_DAYS_IN_DAY
|
||||
|
||||
# Ruby trunk revision 31862 changed the behaviour of DateTime.jd so that it will no
|
||||
# longer accept a fractional civil Julian Day number if further arguments are specified.
|
||||
# Calculate the hours, minutes and seconds to pass to jd.
|
||||
|
||||
jd_i = jd.to_i
|
||||
jd_i -= 1 if jd < 0
|
||||
hours = (jd - jd_i) * 24
|
||||
hours_i = hours.to_i
|
||||
minutes = (hours - hours_i) * 60
|
||||
minutes_i = minutes.to_i
|
||||
seconds = (minutes - minutes_i) * 60
|
||||
|
||||
DateTime.jd(jd_i, hours_i, minutes_i, seconds, of, sg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -42,9 +42,9 @@ class TestJSONDecoding < ActiveSupport::TestCase
|
||||
}
|
||||
|
||||
# load the default JSON backend
|
||||
ActiveSupport::JSON.backend = 'Yaml'
|
||||
ActiveSupport::JSON.backend = 'OkJson'
|
||||
|
||||
backends = %w(Yaml)
|
||||
backends = %w(OkJson)
|
||||
backends << "JSONGem" if defined?(::JSON)
|
||||
backends << "Yajl" if defined?(::Yajl)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user