Public utility methods for stringify/symbolize keys

This commit is contained in:
Max Lincoln 2014-12-23 15:06:40 -05:00 committed by dblock
parent 12f031b013
commit 0e40e63ebc
7 changed files with 272 additions and 118 deletions

View File

@ -7,6 +7,7 @@
* [#256](https://github.com/intridia/hashie/pull/256): Inherit key coercions - [@Erol](https://github.com/Erol).
* [#259](https://github.com/intridia/hashie/pull/259): Fix handling of default proc values in Mash - [@Erol](https://github.com/Erol).
* [#260](https://github.com/intridia/hashie/pull/260): Add block to Extensions::DeepMerge - [@galathius](https://github.com/galathius).
* [#254](https://github.com/intridea/hashie/pull/254): Public utility methods for stringify/symbolize keys - [@maxlinc](https://github.com/maxlinc).
* Your contribution here.
## 3.3.2 (11/26/2014)

View File

@ -141,6 +141,19 @@ end
The KeyConversion extension gives you the convenience methods of `symbolize_keys` and `stringify_keys` along with their bang counterparts. You can also include just stringify or just symbolize with `Hashie::Extensions::StringifyKeys` or `Hashie::Extensions::SymbolizeKeys`.
Hashie also has a utility method for converting keys on a Hash within including a mixin:
```ruby
Hashie.symbolize_keys! hash
# => Symbolizes keys of hash.
Hashie.symbolize_keys hash
# => Returns a copy of hash with keys symbolized.
Hashie.stringify_keys hash
# => Stringifies keys of hash.
Hashie.stringify_keys hash
# => Returns a copy of hash with keys stringified.
```
### MergeInitializer
The MergeInitializer extension simply makes it possible to initialize a Hash subclass with another Hash, giving you a quick short-hand.

View File

@ -38,4 +38,9 @@ module Hashie
autoload :SafeAssignment, 'hashie/extensions/mash/safe_assignment'
end
end
class << self
include Hashie::Extensions::StringifyKeys::ClassMethods
include Hashie::Extensions::SymbolizeKeys::ClassMethods
end
end

View File

@ -8,7 +8,7 @@ module Hashie
# test.stringify_keys!
# test # => {'abc' => 'def'}
def stringify_keys!
_stringify_keys!(self)
StringifyKeys.stringify_keys!(self)
self
end
@ -18,28 +18,50 @@ module Hashie
dup.stringify_keys!
end
protected
# Stringify all keys recursively within nested
# hashes and arrays.
def _stringify_keys_recursively!(object)
case object
when self.class
object.stringify_keys!
when ::Array
object.each do |i|
_stringify_keys_recursively!(i)
module ClassMethods
# Stringify all keys recursively within nested
# hashes and arrays.
# @api private
def stringify_keys_recursively!(object)
case object
when self.class
object.stringify_keys!
when ::Array
object.each do |i|
stringify_keys_recursively!(i)
end
when ::Hash
stringify_keys!(object)
end
end
# Convert all keys in the hash to strings.
#
# @param [::Hash] hash
# @example
# test = {:abc => 'def'}
# test.stringify_keys!
# test # => {'abc' => 'def'}
def stringify_keys!(hash)
hash.keys.each do |k|
stringify_keys_recursively!(hash[k])
hash[k.to_s] = hash.delete(k)
end
hash
end
# Return a copy of hash with all keys converted
# to strings.
# @param [::Hash] hash
def stringify_keys(hash)
hash.dup.tap do | new_hash |
stringify_keys! new_hash
end
when ::Hash
_stringify_keys!(object)
end
end
def _stringify_keys!(hash)
hash.keys.each do |k|
_stringify_keys_recursively!(hash[k])
hash[k.to_s] = hash.delete(k)
end
class << self
include ClassMethods
end
end
end

View File

@ -8,7 +8,7 @@ module Hashie
# test.symbolize_keys!
# test # => {:abc => 'def'}
def symbolize_keys!
_symbolize_keys!(self)
SymbolizeKeys.symbolize_keys!(self)
self
end
@ -18,28 +18,50 @@ module Hashie
dup.symbolize_keys!
end
protected
module ClassMethods
# Symbolize all keys recursively within nested
# hashes and arrays.
# @api private
def symbolize_keys_recursively!(object)
object.symbolize_keys! if object.respond_to? :symbolize_keys!
# Symbolize all keys recursively within nested
# hashes and arrays.
def _symbolize_keys_recursively!(object)
case object
when self.class
object.symbolize_keys!
when ::Array
object.each do |i|
_symbolize_keys_recursively!(i)
case object
when ::Array
object.each do |i|
symbolize_keys_recursively!(i)
end
when ::Hash
symbolize_keys!(object)
end
end
# Convert all keys in hash to symbols.
#
# @param [Hash] hash
# @example
# test = {'abc' => 'def'}
# Hashie.symbolize_keys! test
# test # => {:abc => 'def'}
def symbolize_keys!(hash)
hash.keys.each do |k|
symbolize_keys_recursively!(hash[k])
hash[k.to_sym] = hash.delete(k)
end
hash
end
# Return a copy of hash with all keys converted
# to symbols.
# @param [::Hash] hash
def symbolize_keys(hash)
hash.dup.tap do | new_hash |
symbolize_keys! new_hash
end
when ::Hash
_symbolize_keys!(object)
end
end
def _symbolize_keys!(hash)
hash.keys.each do |k|
_symbolize_keys_recursively!(hash[k])
hash[k.to_sym] = hash.delete(k)
end
class << self
include ClassMethods
end
end
end

View File

@ -1,58 +1,101 @@
require 'spec_helper'
require 'support/module_context'
def invoke(method)
if subject == object
subject.public_send(method)
else
subject.public_send(method, object)
end
end
shared_examples 'stringify_keys!' do
it 'converts keys to strings' do
object[:abc] = 'abc'
object[123] = '123'
invoke :stringify_keys!
expect((object.keys & %w(abc 123)).size).to eq 2
end
it 'converts nested instances of the same class' do
object[:ab] = dummy_class.new
object[:ab][:cd] = dummy_class.new
object[:ab][:cd][:ef] = 'abcdef'
invoke :stringify_keys!
expect(object).to eq('ab' => { 'cd' => { 'ef' => 'abcdef' } })
end
it 'converts nested hashes' do
object[:ab] = { cd: { ef: 'abcdef' } }
invoke :stringify_keys!
expect(object).to eq('ab' => { 'cd' => { 'ef' => 'abcdef' } })
end
it 'converts nested arrays' do
object[:ab] = []
object[:ab] << dummy_class.new
object[:ab] << dummy_class.new
object[:ab][0][:cd] = 'abcd'
object[:ab][1][:ef] = 'abef'
invoke :stringify_keys!
expect(object).to eq('ab' => [{ 'cd' => 'abcd' }, { 'ef' => 'abef' }])
end
end
shared_examples 'stringify_keys' do
it 'converts keys to strings' do
object[:abc] = 'def'
copy = invoke :stringify_keys
expect(copy['abc']).to eq 'def'
end
it 'does not alter the original' do
object[:abc] = 'def'
copy = invoke :stringify_keys
expect(object.keys).to eq [:abc]
expect(copy.keys).to eq %w(abc)
end
end
describe Hashie::Extensions::StringifyKeys do
include_context 'included hash module'
let(:object) { subject }
describe '#stringify_keys!' do
it 'converts keys to strings' do
subject[:abc] = 'abc'
subject[123] = '123'
subject.stringify_keys!
expect((subject.keys & %w(abc 123)).size).to eq 2
end
it 'converts nested instances of the same class' do
subject[:ab] = dummy_class.new
subject[:ab][:cd] = dummy_class.new
subject[:ab][:cd][:ef] = 'abcdef'
subject.stringify_keys!
expect(subject).to eq('ab' => { 'cd' => { 'ef' => 'abcdef' } })
end
it 'converts nested hashes' do
subject[:ab] = { cd: { ef: 'abcdef' } }
subject.stringify_keys!
expect(subject).to eq('ab' => { 'cd' => { 'ef' => 'abcdef' } })
end
it 'converts nested arrays' do
subject[:ab] = []
subject[:ab] << dummy_class.new
subject[:ab] << dummy_class.new
subject[:ab][0][:cd] = 'abcd'
subject[:ab][1][:ef] = 'abef'
subject.stringify_keys!
expect(subject).to eq('ab' => [{ 'cd' => 'abcd' }, { 'ef' => 'abef' }])
end
include_examples 'stringify_keys!'
it 'returns itself' do
expect(subject.stringify_keys!).to eq subject
end
end
describe '#stringify_keys' do
it 'converts keys to strings' do
subject[:abc] = 'def'
copy = subject.stringify_keys
expect(copy['abc']).to eq 'def'
end
context 'class methods' do
subject { described_class }
let(:object) { Hash.new }
it 'does not alter the original' do
subject[:abc] = 'def'
copy = subject.stringify_keys
expect(subject.keys).to eq [:abc]
expect(copy.keys).to eq %w(abc)
describe '.stringify_keys' do
include_examples 'stringify_keys'
end
describe '.stringify_keys!' do
include_examples 'stringify_keys!'
end
end
end
describe Hashie do
let!(:dummy_class) do
klass = Class.new(::Hash)
klass.send :include, Hashie::Extensions::StringifyKeys
klass
end
subject { described_class }
let(:object) { Hash.new }
describe '.stringify_keys' do
include_examples 'stringify_keys'
end
describe '.stringify_keys!' do
include_examples 'stringify_keys!'
end
end

View File

@ -1,40 +1,69 @@
require 'spec_helper'
require 'support/module_context'
def invoke(method)
if subject == object
subject.public_send(method)
else
subject.public_send(method, object)
end
end
shared_examples 'symbolize_keys!' do
it 'converts keys to symbols' do
object['abc'] = 'abc'
object['def'] = 'def'
invoke :symbolize_keys!
expect((object.keys & [:abc, :def]).size).to eq 2
end
it 'converts nested instances of the same class' do
object['ab'] = dummy_class.new
object['ab']['cd'] = dummy_class.new
object['ab']['cd']['ef'] = 'abcdef'
invoke :symbolize_keys!
expect(object).to eq(ab: { cd: { ef: 'abcdef' } })
end
it 'converts nested hashes' do
object['ab'] = { 'cd' => { 'ef' => 'abcdef' } }
invoke :symbolize_keys!
expect(object).to eq(ab: { cd: { ef: 'abcdef' } })
end
it 'performs deep conversion within nested arrays' do
object['ab'] = []
object['ab'] << dummy_class.new
object['ab'] << dummy_class.new
object['ab'][0]['cd'] = 'abcd'
object['ab'][1]['ef'] = 'abef'
new_object = invoke :symbolize_keys
expect(new_object).to eq(ab: [{ cd: 'abcd' }, { ef: 'abef' }])
end
end
shared_examples 'symbolize_keys' do
it 'converts keys to symbols' do
object['abc'] = 'def'
copy = invoke :symbolize_keys
expect(copy[:abc]).to eq 'def'
end
it 'does not alter the original' do
object['abc'] = 'def'
copy = invoke :symbolize_keys
expect(object.keys).to eq ['abc']
expect(copy.keys).to eq [:abc]
end
end
describe Hashie::Extensions::SymbolizeKeys do
include_context 'included hash module'
let(:object) { subject }
describe '#symbolize_keys!' do
it 'converts keys to symbols' do
subject['abc'] = 'abc'
subject['def'] = 'def'
subject.symbolize_keys!
expect((subject.keys & [:abc, :def]).size).to eq 2
end
it 'converts nested instances of the same class' do
subject['ab'] = dummy_class.new
subject['ab']['cd'] = dummy_class.new
subject['ab']['cd']['ef'] = 'abcdef'
subject.symbolize_keys!
expect(subject).to eq(ab: { cd: { ef: 'abcdef' } })
end
it 'converts nested hashes' do
subject['ab'] = { 'cd' => { 'ef' => 'abcdef' } }
subject.symbolize_keys!
expect(subject).to eq(ab: { cd: { ef: 'abcdef' } })
end
it 'performs deep conversion within nested arrays' do
subject['ab'] = []
subject['ab'] << dummy_class.new
subject['ab'] << dummy_class.new
subject['ab'][0]['cd'] = 'abcd'
subject['ab'][1]['ef'] = 'abef'
subject.symbolize_keys!
expect(subject).to eq(ab: [{ cd: 'abcd' }, { ef: 'abef' }])
end
include_examples 'symbolize_keys!'
let(:object) { subject }
it 'returns itself' do
expect(subject.symbolize_keys!).to eq subject
@ -42,17 +71,36 @@ describe Hashie::Extensions::SymbolizeKeys do
end
describe '#symbolize_keys' do
it 'converts keys to symbols' do
subject['abc'] = 'def'
copy = subject.symbolize_keys
expect(copy[:abc]).to eq 'def'
end
include_examples 'symbolize_keys'
end
it 'does not alter the original' do
subject['abc'] = 'def'
copy = subject.symbolize_keys
expect(subject.keys).to eq ['abc']
expect(copy.keys).to eq [:abc]
context 'class methods' do
subject { described_class }
let(:object) { Hash.new }
describe '.symbolize_keys' do
include_examples 'symbolize_keys'
end
describe '.symbolize_keys!' do
include_examples 'symbolize_keys!'
end
end
end
describe Hashie do
let!(:dummy_class) do
klass = Class.new(::Hash)
klass.send :include, Hashie::Extensions::StringifyKeys
klass
end
subject { described_class }
let(:object) { Hash.new }
describe '.symbolize_keys' do
include_examples 'symbolize_keys'
end
describe '.symbolize_keys!' do
include_examples 'symbolize_keys!'
end
end