Implement non-destructive hash methods

When calling the following non-destructive hash methods:
:compact
:invert
:reject
:select
:slice
:transform_keys
:transform_values
we would be returned an instance of a standard Hash rather
than a Mash (or subclass). This changes that behavior to
instead return an instance of the class the method was
called on.
This commit is contained in:
Bobby McDonald 2019-08-13 23:38:06 -04:00
parent 1de19bff87
commit 3e64a4f58d
No known key found for this signature in database
GPG Key ID: CAD931A49619329A
3 changed files with 196 additions and 3 deletions

View File

@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2019-07-17 09:23:49 -0400 using RuboCop version 0.52.1.
# on 2019-08-13 23:33:30 -0400 using RuboCop version 0.52.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
@ -13,13 +13,13 @@ Metrics/AbcSize:
# Offense count: 2
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 221
Max: 266
# Offense count: 7
Metrics/CyclomaticComplexity:
Max: 11
# Offense count: 19
# Offense count: 18
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 28

View File

@ -207,6 +207,31 @@ module Hashie
super(*keys.map { |key| convert_key(key) })
end
# Returns a new instance of the class it was called on, with nil values
# removed.
def compact
self.class.new(super)
end
# Returns a new instance of the class it was called on, using its keys as
# values, and its values as keys. The new values and keys will always be
# strings.
def invert
self.class.new(super)
end
# Returns a new instance of the class it was called on, containing elements
# for which the given block returns false.
def reject(&blk)
self.class.new(super(&blk))
end
# Returns a new instance of the class it was called on, containing elements
# for which the given block returns true.
def select(&blk)
self.class.new(super(&blk))
end
alias regular_dup dup
# Duplicates the current mash as a new mash.
def dup
@ -320,6 +345,23 @@ module Hashie
end
end
with_minimum_ruby('2.4.0') do
def transform_values(&blk)
self.class.new(super(&blk))
end
end
with_minimum_ruby('2.5.0') do
def slice(*keys)
string_keys = keys.map { |key| convert_key(key) }
self.class.new(super(*string_keys))
end
def transform_keys(&blk)
self.class.new(super(&blk))
end
end
protected
def method_name_and_suffix(method_name)

View File

@ -878,6 +878,90 @@ describe Hashie::Mash do
end
end
describe '#compact' do
subject(:mash) { described_class.new(a: 1, b: nil) }
it 'returns a Hashie::Mash' do
expect(mash.compact).to be_kind_of(described_class)
end
it 'removes keys with nil values' do
expect(mash.compact).to eq('a' => 1)
end
context 'when using with subclass' do
let(:subclass) { Class.new(Hashie::Mash) }
subject(:sub_mash) { subclass.new(a: 1, b: nil) }
it 'creates an instance of subclass' do
expect(sub_mash.compact).to be_kind_of(subclass)
end
end
end
describe '#invert' do
subject(:mash) { described_class.new(a: 'apple', b: 4) }
it 'returns a Hashie::Mash' do
expect(mash.invert).to be_kind_of(described_class)
end
it 'returns a mash with the keys and values inverted' do
expect(mash.invert).to eq('apple' => 'a', '4' => 'b')
end
context 'when using with subclass' do
let(:subclass) { Class.new(Hashie::Mash) }
subject(:sub_mash) { subclass.new(a: 1, b: nil) }
it 'creates an instance of subclass' do
expect(sub_mash.invert).to be_kind_of(subclass)
end
end
end
describe '#reject' do
subject(:mash) { described_class.new(a: 1, b: nil) }
it 'returns a Hashie::Mash' do
expect(mash.reject { |_k, v| v.nil? }).to be_kind_of(described_class)
end
it 'rejects keys for which the block returns true' do
expect(mash.reject { |_k, v| v.nil? }).to eq('a' => 1)
end
context 'when using with subclass' do
let(:subclass) { Class.new(Hashie::Mash) }
subject(:sub_mash) { subclass.new(a: 1, b: nil) }
it 'creates an instance of subclass' do
expect(sub_mash.reject { |_k, v| v.nil? }).to be_kind_of(subclass)
end
end
end
describe '#select' do
subject(:mash) { described_class.new(a: 'apple', b: 4) }
it 'returns a Hashie::Mash' do
expect(mash.select { |_k, v| v.is_a? String }).to be_kind_of(described_class)
end
it 'selects keys for which the block returns true' do
expect(mash.select { |_k, v| v.is_a? String }).to eq('a' => 'apple')
end
context 'when using with subclass' do
let(:subclass) { Class.new(Hashie::Mash) }
subject(:sub_mash) { subclass.new(a: 1, b: nil) }
it 'creates an instance of subclass' do
expect(sub_mash.select { |_k, v| v.is_a? String }).to be_kind_of(subclass)
end
end
end
with_minimum_ruby('2.3.0') do
describe '#dig' do
subject { described_class.new(a: { b: 1 }) }
@ -895,4 +979,71 @@ describe Hashie::Mash do
end
end
end
with_minimum_ruby('2.4.0') do
describe '#transform_values' do
subject(:mash) { described_class.new(a: 1) }
it 'returns a Hashie::Mash' do
expect(mash.transform_values(&:to_s)).to be_kind_of(described_class)
end
it 'transforms the value' do
expect(mash.transform_values(&:to_s).a).to eql('1')
end
context 'when using with subclass' do
let(:subclass) { Class.new(Hashie::Mash) }
subject(:sub_mash) { subclass.new(a: 1).transform_values { |a| a + 2 } }
it 'creates an instance of subclass' do
expect(sub_mash).to be_kind_of(subclass)
end
end
end
end
with_minimum_ruby('2.5.0') do
describe '#slice' do
subject(:mash) { described_class.new(a: 1, b: 2) }
it 'returns a Hashie::Mash' do
expect(mash.slice(:a)).to be_kind_of(described_class)
end
it 'returns a Mash with only the keys passed' do
expect(mash.slice(:a).to_hash).to eq('a' => 1)
end
context 'when using with subclass' do
let(:subclass) { Class.new(Hashie::Mash) }
subject(:sub_mash) { subclass.new(a: 1, b: 2) }
it 'creates an instance of subclass' do
expect(sub_mash.slice(:a)).to be_kind_of(subclass)
end
end
end
describe '#transform_keys' do
subject(:mash) { described_class.new(a: 1, b: 2) }
it 'returns a Hashie::Mash' do
expect(mash.transform_keys { |k| k + k }).to be_kind_of(described_class)
end
it 'returns a Mash with transformed keys' do
expect(mash.transform_keys { |k| k + k }).to eq('aa' => 1, 'bb' => 2)
end
context 'when using with subclass' do
let(:subclass) { Class.new(Hashie::Mash) }
subject(:sub_mash) { subclass.new(a: 1, b: 2) }
it 'creates an instance of subclass' do
expect(sub_mash.transform_keys { |k| k + k }).to be_kind_of(subclass)
end
end
end
end
end