Created
November 3, 2009 13:38
-
-
Save JoshCheek/225048 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module SymbolizeKeys | |
# converts any current string keys to symbol keys | |
def self.extended(hash) | |
hash.each do |key,value| | |
if key.is_a?(String) | |
hash.delete key | |
hash[key] = value #through overridden []= | |
end | |
end | |
end | |
#considers string keys and symbol keys to be the same | |
def [](key) | |
super convert_key(key) | |
end | |
#considers string keys and symbol keys to be the same | |
def has_key?(key) | |
super convert_key(key) | |
end | |
# assigns a new key/value pair | |
# converts they key to a symbol if it is a string | |
def []=(*args) | |
args[0] = convert_key(args[0]) | |
super | |
end | |
# returns new hash which is the merge of self and other hashes | |
# the returned hash will also be extended by SymbolizeKeys | |
def merge(*other_hashes , &resolution_proc ) | |
merged = Hash.new.extend SymbolizeKeys | |
merged.merge! self , *other_hashes , &resolution_proc | |
end | |
# merges the other hashes into self | |
# if a proc is submitted , it's return will be the value for the key | |
def merge!( *other_hashes , &resolution_proc ) | |
# default resolution: value of the other hash | |
resolution_proc ||= proc{ |key,oldval,newval| newval } | |
# merge each hash into self | |
other_hashes.each do |hash| | |
hash.each{ |k,v| | |
# assign new k/v into self, resolving conflicts with resolution_proc | |
self[k] = self.has_key?(k) ? resolution_proc[convert_key(k),self[k],v] : v | |
} | |
end | |
self | |
end | |
private | |
def convert_key(key) | |
key.is_a?(String) ? key.to_sym : key | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'test/unit' | |
require 'symbolize_keys' | |
# this method was written by Gregory Brown | |
# and comes from http://github.com/sandal/rbp/blob/7db25d4873042c02eca7232ad18e2432f0db7cb8/testing/test_unit_extensions.rb | |
module Test::Unit | |
# Used to fix a minor minitest/unit incompatibility in flexmock | |
AssertionFailedError = Class.new(StandardError) | |
class TestCase | |
def self.must(name, &block) | |
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym | |
defined = instance_method(test_name) rescue false | |
raise "#{test_name} is already defined in #{self}" if defined | |
if block_given? | |
define_method(test_name, &block) | |
else | |
define_method(test_name) do | |
flunk "No implementation provided for #{name}" | |
end | |
end | |
end | |
end | |
end | |
class ExtendingWithSymbolizeKeysTest < Test::Unit::TestCase | |
def setup | |
@default = { | |
:age => 50 , | |
'initially a string' => 51 , | |
/neither string nor symbol/ => 52 , | |
} | |
@default.extend SymbolizeKeys | |
end | |
must "convert string keys to symbols when extended" do | |
assert_equal @default[:'initially a string'] , 51 | |
end | |
must "sym/str keys can access through either, but only one key" do | |
assert_equal @default[ 'initially a string'] , 51 | |
assert_equal @default[:'initially a string'] , 51 | |
assert_equal @default[:'initially a string'] , @default['initially a string'] | |
assert_equal @default.size , 3 | |
end | |
must "leave symbol keys as symbols" do | |
assert_equal @default[:age] , 50 | |
end | |
must 'leave non symbols / strings as they are' do | |
assert_equal @default[/neither string nor symbol/] , 52 | |
end | |
end | |
class SettingKeysWithSymbolizeKeysTest < Test::Unit::TestCase | |
def setup | |
@default = Hash.new.extend SymbolizeKeys | |
end | |
must "enable access to strings through symbols" do | |
@default['foo'] = 'bar' | |
assert_equal @default[:foo] , 'bar' | |
assert_same @default[:foo] , @default['foo'] | |
end | |
must "enable access to symbols through strings" do | |
@default[:foo] = 'bar' | |
assert_equal @default['foo'] , 'bar' | |
assert_same @default[:foo] , @default['foo'] | |
end | |
must 'leave non symbols / strings as they are' do | |
@default[/foo/] = :bar | |
assert_equal @default[/foo/] , :bar | |
end | |
end | |
class MergingWithSymbolizeKeysTest < Test::Unit::TestCase | |
def setup | |
@default = { | |
:name => 'Joe' , | |
:age => 50 , | |
'initially a string' => 51 , | |
/neither string nor symbol/ => 52 , | |
:'from default' => :default , | |
} | |
@params1 = { | |
:name => 'Bill' , | |
'alias' => 'Billy' , | |
:'from params1' => :params1 , | |
} | |
@params2 = { | |
'name' => 'Sam' , | |
'alias' => 'Sammy' , | |
12 => 'favourite number' , | |
:'from params2' => :params2 , | |
} | |
@default.extend SymbolizeKeys | |
end | |
must "retain new keys for merge" do | |
merged = @default.merge(@params2) | |
assert_equal merged[12] , 'favourite number' | |
end | |
must "retain new keys for merge!" do | |
@default.merge!(@params2) | |
assert_equal @default[12] , 'favourite number' | |
end | |
must "replace current values with new values for merge" do | |
merged = @default.merge(@params1) | |
assert_equal merged[:name] , 'Bill' | |
end | |
must "replace current values with new values for merge!" do | |
@default.merge!(@params1) | |
assert_equal @default[:name] , 'Bill' | |
end | |
must "not change original hash for merge" do | |
@default.merge(@params1) | |
assert_equal @default[:name] , 'Joe' | |
end | |
must "receive [key,oldval,newval] as params to block" do | |
h1 = {:no_conflict_1 => 1 , :conflict => 2 }.extend(SymbolizeKeys) | |
h2 = {:conflict => 3 , :no_conflict_2 => 4} | |
resolution_proc_params = nil | |
h1.merge(h2){ |*params| resolution_proc_params = params } | |
assert_equal resolution_proc_params , [:conflict,2,3] | |
end | |
must "only invoke the resolution proc on conflicts" do | |
conflict_count = 0 | |
conflicts = { :name => false , :alias => false } | |
@params1.extend(SymbolizeKeys).merge(@params2) do |k,ov,nv| | |
conflict_count += 1 | |
conflicts[k] = true | |
end | |
assert_equal conflict_count , 2 | |
assert conflicts[:name] | |
assert conflicts[:alias] | |
end | |
must "replace resolve conflicts with block for merge" do | |
merged = @default.merge(@params1){ |key,oldval,newval| oldval } | |
assert_equal merged[:name] , 'Joe' | |
merged = @default.merge(@params1){ |key,oldval,newval| newval } | |
assert_equal merged[:name] , 'Bill' | |
end | |
must "replace resolve conflicts with block for merge!" do | |
@default.merge!(@params1){ |key,oldval,newval| oldval } | |
assert_equal @default[:name] , 'Joe' | |
@default.merge!(@params1){ |key,oldval,newval| newval } | |
assert_equal @default[:name] , 'Bill' | |
end | |
must "convert string keys to symbols for merge" do | |
unique_keys = @default.keys.map{|k|k.to_s.to_sym} | @params1.keys.map{|k|k.to_s.to_sym} | |
merged = @default.merge(@params1) | |
assert_equal merged['alias'] , 'Billy' | |
assert_equal merged[ :alias] , 'Billy' | |
assert_equal merged.size , unique_keys.size | |
end | |
must "convert string keys to symbols for merge!" do | |
unique_keys = @default.keys.map{|k|k.to_s.to_sym} | @params1.keys.map{|k|k.to_s.to_sym} | |
@default.merge!(@params1) | |
assert_equal @default['alias'] , 'Billy' | |
assert_equal @default[ :alias] , 'Billy' | |
assert_equal @default.size , unique_keys.size | |
end | |
must "merge with multiple hashes" do | |
merged = @default.merge(@params1,@params2) | |
assert_equal merged[:'from default'] , :default | |
assert_equal merged[:'from params1'] , :params1 | |
assert_equal merged[:'from params2'] , :params2 | |
end | |
must "merge! with multiple hashes" do | |
@default.merge!(@params1,@params2) | |
assert_equal @default[:'from default'] , :default | |
assert_equal @default[:'from params1'] , :params1 | |
assert_equal @default[:'from params2'] , :params2 | |
end | |
must "return object that is extended with SymbolizeKeys, for merge" do | |
merged = @default.merge(@params1) | |
assert_kind_of SymbolizeKeys , merged | |
end | |
must "not modify original hash, for merge" do | |
original = @default.dup | |
@default.merge(@params1,@params2) | |
assert_equal original , @default | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment