aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/core_ext/object
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support/core_ext/object')
-rw-r--r--activesupport/lib/active_support/core_ext/object/blank.rb60
-rw-r--r--activesupport/lib/active_support/core_ext/object/deep_dup.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/object/duplicable.rb3
-rw-r--r--activesupport/lib/active_support/core_ext/object/inclusion.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/object/json.rb197
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_json.rb27
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_param.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_query.rb7
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb2
9 files changed, 272 insertions, 54 deletions
diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb
index 8a5eb4bc93..38e43478df 100644
--- a/activesupport/lib/active_support/core_ext/object/blank.rb
+++ b/activesupport/lib/active_support/core_ext/object/blank.rb
@@ -4,36 +4,42 @@ class Object
# An object is blank if it's false, empty, or a whitespace string.
# For example, '', ' ', +nil+, [], and {} are all blank.
#
- # This simplifies:
+ # This simplifies
#
- # if address.nil? || address.empty?
+ # address.nil? || address.empty?
#
- # ...to:
+ # to
#
- # if address.blank?
+ # address.blank?
+ #
+ # @return [true, false]
def blank?
- respond_to?(:empty?) ? empty? : !self
+ respond_to?(:empty?) ? !!empty? : !self
end
- # An object is present if it's not <tt>blank?</tt>.
+ # An object is present if it's not blank.
+ #
+ # @return [true, false]
def present?
!blank?
end
- # Returns object if it's <tt>present?</tt> otherwise returns +nil+.
- # <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
+ # Returns the receiver if it's present otherwise returns +nil+.
+ # <tt>object.presence</tt> is equivalent to
#
- # This is handy for any representation of objects where blank is the same
- # as not present at all. For example, this simplifies a common check for
- # HTTP POST/query parameters:
+ # object.present? ? object : nil
+ #
+ # For example, something like
#
# state = params[:state] if params[:state].present?
# country = params[:country] if params[:country].present?
# region = state || country || 'US'
#
- # ...becomes:
+ # becomes
#
# region = params[:state].presence || params[:country].presence || 'US'
+ #
+ # @return [Object]
def presence
self if present?
end
@@ -43,6 +49,8 @@ class NilClass
# +nil+ is blank:
#
# nil.blank? # => true
+ #
+ # @return [true]
def blank?
true
end
@@ -52,6 +60,8 @@ class FalseClass
# +false+ is blank:
#
# false.blank? # => true
+ #
+ # @return [true]
def blank?
true
end
@@ -61,6 +71,8 @@ class TrueClass
# +true+ is not blank:
#
# true.blank? # => false
+ #
+ # @return [false]
def blank?
false
end
@@ -71,6 +83,8 @@ class Array
#
# [].blank? # => true
# [1,2,3].blank? # => false
+ #
+ # @return [true, false]
alias_method :blank?, :empty?
end
@@ -79,18 +93,28 @@ class Hash
#
# {}.blank? # => true
# { key: 'value' }.blank? # => false
+ #
+ # @return [true, false]
alias_method :blank?, :empty?
end
class String
+ BLANK_RE = /\A[[:space:]]*\z/
+
# A string is blank if it's empty or contains whitespaces only:
#
- # ''.blank? # => true
- # ' '.blank? # => true
- # ' '.blank? # => true
- # ' something here '.blank? # => false
+ # ''.blank? # => true
+ # ' '.blank? # => true
+ # "\t\n\r".blank? # => true
+ # ' blah '.blank? # => false
+ #
+ # Unicode whitespace is supported:
+ #
+ # "\u00a0".blank? # => true
+ #
+ # @return [true, false]
def blank?
- self !~ /[^[:space:]]/
+ BLANK_RE === self
end
end
@@ -99,6 +123,8 @@ class Numeric #:nodoc:
#
# 1.blank? # => false
# 0.blank? # => false
+ #
+ # @return [false]
def blank?
false
end
diff --git a/activesupport/lib/active_support/core_ext/object/deep_dup.rb b/activesupport/lib/active_support/core_ext/object/deep_dup.rb
index 1d639f3af6..2e99f4a1b8 100644
--- a/activesupport/lib/active_support/core_ext/object/deep_dup.rb
+++ b/activesupport/lib/active_support/core_ext/object/deep_dup.rb
@@ -8,8 +8,8 @@ class Object
# dup = object.deep_dup
# dup.instance_variable_set(:@a, 1)
#
- # object.instance_variable_defined?(:@a) #=> false
- # dup.instance_variable_defined?(:@a) #=> true
+ # object.instance_variable_defined?(:@a) # => false
+ # dup.instance_variable_defined?(:@a) # => true
def deep_dup
duplicable? ? dup : self
end
@@ -22,8 +22,8 @@ class Array
# dup = array.deep_dup
# dup[1][2] = 4
#
- # array[1][2] #=> nil
- # dup[1][2] #=> 4
+ # array[1][2] # => nil
+ # dup[1][2] # => 4
def deep_dup
map { |it| it.deep_dup }
end
@@ -36,8 +36,8 @@ class Hash
# dup = hash.deep_dup
# dup[:a][:c] = 'c'
#
- # hash[:a][:c] #=> nil
- # dup[:a][:c] #=> "c"
+ # hash[:a][:c] # => nil
+ # dup[:a][:c] # => "c"
def deep_dup
each_with_object(dup) do |(key, value), hash|
hash[key.deep_dup] = value.deep_dup
diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb
index 9cd7485e2e..3d2c809c9f 100644
--- a/activesupport/lib/active_support/core_ext/object/duplicable.rb
+++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb
@@ -78,6 +78,9 @@ end
require 'bigdecimal'
class BigDecimal
+ # Needed to support Ruby 1.9.x, as it doesn't allow dup on BigDecimal, instead
+ # raises TypeError exception. Checking here on the runtime whether BigDecimal
+ # will allow dup or not.
begin
BigDecimal.new('4.56').dup
diff --git a/activesupport/lib/active_support/core_ext/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb
index b5671f66d0..55f281b213 100644
--- a/activesupport/lib/active_support/core_ext/object/inclusion.rb
+++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb
@@ -12,4 +12,16 @@ class Object
rescue NoMethodError
raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
end
+
+ # Returns the receiver if it's included in the argument otherwise returns +nil+.
+ # Argument must be any object which responds to +#include?+. Usage:
+ #
+ # params[:bucket_type].presence_in %w( project calendar )
+ #
+ # This will throw an ArgumentError if the argument doesn't respond to +#include?+.
+ #
+ # @return [Object]
+ def presence_in(another_object)
+ self.in?(another_object) ? self : nil
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/object/json.rb b/activesupport/lib/active_support/core_ext/object/json.rb
new file mode 100644
index 0000000000..5496692373
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/object/json.rb
@@ -0,0 +1,197 @@
+# Hack to load json gem first so we can overwrite its to_json.
+require 'json'
+require 'bigdecimal'
+require 'active_support/core_ext/big_decimal/conversions' # for #to_s
+require 'active_support/core_ext/hash/except'
+require 'active_support/core_ext/hash/slice'
+require 'active_support/core_ext/object/instance_variables'
+require 'time'
+require 'active_support/core_ext/time/conversions'
+require 'active_support/core_ext/date_time/conversions'
+require 'active_support/core_ext/date/conversions'
+require 'active_support/core_ext/module/aliasing'
+
+# 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.
+#
+# On the other hand, we should avoid conflict with ::JSON.{generate,dump}(obj). Unfortunately, the
+# JSON gem's encoder relies on its own to_json implementation to encode objects. Since it always
+# passes a ::JSON::State object as the only argument to to_json, we can detect that and forward the
+# calls to the original to_json method.
+#
+# It should be noted that when using ::JSON.{generate,dump} directly, ActiveSupport's encoder is
+# bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
+# ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
+# should give exactly the same results with or without active support.
+[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass, Enumerable].each do |klass|
+ klass.class_eval do
+ def to_json_with_active_support_encoder(options = nil)
+ if options.is_a?(::JSON::State)
+ # Called from JSON.{generate,dump}, forward it to JSON gem's to_json
+ self.to_json_without_active_support_encoder(options)
+ else
+ # to_json is being invoked directly, use ActiveSupport's encoder
+ ActiveSupport::JSON.encode(self, options)
+ end
+ end
+
+ alias_method_chain :to_json, :active_support_encoder
+ end
+end
+
+class Object
+ def as_json(options = nil) #:nodoc:
+ if respond_to?(:to_hash)
+ to_hash.as_json(options)
+ else
+ instance_values.as_json(options)
+ end
+ end
+end
+
+class Struct #:nodoc:
+ def as_json(options = nil)
+ Hash[members.zip(values)].as_json(options)
+ end
+end
+
+class TrueClass
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+end
+
+class FalseClass
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+end
+
+class NilClass
+ def as_json(options = nil) #:nodoc:
+ self
+ end
+end
+
+class String
+ def as_json(options = nil) #:nodoc:
+ 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
+end
+
+class Float
+ # Encoding Infinity or NaN to JSON should return "null". The default returns
+ # "Infinity" or "NaN" which are not valid JSON.
+ 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.
+ def as_json(options = nil) #:nodoc:
+ finite? ? to_s : nil
+ 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:
+ map { |v| options ? v.as_json(options.dup) : v.as_json }
+ 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
+
+ Hash[subset.map { |k, v| [k.to_s, options ? v.as_json(options.dup) : v.as_json] }]
+ end
+end
+
+class Time
+ def as_json(options = nil) #:nodoc:
+ if ActiveSupport.use_standard_json_time_format
+ xmlschema(ActiveSupport::JSON::Encoding.time_precision)
+ 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(ActiveSupport::JSON::Encoding.time_precision)
+ else
+ strftime('%Y/%m/%d %H:%M:%S %z')
+ end
+ end
+end
+
+class Process::Status #:nodoc:
+ def as_json(options = nil)
+ { :exitstatus => exitstatus, :pid => pid }
+ end
+end
diff --git a/activesupport/lib/active_support/core_ext/object/to_json.rb b/activesupport/lib/active_support/core_ext/object/to_json.rb
deleted file mode 100644
index 83cc8066e7..0000000000
--- a/activesupport/lib/active_support/core_ext/object/to_json.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# Hack to load json gem first so we can overwrite its to_json.
-begin
- require 'json'
-rescue LoadError
-end
-
-# 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
diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb
index 3b137ce6ae..e65fc5bac1 100644
--- a/activesupport/lib/active_support/core_ext/object/to_param.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_param.rb
@@ -52,7 +52,9 @@ class Hash
# This method is also aliased as +to_query+.
def to_param(namespace = nil)
collect do |key, value|
- value.to_query(namespace ? "#{namespace}[#{key}]" : key)
- end.sort! * '&'
+ unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
+ value.to_query(namespace ? "#{namespace}[#{key}]" : key)
+ end
+ end.compact.sort! * '&'
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/to_query.rb b/activesupport/lib/active_support/core_ext/object/to_query.rb
index 5d5fcf00e0..37352fa608 100644
--- a/activesupport/lib/active_support/core_ext/object/to_query.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_query.rb
@@ -18,7 +18,12 @@ class Array
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
def to_query(key)
prefix = "#{key}[]"
- collect { |value| value.to_query(prefix) }.join '&'
+
+ if empty?
+ nil.to_query(prefix)
+ else
+ collect { |value| value.to_query(prefix) }.join '&'
+ end
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index 534bbe3c42..48190e1e66 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -47,7 +47,7 @@ class Object
end
# Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
- # does not implemented the tried method.
+ # does not implement the tried method.
def try!(*a, &b)
if a.empty? && block_given?
yield self