aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/core_ext/object/deep_dup.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport/lib/active_support/core_ext/object/deep_dup.rb')
-rw-r--r--activesupport/lib/active_support/core_ext/object/deep_dup.rb55
1 files changed, 55 insertions, 0 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
new file mode 100644
index 0000000000..c66c5eb2d9
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/object/deep_dup.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require "active_support/core_ext/object/duplicable"
+
+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 key.frozen? && ::String === key
+ hash[key] = value.deep_dup
+ else
+ hash.delete(key)
+ hash[key.deep_dup] = value.deep_dup
+ end
+ end
+ hash
+ end
+end