diff options
Diffstat (limited to 'activesupport/lib')
4 files changed, 51 insertions, 13 deletions
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index a1c63a0e54..48e9d04787 100644 --- a/activesupport/lib/active_support/core_ext/object/try.rb +++ b/activesupport/lib/active_support/core_ext/object/try.rb @@ -1,4 +1,7 @@ class Object + ## + # :method: try + # # Invokes the method identified by the symbol +method+, passing it any arguments # and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does. # @@ -19,13 +22,8 @@ class Object # Person.try(:find, 1) # @people.try(:collect) {|p| p.name} #-- - # This method definition below is for rdoc purposes only. The alias_method call - # below overrides it as an optimization since +try+ behaves like +Object#send+, - # unless called on +NilClass+. - def try(method, *args, &block) - send(method, *args, &block) - end - remove_method :try + # +try+ behaves like +Object#send+, unless called on +NilClass+. + alias_method :try, :__send__ end diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb index 45b9d20c01..4d33f597d9 100644 --- a/activesupport/lib/active_support/i18n.rb +++ b/activesupport/lib/active_support/i18n.rb @@ -1,5 +1,6 @@ begin require 'i18n' + require 'active_support/lazy_load_hooks' rescue LoadError => e $stderr.puts "You don't have i18n installed in your application. Please add it to your Gemfile and run bundle install" raise e diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb index bccc5425a6..40e7a0e389 100644 --- a/activesupport/lib/active_support/inflector/transliterate.rb +++ b/activesupport/lib/active_support/inflector/transliterate.rb @@ -1,5 +1,6 @@ # encoding: utf-8 require 'active_support/core_ext/string/multibyte' +require 'active_support/i18n' module ActiveSupport module Inflector diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 2f9588e0f4..6e9d62bd16 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -41,9 +41,26 @@ module ActiveSupport @seen = [] end - def encode(value) + def encode(value, use_options = true) check_for_circular_references(value) do - value.as_json(options).encode_json(self) + jsonified = use_options ? value.as_json(options_for(value)) : value.as_json + jsonified.encode_json(self) + end + end + + # like encode, but only calls as_json, without encoding to string + def as_json(value) + check_for_circular_references(value) do + value.as_json(options_for(value)) + end + end + + def options_for(value) + if value.is_a?(Array) || value.is_a?(Hash) + # hashes and arrays need to get encoder in the options, so that they can detect circular references + (options || {}).merge(:encoder => self) + else + options end end @@ -186,13 +203,22 @@ module Enumerable end class Array - def as_json(options = nil) self end #:nodoc: - def encode_json(encoder) "[#{map { |v| encoder.encode(v) } * ','}]" end #:nodoc: + 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) } + 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: - if options + # create a subset of the hash by applying :only or :except + subset = if options if attrs = options[:only] slice(*Array.wrap(attrs)) elsif attrs = options[:except] @@ -203,10 +229,22 @@ class Hash 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) + pairs = subset.map { |k, v| [k.to_s, encoder.as_json(v)] } + result = self.is_a?(ActiveSupport::OrderedHash) ? ActiveSupport::OrderedHash.new : Hash.new + pairs.inject(result) { |hash, pair| hash[pair.first] = pair.last; hash } end def encode_json(encoder) - "{#{map { |k,v| "#{encoder.encode(k.to_s)}:#{encoder.encode(v)}" } * ','}}" + # 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 |