Skip to content

Commit

Permalink
Improve our SafeDup module
Browse files Browse the repository at this point in the history
Include Refiment for True, False, Integer and Float classes
to be able to call #dup on ruby 2.1

The method frozen_or_dup for ruby 2.3 an oward only epxect to receive
String values. by checking if the value we can warranty same behavior
for all kind of objects
  • Loading branch information
GustavoCaso committed Jul 12, 2023
1 parent f168a6f commit 63cbbc8
Show file tree
Hide file tree
Showing 2 changed files with 287 additions and 39 deletions.
60 changes: 55 additions & 5 deletions lib/datadog/core/utils/safe_dup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,70 @@ def dup
end
end

# Ensures #initialize can call true.dup safely
module RefineTrue
refine TrueClass do
def dup
self
end
end
end

# Ensures #initialize can call false.dup safely
module RefineFalse
refine FalseClass do
def dup
self
end
end
end

# Ensures #initialize can call 1.dup safely
module RefineInteger
refine Integer do
def dup
self
end
end
end

# Ensures #initialize can call 1.0.dup safely
module RefineFloat
refine Float do
def dup
self
end
end
end

using RefineNil
using RefineTrue
using RefineFalse
using RefineInteger
using RefineFloat
end

# String#+@ was introduced in Ruby 2.3
if String.method_defined?(:+@) && String.method_defined?(:-@)
def self.frozen_or_dup(v)
# If the string is not frozen, the +(-v) will:
# - first create a frozen deduplicated copy with -v
# - then it will dup it more efficiently with +v
v.frozen? ? v : +(-v)
case v
when String
# If the string is not frozen, the +(-v) will:
# - first create a frozen deduplicated copy with -v
# - then it will dup it more efficiently with +v
v.frozen? ? v : +(-v)
else
v.frozen? ? v : v.dup
end
end

def self.frozen_dup(v)
-v if v
case v
when String
-v if v
else
v.frozen? ? v : v.dup.freeze
end
end
else
def self.frozen_or_dup(v)
Expand Down
266 changes: 232 additions & 34 deletions spec/datadog/core/utils/safe_dup_spec.rb
Original file line number Diff line number Diff line change
@@ -1,62 +1,260 @@
require 'datadog/core/utils/safe_dup'

RSpec.describe Datadog::Core::Utils::SafeDup do
describe '.frozen_or_dup' do
context 'when given a frozen string' do
it 'returns the original input' do
input = 'a_frozen_string'.freeze
context 'String' do
describe '.frozen_or_dup' do
context 'when given a frozen string' do
it 'returns the original input' do
input = 'a_frozen_string'.freeze

result = described_class.frozen_or_dup(input)
result = described_class.frozen_or_dup(input)

expect(input).to be_frozen
expect(input).to be_frozen

expect(result).to eq(input)
expect(result).to be(input)
expect(result).to be_frozen
expect(result).to eq(input)
expect(result).to be(input)
expect(result).to be_frozen
end
end

context 'when given a string' do
it 'returns a non frozen dupliacte' do
input = 'a_string'

result = described_class.frozen_or_dup(input)

expect(input).not_to be_frozen

expect(result).to eq(input)
expect(result).not_to be(input)
expect(result).not_to be_frozen
end
end
end

describe '.frozen_dup' do
context 'when given a frozen string' do
it 'returns the original input' do
input = 'a_frozen_string'.freeze

result = described_class.frozen_dup(input)

expect(input).to be_frozen

expect(result).to eq(input)
expect(result).to be(input)
expect(result).to be_frozen
end
end

context 'when given a string' do
it 'returns a frozen duplicate' do
input = 'a_string'

result = described_class.frozen_dup(input)

expect(input).not_to be_frozen

expect(result).to eq(input)
expect(result).not_to be(input)
expect(result).to be_frozen
end
end
end
end

context 'Hash' do
describe '.frozen_or_dup' do
context 'when given a frozen hash' do
it 'returns the original input' do
input = { a: :b }.freeze

result = described_class.frozen_or_dup(input)

expect(input).to be_frozen

expect(result).to eq(input)
expect(result).to be(input)
expect(result).to be_frozen
end
end

