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/deep_dup.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/duplicable.rb20
-rw-r--r--activesupport/lib/active_support/core_ext/object/inclusion.rb12
-rw-r--r--activesupport/lib/active_support/core_ext/object/instance_variables.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/json.rb16
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_json.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_param.rb59
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_query.rb73
-rw-r--r--activesupport/lib/active_support/core_ext/object/try.rb56
-rw-r--r--activesupport/lib/active_support/core_ext/object/with_options.rb33
10 files changed, 172 insertions, 106 deletions
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 2e99f4a1b8..0191d2e973 100644
--- a/activesupport/lib/active_support/core_ext/object/deep_dup.rb
+++ b/activesupport/lib/active_support/core_ext/object/deep_dup.rb
@@ -25,7 +25,7 @@ class Array
# array[1][2] # => nil
# dup[1][2] # => 4
def deep_dup
- map { |it| it.deep_dup }
+ map(&:deep_dup)
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb
index 9cd7485e2e..620f7b6561 100644
--- a/activesupport/lib/active_support/core_ext/object/duplicable.rb
+++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb
@@ -19,7 +19,7 @@
class Object
# Can you safely dup this object?
#
- # False for +nil+, +false+, +true+, symbol, and number objects;
+ # False for +nil+, +false+, +true+, symbol, number objects;
# true otherwise.
def duplicable?
true
@@ -78,13 +78,17 @@ end
require 'bigdecimal'
class BigDecimal
- begin
- BigDecimal.new('4.56').dup
+ def duplicable?
+ true
+ end
+end
- def duplicable?
- true
- end
- rescue TypeError
- # can't dup, so use superclass implementation
+class Method
+ # Methods are not duplicable:
+ #
+ # method(:puts).duplicable? # => false
+ # method(:puts).dup # => TypeError: allocator undefined for Method
+ def duplicable?
+ false
end
end
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/instance_variables.rb b/activesupport/lib/active_support/core_ext/object/instance_variables.rb
index 755e1c6b16..593a7a4940 100644
--- a/activesupport/lib/active_support/core_ext/object/instance_variables.rb
+++ b/activesupport/lib/active_support/core_ext/object/instance_variables.rb
@@ -23,6 +23,6 @@ class Object
#
# C.new(0, 1).instance_variable_names # => ["@y", "@x"]
def instance_variable_names
- instance_variables.map { |var| var.to_s }
+ instance_variables.map(&:to_s)
end
end
diff --git a/activesupport/lib/active_support/core_ext/object/json.rb b/activesupport/lib/active_support/core_ext/object/json.rb
index 1675145ffe..698b2d1920 100644
--- a/activesupport/lib/active_support/core_ext/object/json.rb
+++ b/activesupport/lib/active_support/core_ext/object/json.rb
@@ -16,17 +16,17 @@ require 'active_support/core_ext/module/aliasing'
# 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].each do |klass|
+[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)
@@ -162,8 +162,8 @@ end
class Time
def as_json(options = nil) #:nodoc:
- if ActiveSupport.use_standard_json_time_format
- xmlschema(3)
+ if ActiveSupport::JSON::Encoding.use_standard_json_time_format
+ xmlschema(ActiveSupport::JSON::Encoding.time_precision)
else
%(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
end
@@ -172,7 +172,7 @@ end
class Date
def as_json(options = nil) #:nodoc:
- if ActiveSupport.use_standard_json_time_format
+ if ActiveSupport::JSON::Encoding.use_standard_json_time_format
strftime("%Y-%m-%d")
else
strftime("%Y/%m/%d")
@@ -182,8 +182,8 @@ end
class DateTime
def as_json(options = nil) #:nodoc:
- if ActiveSupport.use_standard_json_time_format
- xmlschema(3)
+ if ActiveSupport::JSON::Encoding.use_standard_json_time_format
+ xmlschema(ActiveSupport::JSON::Encoding.time_precision)
else
strftime('%Y/%m/%d %H:%M:%S %z')
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 3dcae6fc7f..0000000000
--- a/activesupport/lib/active_support/core_ext/object/to_json.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-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.'
-
-require 'active_support/core_ext/object/json'
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..684d4ef57e 100644
--- a/activesupport/lib/active_support/core_ext/object/to_param.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_param.rb
@@ -1,58 +1 @@
-class Object
- # Alias of <tt>to_s</tt>.
- def to_param
- to_s
- end
-end
-
-class NilClass
- # Returns +self+.
- def to_param
- self
- end
-end
-
-class TrueClass
- # Returns +self+.
- def to_param
- self
- end
-end
-
-class FalseClass
- # Returns +self+.
- def to_param
- self
- end
-end
-
-class Array
- # Calls <tt>to_param</tt> on all its elements and joins the result with
- # slashes. This is used by <tt>url_for</tt> in Action Pack.
- def to_param
- collect { |e| e.to_param }.join '/'
- end
-end
-
-class Hash
- # Returns a string representation of the receiver suitable for use as a URL
- # query string:
- #
- # {name: 'David', nationality: 'Danish'}.to_param
- # # => "name=David&nationality=Danish"
- #
- # An optional namespace can be passed to enclose the param names:
- #
- # {name: 'David', nationality: 'Danish'}.to_param('user')
- # # => "user[name]=David&user[nationality]=Danish"
- #
- # The string pairs "key=value" that conform the query string
- # are sorted lexicographically in ascending order.
- #
- # 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! * '&'
- end
-end
+require 'active_support/core_ext/object/to_query'
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..ec5ace4e16 100644
--- a/activesupport/lib/active_support/core_ext/object/to_query.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_query.rb
@@ -1,27 +1,84 @@
-require 'active_support/core_ext/object/to_param'
+require 'cgi'
class Object
- # Converts an object into a string suitable for use as a URL query string, using the given <tt>key</tt> as the
- # param name.
- #
- # Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
+ # Alias of <tt>to_s</tt>.
+ def to_param
+ to_s
+ end
+
+ # Converts an object into a string suitable for use as a URL query string,
+ # using the given <tt>key</tt> as the param name.
def to_query(key)
- require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
"#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
end
end
+class NilClass
+ # Returns +self+.
+ def to_param
+ self
+ end
+end
+
+class TrueClass
+ # Returns +self+.
+ def to_param
+ self
+ end
+end
+
+class FalseClass
+ # Returns +self+.
+ def to_param
+ self
+ end
+end
+
class Array
+ # Calls <tt>to_param</tt> on all its elements and joins the result with
+ # slashes. This is used by <tt>url_for</tt> in Action Pack.
+ def to_param
+ collect(&:to_param).join '/'
+ end
+
# Converts an array into a string suitable for use as a URL query string,
# using the given +key+ as the param name.
#
# ['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
class Hash
- alias_method :to_query, :to_param
+ # Returns a string representation of the receiver suitable for use as a URL
+ # query string:
+ #
+ # {name: 'David', nationality: 'Danish'}.to_query
+ # # => "name=David&nationality=Danish"
+ #
+ # An optional namespace can be passed to enclose key names:
+ #
+ # {name: 'David', nationality: 'Danish'}.to_query('user')
+ # # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
+ #
+ # The string pairs "key=value" that conform the query string
+ # are sorted lexicographically in ascending order.
+ #
+ # This method is also aliased as +to_param+.
+ def to_query(namespace = nil)
+ collect do |key, value|
+ unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
+ value.to_query(namespace ? "#{namespace}[#{key}]" : key)
+ end
+ end.compact.sort! * '&'
+ end
+
+ alias_method :to_param, :to_query
end
diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb
index 48190e1e66..e0f70b9caa 100644
--- a/activesupport/lib/active_support/core_ext/object/try.rb
+++ b/activesupport/lib/active_support/core_ext/object/try.rb
@@ -9,7 +9,23 @@ class Object
#
# instead of
#
- # @person ? @person.name : nil
+ # @person.name if @person
+ #
+ # +try+ calls can be chained:
+ #
+ # @person.try(:spouse).try(:name)
+ #
+ # instead of
+ #
+ # @person.spouse.name if @person && @person.spouse
+ #
+ # +try+ will also return +nil+ if the receiver does not respond to the method:
+ #
+ # @person.try(:non_existing_method) # => nil
+ #
+ # instead of
+ #
+ # @person.non_existing_method if @person.respond_to?(:non_existing_method) # => nil
#
# +try+ returns +nil+ when called on +nil+ regardless of whether it responds
# to the method:
@@ -24,7 +40,7 @@ class Object
#
# The number of arguments in the signature must match. If the object responds
# to the method the call is attempted and +ArgumentError+ is still raised
- # otherwise.
+ # in case of argument mismatch.
#
# If +try+ is called without arguments it yields the receiver to a given
# block unless it is +nil+:
@@ -33,24 +49,33 @@ class Object
# ...
# end
#
- # Please also note that +try+ is defined on +Object+, therefore it won't work
+ # You can also call try with a block without accepting an argument, and the block
+ # will be instance_eval'ed instead:
+ #
+ # @person.try { upcase.truncate(50) }
+ #
+ # Please also note that +try+ is defined on +Object+. Therefore, it won't work
# with instances of classes that do not have +Object+ among their ancestors,
# like direct subclasses of +BasicObject+. For example, using +try+ with
# +SimpleDelegator+ will delegate +try+ to the target instead of calling it on
- # delegator itself.
+ # the delegator itself.
def try(*a, &b)
- if a.empty? && block_given?
- yield self
- else
- public_send(*a, &b) if respond_to?(a.first)
- end
+ try!(*a, &b) if a.empty? || respond_to?(a.first)
end
- # Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
- # does not implement the tried method.
+ # Same as #try, but raises a NoMethodError exception if the receiver is
+ # not +nil+ and does not implement the tried method.
+ #
+ # "a".try!(:upcase) # => "A"
+ # nil.try!(:upcase) # => nil
+ # 123.try!(:upcase) # => NoMethodError: undefined method `upcase' for 123:Fixnum
def try!(*a, &b)
if a.empty? && block_given?
- yield self
+ if b.arity.zero?
+ instance_eval(&b)
+ else
+ yield self
+ end
else
public_send(*a, &b)
end
@@ -59,12 +84,12 @@ end
class NilClass
# Calling +try+ on +nil+ always returns +nil+.
- # It becomes specially helpful when navigating through associations that may return +nil+.
+ # It becomes especially helpful when navigating through associations that may return +nil+.
#
# nil.try(:name) # => nil
#
# Without +try+
- # @person && !@person.children.blank? && @person.children.first.name
+ # @person && @person.children.any? && @person.children.first.name
#
# With +try+
# @person.try(:children).try(:first).try(:name)
@@ -72,6 +97,9 @@ class NilClass
nil
end
+ # Calling +try!+ on +nil+ always returns +nil+.
+ #
+ # nil.try!(:name) # => nil
def try!(*args)
nil
end
diff --git a/activesupport/lib/active_support/core_ext/object/with_options.rb b/activesupport/lib/active_support/core_ext/object/with_options.rb
index 42e388b065..7d38e1d134 100644
--- a/activesupport/lib/active_support/core_ext/object/with_options.rb
+++ b/activesupport/lib/active_support/core_ext/object/with_options.rb
@@ -34,9 +34,36 @@ class Object
# body i18n.t :body, user_name: user.name
# end
#
+ # When you don't pass an explicit receiver, it executes the whole block
+ # in merging options context:
+ #
+ # class Account < ActiveRecord::Base
+ # with_options dependent: :destroy do
+ # has_many :customers
+ # has_many :products
+ # has_many :invoices
+ # has_many :expenses
+ # end
+ # end
+ #
# <tt>with_options</tt> can also be nested since the call is forwarded to its receiver.
- # Each nesting level will merge inherited defaults in addition to their own.
- def with_options(options)
- yield ActiveSupport::OptionMerger.new(self, options)
+ #
+ # NOTE: Each nesting level will merge inherited defaults in addition to their own.
+ #
+ # class Post < ActiveRecord::Base
+ # with_options if: :persisted?, length: { minimum: 50 } do
+ # validates :content, if: -> { content.present? }
+ # end
+ # end
+ #
+ # The code is equivalent to:
+ #
+ # validates :content, length: { minimum: 50 }, if: -> { content.present? }
+ #
+ # Hence the inherited default for `if` key is ignored.
+ #
+ def with_options(options, &block)
+ option_merger = ActiveSupport::OptionMerger.new(self, options)
+ block.arity.zero? ? option_merger.instance_eval(&block) : block.call(option_merger)
end
end