lib/active_support_deep_dup.rb
# Copyright (c) 2005-2022 David Heinemeier Hansson
#
# 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.
# frozen_string_literal: true
# From https://github.com/rails/rails/blob/main/activesupport/lib/active_support/core_ext/object/duplicable.rb
class Object
# Can you safely dup this object?
#
# False for method objects;
# true otherwise.
def duplicable?
true
end
end
class Method
# Methods are not duplicable:
#
# method(:puts).duplicable? # => false
# method(:puts).dup # => TypeError: allocator undefined for Method
def duplicable?
false
end
end
class UnboundMethod
# Unbound methods are not duplicable:
#
# method(:puts).unbind.duplicable? # => false
# method(:puts).unbind.dup # => TypeError: allocator undefined for UnboundMethod
def duplicable?
false
end
end
class Object
# Returns a deep copy of object if it's duplicable. If it's
# not duplicable, returns +self+.
#
# object = Object.new
# dup = object.deep_dup
# dup.instance_variable_set(:@a, 1)
#
# object.instance_variable_defined?(:@a) # => false
# dup.instance_variable_defined?(:@a) # => true
def deep_dup
duplicable? ? dup : self
end
end
class Array
# Returns a deep copy of array.
#
# array = [1, [2, 3]]
# dup = array.deep_dup
# dup[1][2] = 4
#
# array[1][2] # => nil
# dup[1][2] # => 4
def deep_dup
map(&:deep_dup)
end
end
class Hash
# Returns a deep copy of hash.
#
# hash = { a: { b: 'b' } }
# dup = hash.deep_dup
# dup[:a][:c] = 'c'
#
# hash[:a][:c] # => nil
# dup[:a][:c] # => "c"
def deep_dup
hash = dup
each_pair do |key, value|
if ::String === key || ::Symbol === key
hash[key] = value.deep_dup
else
hash.delete(key)
hash[key.deep_dup] = value.deep_dup
end
end
hash
end
end
# From https://github.com/rails/rails/blob/main/activesupport/lib/active_support/core_ext/object/deep_dup.rb
class Object
# Returns a deep copy of object if it's duplicable. If it's
# not duplicable, returns +self+.
#
# object = Object.new
# dup = object.deep_dup
# dup.instance_variable_set(:@a, 1)
#
# object.instance_variable_defined?(:@a) # => false
# dup.instance_variable_defined?(:@a) # => true
def deep_dup
duplicable? ? dup : self
end
end
class Array
# Returns a deep copy of array.
#
# array = [1, [2, 3]]
# dup = array.deep_dup
# dup[1][2] = 4
#
# array[1][2] # => nil
# dup[1][2] # => 4
def deep_dup
map(&:deep_dup)
end
end
class Hash
# Returns a deep copy of hash.
#
# hash = { a: { b: 'b' } }
# dup = hash.deep_dup
# dup[:a][:c] = 'c'
#
# hash[:a][:c] # => nil
# dup[:a][:c] # => "c"
def deep_dup
hash = dup
each_pair do |key, value|
if ::String === key || ::Symbol === key
hash[key] = value.deep_dup
else
hash.delete(key)
hash[key.deep_dup] = value.deep_dup
end
end
hash
end
end