context 'when given a hash' do
it 'returns a non frozen dupliacte' do
input = { a: :b }

result = described_class.frozen_or_dup(input)

expect(input).not_to be_frozen

expect(result).to eq(input)
expect(result).not_to be(input)
expect(result).not_to be_frozen
end
end
end

context 'when given a string' do
it 'returns a non frozen dupliacte' do
input = 'a_string'
describe '.frozen_dup' do
context 'when given a frozen hash' do
it 'returns the original input' do
input = { a: :b }.freeze

result = described_class.frozen_dup(input)

expect(input).to be_frozen

expect(result).to eq(input)
expect(result).to be(input)
expect(result).to be_frozen
end
end

context 'when given a hash' do
it 'returns a frozen duplicate' do
input = { a: :b }

result = described_class.frozen_dup(input)

expect(input).not_to be_frozen

expect(result).to eq(input)
expect(result).not_to be(input)
expect(result).to be_frozen
end
end
end
end

context 'Boolean' do
before do
skip 'TrueClass and FalseClass are not frozen by default on ruby 2.1' if RUBY_VERSION < '2.2'
end

describe '.frozen_or_dup' do
context 'when given a boolean' do
it 'returns the original input' do
input = true

result = described_class.frozen_or_dup(input)

expect(input).to be_frozen

expect(result).to eq(input)
expect(result).to be(input)
expect(result).to be_frozen
end
end
end

describe '.frozen_dup' do
context 'when given a boolean' do
it 'returns the original input' do
input = true

result = described_class.frozen_dup(input)

expect(input).to be_frozen

expect(result).to eq(input)
expect(result).to be(input)
expect(result).to be_frozen
end
end
end
end

context 'Array' do
describe '.frozen_or_dup' do
context 'when given a frozen array' do
it 'returns the original input' do
input = [1].freeze

result = described_class.frozen_or_dup(input)

expect(input).to be_frozen

expect(result).to eq(input)
expect(result).to be(input)
expect(result).to be_frozen
end
end

context 'when given a string' do
it 'returns a non frozen array' do
input = [1]

result = described_class.frozen_or_dup(input)

expect(input).not_to be_frozen

expect(result).to eq(input)
expect(result).not_to be(input)
expect(result).not_to be_frozen
end
end
end

describe '.frozen_dup' do
context 'when given a frozen array' do
it 'returns the original input' do
input = [1].freeze

result = described_class.frozen_dup(input)

expect(input).to be_frozen

expect(result).to eq(input)
expect(result).to be(input)
expect(result).to be_frozen
end
end

context 'when given a array' do
it 'returns a frozen duplicate' do
input = [1]

result = described_class.frozen_or_dup(input)
result = described_class.frozen_dup(input)

expect(input).not_to be_frozen
expect(input).not_to be_frozen

expect(result).to eq(input)
expect(result).not_to be(input)
expect(result).not_to be_frozen
expect(result).to eq(input)
expect(result).not_to be(input)
expect(result).to be_frozen
end
end
end
end

describe '.frozen_dup' do
context 'when given a frozen string' do
it 'returns the original input' do
input = 'a_frozen_string'.freeze
context 'Numeric' do
describe '.frozen_or_dup' do
context 'when given a numeric' do
it 'returns the original input' do
input = 1

result = described_class.frozen_dup(input)
result = described_class.frozen_or_dup(input)

expect(input).to be_frozen
expect(input).to be_frozen

expect(result).to eq(input)
expect(result).to be(input)
expect(result).to be_frozen
expect(result).to eq(input)
expect(result).to be(input)
expect(result).to be_frozen
end
end
end

context 'when given a string' do
it 'returns a frozen duplicate' do
input = 'a_string'
describe '.frozen_dup' do
context 'when given a numeric' do
it 'returns the original input' do
input = 10.0

result = described_class.frozen_dup(input)
result = described_class.frozen_dup(input)

expect(input).not_to be_frozen
expect(input).to be_frozen

expect(result).to eq(input)
expect(result).not_to be(input)
expect(result).to be_frozen
expect(result).to eq(input)
expect(result).to be(input)
expect(result).to be_frozen
end
end
end
end
Expand Down

0 comments on commit 63cbbc8

Please sign in to comment.