Created
November 1, 2009 02:42
-
-
Save methodmissing/223370 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| methodmissing:ruby lourens$ ./miniruby -Ilib -rtest/unit test/ruby/test_hash.rb | |
| Loaded suite test/ruby/test_hash | |
| Started | |
| .....E..................................................................EF.......F.....F... | |
| Finished in 0.019967 seconds. | |
| 1) Error: | |
| test_callcc(TestHash): | |
| NoMethodError: undefined method `callcc' for #<TestHash:0x0000010101e9e0> | |
| test/ruby/test_hash.rb:818:in `block in test_callcc' | |
| test/ruby/test_hash.rb:818:in `each' | |
| test/ruby/test_hash.rb:818:in `test_callcc' | |
| 2) Error: | |
| test_hash_with_array_of_hashes(TestStrHash): | |
| NoMethodError: undefined method `first' for nil:NilClass | |
| test/ruby/test_hash.rb:1088:in `test_hash_with_array_of_hashes' | |
| 3) Failure: | |
| test_indifferent_subhashes(TestStrHash) [test/ruby/test_hash.rb:1093]: | |
| h["user"][:id] should be 5. | |
| <5> expected but was | |
| <nil>. | |
| 4) Failure: | |
| test_set(TestStrHash) [test/ruby/test_hash.rb:915]: | |
| Expected {"a"=>1, "b"=>2} to be an instance of StrHash, not Hash. | |
| 5) Failure: | |
| test_to_hash(TestStrHash) [test/ruby/test_hash.rb:1072]: | |
| Expected {"a"=>1, "b"=>2} to be an instance of Hash, not StrHash. | |
| 91 tests, 521 assertions, 3 failures, 2 errors, 0 skips |
This file contains hidden or 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
| diff --git a/hash.c b/hash.c | |
| index ef8b9a8..b6cd560 100644 | |
| --- a/hash.c | |
| +++ b/hash.c | |
| @@ -23,6 +23,25 @@ static VALUE rb_hash_s_try_convert(VALUE, VALUE); | |
| #define HASH_DELETED FL_USER1 | |
| #define HASH_PROC_DEFAULT FL_USER2 | |
| +#define STR_HASH FL_USER3 | |
| + | |
| +static VALUE rb_hash_rehash(VALUE hash); | |
| +static VALUE rb_hash_update(VALUE hash1, VALUE hash2); | |
| +static VALUE rb_hash_strhash(VALUE hash); | |
| +static void rb_strhash_convert(VALUE *value); | |
| + | |
| +#define NEW_STR_HASH(hash,other) do {\ | |
| + FL_SET(RHASH(hash), STR_HASH); \ | |
| + RHASH(hash)->ntbl->type = &strhash; \ | |
| + RHASH(hash)->ifnone = RHASH(other)->ifnone; \ | |
| + return rb_hash_rehash(rb_convert_type(hash, T_HASH, "StrHash", "to_hash")); \ | |
| +} while (0) | |
| + | |
| +#define CONVERT_STR_HASH(hash,value) do {\ | |
| + if FL_TEST(RHASH(hash), STR_HASH){ \ | |
| + rb_strhash_convert(&value); \ | |
| + } \ | |
| +} while (0) | |
| VALUE | |
| rb_hash_freeze(VALUE hash) | |
| @@ -31,6 +50,7 @@ rb_hash_freeze(VALUE hash) | |
| } | |
| VALUE rb_cHash; | |
| +VALUE rb_cStrHash; | |
| static VALUE envtbl; | |
| static ID id_hash, id_yield, id_default; | |
| @@ -54,6 +74,37 @@ rb_any_cmp(VALUE a, VALUE b) | |
| return !rb_eql(a, b); | |
| } | |
| +int | |
| +strhash_cmp(VALUE *s1,VALUE *s2) | |
| +{ | |
| + int s1_hash = SYMBOL_P(*s1) ? rb_sym_hash(*s1) : rb_str_hash(*s1); | |
| + int s2_hash = SYMBOL_P(*s2) ? rb_sym_hash(*s2) : rb_str_hash(*s2); | |
| + if (s1_hash == s2_hash) return 0; | |
| + if (s1_hash > s2_hash) return 1; | |
| + return -1; | |
| +} | |
| + | |
| +static int | |
| +rb_strhash_cmp(VALUE a, VALUE b) | |
| +{ | |
| + if (a == b) return 0; | |
| + if (FIXNUM_P(a) && FIXNUM_P(b)) { | |
| + return a != b; | |
| + } | |
| + if (a == Qundef || b == Qundef) return -1; | |
| + if (SYMBOL_P(a) && SYMBOL_P(b)) { | |
| + return a != b; | |
| + } | |
| + if ((TYPE(a) == T_STRING && RBASIC(a)->klass == rb_cString && SYMBOL_P(b)) || (TYPE(b) == T_STRING && RBASIC(b)->klass == rb_cString && SYMBOL_P(a))) { | |
| + return strhash_cmp(&a, &b); | |
| + } | |
| + if (TYPE(a) == T_STRING && RBASIC(a)->klass == rb_cString && | |
| + TYPE(b) == T_STRING && RBASIC(b)->klass == rb_cString) { | |
| + return rb_str_cmp(a, b); | |
| + } | |
| + return !rb_eql(a, b); | |
| +} | |
| + | |
| VALUE | |
| rb_hash(VALUE obj) | |
| { | |
| @@ -99,11 +150,44 @@ rb_any_hash(VALUE a) | |
| return (st_index_t)RSHIFT(hnum, 1); | |
| } | |
| +static st_index_t | |
| +rb_strhash_hash(VALUE a) | |
| +{ | |
| + VALUE hval; | |
| + st_index_t hnum; | |
| + | |
| + switch (TYPE(a)) { | |
| + case T_FIXNUM: | |
| + case T_NIL: | |
| + case T_FALSE: | |
| + case T_TRUE: | |
| + hnum = rb_hash_end(rb_hash_start((unsigned int)a)); | |
| + break; | |
| + case T_SYMBOL: | |
| + hnum = rb_sym_hash(a); | |
| + break; | |
| + case T_STRING: | |
| + hnum = rb_str_hash(a); | |
| + break; | |
| + | |
| + default: | |
| + hval = rb_hash(a); | |
| + hnum = FIX2LONG(hval); | |
| + } | |
| + hnum <<= 1; | |
| + return (st_index_t)RSHIFT(hnum, 1); | |
| +} | |
| + | |
| static const struct st_hash_type objhash = { | |
| rb_any_cmp, | |
| rb_any_hash, | |
| }; | |
| +static const struct st_hash_type strhash = { | |
| + rb_strhash_cmp, | |
| + rb_strhash_hash, | |
| +}; | |
| + | |
| static const struct st_hash_type identhash = { | |
| st_numcmp, | |
| st_numhash, | |
| @@ -219,7 +303,9 @@ hash_alloc(VALUE klass) | |
| OBJSETUP(hash, klass, T_HASH); | |
| hash->ifnone = Qnil; | |
| - | |
| + if (klass == rb_cStrHash){ | |
| + FL_SET(hash, STR_HASH); | |
| + } | |
| return (VALUE)hash; | |
| } | |
| @@ -230,6 +316,12 @@ rb_hash_new(void) | |
| } | |
| VALUE | |
| +rb_strhash_new(void) | |
| +{ | |
| + return hash_alloc(rb_cStrHash); | |
| +} | |
| + | |
| +VALUE | |
| rb_hash_dup(VALUE hash) | |
| { | |
| NEWOBJ(ret, struct RHash); | |
| @@ -240,8 +332,33 @@ rb_hash_dup(VALUE hash) | |
| if (FL_TEST(hash, HASH_PROC_DEFAULT)) { | |
| FL_SET(ret, HASH_PROC_DEFAULT); | |
| } | |
| - ret->ifnone = RHASH(hash)->ifnone; | |
| - return (VALUE)ret; | |
| + if (FL_TEST(hash, STR_HASH)){ | |
| + NEW_STR_HASH(ret,hash); | |
| + }else{ | |
| + ret->ifnone = RHASH(hash)->ifnone; | |
| + return (VALUE)ret; | |
| + } | |
| +} | |
| + | |
| +static void | |
| +rb_strhash_convert(VALUE *val) | |
| +{ | |
| + int i; | |
| + VALUE values; | |
| + | |
| + switch (TYPE(*val)) { | |
| + case T_HASH: | |
| + *val = rb_hash_strhash(*val); | |
| + break; | |
| + case T_ARRAY: | |
| + values = rb_ary_new2(RARRAY_LEN(*val)); | |
| + for (i = 0; i < RARRAY_LEN(*val); i++) { | |
| + VALUE el = RARRAY_PTR(*val)[i]; | |
| + rb_ary_push(values, (TYPE(el) == T_HASH) ? rb_hash_strhash(el) : el); | |
| + } | |
| + *val = values; | |
| + break; | |
| + } | |
| } | |
| static void | |
| @@ -256,7 +373,7 @@ struct st_table * | |
| rb_hash_tbl(VALUE hash) | |
| { | |
| if (!RHASH(hash)->ntbl) { | |
| - RHASH(hash)->ntbl = st_init_table(&objhash); | |
| + RHASH(hash)->ntbl = FL_TEST(hash, STR_HASH) ? st_init_table(&strhash) : st_init_table(&objhash); | |
| } | |
| return RHASH(hash)->ntbl; | |
| } | |
| @@ -318,7 +435,11 @@ static VALUE | |
| rb_hash_initialize(int argc, VALUE *argv, VALUE hash) | |
| { | |
| VALUE ifnone; | |
| - | |
| + VALUE constructor; | |
| + rb_scan_args(argc, argv, "01", &constructor); | |
| + if(TYPE(constructor) == T_HASH){ | |
| + return rb_hash_update(hash,constructor); | |
| + } | |
| rb_hash_modify(hash); | |
| if (rb_block_given_p()) { | |
| if (argc > 0) { | |
| @@ -333,7 +454,6 @@ rb_hash_initialize(int argc, VALUE *argv, VALUE hash) | |
| rb_scan_args(argc, argv, "01", &ifnone); | |
| RHASH(hash)->ifnone = ifnone; | |
| } | |
| - | |
| return hash; | |
| } | |
| @@ -366,8 +486,11 @@ rb_hash_s_create(int argc, VALUE *argv, VALUE klass) | |
| hash = hash_alloc(klass); | |
| if (RHASH(tmp)->ntbl) { | |
| RHASH(hash)->ntbl = st_copy(RHASH(tmp)->ntbl); | |
| + if (FL_TEST(RHASH(tmp), STR_HASH) || klass == rb_cStrHash){ | |
| + NEW_STR_HASH(hash,tmp); | |
| + }else | |
| + return hash; | |
| } | |
| - return hash; | |
| } | |
| tmp = rb_check_array_type(argv[0]); | |
| @@ -406,7 +529,8 @@ rb_hash_s_create(int argc, VALUE *argv, VALUE klass) | |
| static VALUE | |
| to_hash(VALUE hash) | |
| { | |
| - return rb_convert_type(hash, T_HASH, "Hash", "to_hash"); | |
| + char *type = FL_TEST(hash, STR_HASH) ? "StrHash" : "Hash"; | |
| + return rb_convert_type(hash, T_HASH, type, "to_hash"); | |
| } | |
| /* | |
| @@ -423,7 +547,8 @@ to_hash(VALUE hash) | |
| static VALUE | |
| rb_hash_s_try_convert(VALUE dummy, VALUE hash) | |
| { | |
| - return rb_check_convert_type(hash, T_HASH, "Hash", "to_hash"); | |
| + char *type = FL_TEST(hash, STR_HASH) ? "StrHash" : "Hash"; | |
| + return rb_check_convert_type(hash, T_HASH, type, "to_hash"); | |
| } | |
| static int | |
| @@ -431,7 +556,12 @@ rb_hash_rehash_i(VALUE key, VALUE value, VALUE arg) | |
| { | |
| st_table *tbl = (st_table *)arg; | |
| - if (key != Qundef) st_insert(tbl, key, value); | |
| + if (key != Qundef){ | |
| + if (tbl == &strhash){ | |
| + rb_strhash_convert(&value); | |
| + } | |
| + st_insert(tbl, key, value); | |
| + } | |
| return ST_CONTINUE; | |
| } | |
| @@ -974,7 +1104,7 @@ rb_hash_select(VALUE hash) | |
| VALUE result; | |
| RETURN_ENUMERATOR(hash, 0, 0); | |
| - result = rb_hash_new(); | |
| + result = (FL_TEST(hash, STR_HASH)) ? rb_strhash_new() : rb_hash_new(); | |
| rb_hash_foreach(hash, select_i, result); | |
| return result; | |
| } | |
| @@ -1034,6 +1164,7 @@ VALUE | |
| rb_hash_aset(VALUE hash, VALUE key, VALUE val) | |
| { | |
| rb_hash_modify(hash); | |
| + CONVERT_STR_HASH(hash,val); | |
| if (hash == key) { | |
| rb_raise(rb_eArgError, "recursive key for hash"); | |
| } | |
| @@ -1617,7 +1748,7 @@ rb_hash_invert_i(VALUE key, VALUE value, VALUE hash) | |
| static VALUE | |
| rb_hash_invert(VALUE hash) | |
| { | |
| - VALUE h = rb_hash_new(); | |
| + VALUE h = (FL_TEST(hash, STR_HASH)) ? rb_strhash_new() : rb_hash_new(); | |
| rb_hash_foreach(hash, rb_hash_invert_i, h); | |
| return h; | |
| @@ -1627,6 +1758,7 @@ static int | |
| rb_hash_update_i(VALUE key, VALUE value, VALUE hash) | |
| { | |
| if (key == Qundef) return ST_CONTINUE; | |
| + CONVERT_STR_HASH(hash,value); | |
| st_insert(RHASH(hash)->ntbl, key, value); | |
| return ST_CONTINUE; | |
| } | |
| @@ -1637,6 +1769,7 @@ rb_hash_update_block_i(VALUE key, VALUE value, VALUE hash) | |
| if (key == Qundef) return ST_CONTINUE; | |
| if (rb_hash_has_key(hash, key)) { | |
| value = rb_yield_values(3, key, rb_hash_aref(hash, key), value); | |
| + CONVERT_STR_HASH(hash,value); | |
| } | |
| st_insert(RHASH(hash)->ntbl, key, value); | |
| return ST_CONTINUE; | |
| @@ -1852,6 +1985,14 @@ rb_hash_compare_by_id_p(VALUE hash) | |
| return Qfalse; | |
| } | |
| +static VALUE | |
| +rb_hash_strhash(VALUE hash) | |
| +{ | |
| + VALUE args[1]; | |
| + args[0] = hash; | |
| + return rb_hash_s_create(1, (VALUE *)args, rb_cStrHash); | |
| +} | |
| + | |
| static int path_tainted = -1; | |
| static char **origenviron; | |
| @@ -2649,6 +2790,7 @@ Init_Hash(void) | |
| id_default = rb_intern("default"); | |
| rb_cHash = rb_define_class("Hash", rb_cObject); | |
| + rb_cStrHash = rb_define_class("StrHash", rb_cHash); | |
| rb_include_module(rb_cHash, rb_mEnumerable); | |
| @@ -2715,6 +2857,7 @@ Init_Hash(void) | |
| rb_define_method(rb_cHash,"compare_by_identity", rb_hash_compare_by_id, 0); | |
| rb_define_method(rb_cHash,"compare_by_identity?", rb_hash_compare_by_id_p, 0); | |
| + rb_define_method(rb_cHash, "strhash", rb_hash_strhash, 0); | |
| origenviron = environ; | |
| envtbl = rb_obj_alloc(rb_cObject); | |
| diff --git a/include/ruby/intern.h b/include/ruby/intern.h | |
| index cece642..dabe2cd 100644 | |
| --- a/include/ruby/intern.h | |
| +++ b/include/ruby/intern.h | |
| @@ -659,6 +659,7 @@ void rb_str_associate(VALUE, VALUE); | |
| VALUE rb_str_associated(VALUE); | |
| void rb_str_setter(VALUE, ID, VALUE*); | |
| VALUE rb_str_intern(VALUE); | |
| +st_index_t rb_sym_hash(VALUE); | |
| VALUE rb_sym_to_s(VALUE); | |
| VALUE rb_str_length(VALUE); | |
| long rb_str_offset(VALUE, long); | |
| diff --git a/string.c b/string.c | |
| index 4f607a4..1bb06cf 100644 | |
| --- a/string.c | |
| +++ b/string.c | |
| @@ -7012,6 +7012,17 @@ sym_equal(VALUE sym1, VALUE sym2) | |
| return Qfalse; | |
| } | |
| +st_index_t | |
| +rb_sym_hash(VALUE sym){ | |
| + ID id = SYM2ID(sym); | |
| + return rb_str_hash(rb_id2str(id)); | |
| +} | |
| + | |
| +static VALUE | |
| +rb_sym_hash_m(VALUE sym){ | |
| + st_index_t hval = rb_sym_hash(sym); | |
| + return INT2FIX(hval); | |
| +} | |
| static int | |
| sym_printable(const char *s, const char *send, rb_encoding *enc) | |
| @@ -7495,6 +7506,7 @@ Init_String(void) | |
| rb_define_method(rb_cSymbol, "===", sym_equal, 1); | |
| rb_define_method(rb_cSymbol, "inspect", sym_inspect, 0); | |
| rb_define_method(rb_cSymbol, "to_s", rb_sym_to_s, 0); | |
| + rb_define_method(rb_cSymbol, "hash", rb_sym_hash_m, 0); | |
| rb_define_method(rb_cSymbol, "id2name", rb_sym_to_s, 0); | |
| rb_define_method(rb_cSymbol, "intern", sym_to_sym, 0); | |
| rb_define_method(rb_cSymbol, "to_sym", sym_to_sym, 0); | |
| diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb | |
| index c860b25..ce43220 100644 | |
| --- a/test/ruby/test_hash.rb | |
| +++ b/test/ruby/test_hash.rb | |
| @@ -1,5 +1,5 @@ | |
| require 'test/unit' | |
| -require 'continuation' | |
| +#require 'continuation' | |
| class TestHash < Test::Unit::TestCase | |
| @@ -872,3 +872,254 @@ class TestHash < Test::Unit::TestCase | |
| assert_equal({x=>1}.hash, {x=>1}.hash) | |
| end | |
| end | |
| + | |
| +class HashToStrHash < Test::Unit::TestCase | |
| + def test_strhash | |
| + hash = { 'a' => 1, 'b' => 2 } | |
| + assert_instance_of StrHash, hash.strhash | |
| + assert_equal %w(a b), hash.keys | |
| + assert_equal [1,2], hash.values | |
| + end | |
| +end | |
| + | |
| +class TestStrHash < Test::Unit::TestCase | |
| + def setup | |
| + @strings = { 'a' => 1, 'b' => 2 }.strhash | |
| + @symbols = { :a => 1, :b => 2 }.strhash | |
| + @mixed = { :a => 1, 'b' => 2 }.strhash | |
| + @fixnums = { 0 => 1, 1 => 2 }.strhash | |
| + end | |
| + | |
| + def test_inherits_hash | |
| + assert_equal Hash, StrHash.superclass | |
| + end | |
| + | |
| + def test_strhash | |
| + #assert_equal @strings.object_id, @strings.strhash.object_id | |
| + assert_instance_of StrHash, { 'a' => 1, 'b' => 2 }.strhash | |
| + end | |
| + | |
| + def test_initialize | |
| + strhash = StrHash.new({ 'a' => 1, 'b' => 2 }) | |
| + assert_equal 1, strhash[:a] | |
| + strhash = StrHash.new | |
| + strhash[:a] = 'a' | |
| + assert_equal 'a', strhash[:a] | |
| + end | |
| + | |
| + def test_set | |
| + array = [{ 'a' => 1, 'b' => 2 }, [:a,:b,:c]] | |
| + @strings[:array] = array | |
| + assert_instance_of StrHash, @strings[:array].shift | |
| + assert_instance_of Array, @strings[:array] = array | |
| + assert_instance_of StrHash, @strings[:hash] = { 'a' => 1, 'b' => 2 } | |
| + end | |
| + | |
| + def test_dup | |
| + assert_equal @strings, @strings.dup | |
| + assert_equal @mixed, @mixed.dup | |
| + assert_not_equal @mixed.object_id, @mixed.dup.object_id | |
| + end | |
| + | |
| + def test_keys | |
| + assert_equal ["a", "b"], @strings.keys | |
| + assert_equal [:a, :b], @symbols.keys | |
| + assert_equal [:a, "b"], @mixed.keys | |
| + assert_equal [0, 1], @fixnums.keys | |
| + end | |
| + | |
| + def test_values | |
| + assert_equal [1, 2], @strings.values | |
| + assert_equal [1, 2], @symbols.values | |
| + assert_equal [1, 2], @mixed.values | |
| + assert_equal [1, 2], @fixnums.values | |
| + end | |
| + | |
| + def test_fetch | |
| + assert_equal 1, @strings.fetch('a') | |
| + assert_equal 1, @strings.fetch(:a.to_s) | |
| + assert_equal 1, @strings.fetch(:a) | |
| + end | |
| + | |
| + def test_key? | |
| + assert @strings.key?(:a) | |
| + assert @strings.include?('a') | |
| + assert @mixed.has_key?('b') | |
| + end | |
| + | |
| + def test_delete | |
| + @strings.delete('a') | |
| + assert !@strings.key?(:a) | |
| + end | |
| + | |
| + def test_assorted | |
| + hashes = { :@strings => @strings, :@symbols => @symbols, :@mixed => @mixed } | |
| + method_map = { :'[]' => 1, :fetch => 1, :values_at => [1], | |
| + :has_key? => true, :include? => true, :key? => true, | |
| + :member? => true } | |
| + | |
| + hashes.each do |name, hash| | |
| + method_map.sort_by { |m| m.to_s }.each do |meth, expected| | |
| + assert_equal(expected, hash.__send__(meth, 'a'), | |
| + "Calling #{name}.#{meth} 'a'") | |
| + assert_equal(expected, hash.__send__(meth, :a), | |
| + "Calling #{name}.#{meth} :a") | |
| + end | |
| + end | |
| + | |
| + assert_equal [1, 2], @strings.values_at('a', 'b') | |
| + assert_equal [1, 2], @strings.values_at(:a, :b) | |
| + assert_equal [1, 2], @symbols.values_at('a', 'b') | |
| + assert_equal [1, 2], @symbols.values_at(:a, :b) | |
| + assert_equal [1, 2], @mixed.values_at('a', 'b') | |
| + assert_equal [1, 2], @mixed.values_at(:a, :b) | |
| + end | |
| + | |
| + def test_reading | |
| + hash = StrHash.new | |
| + hash["a"] = 1 | |
| + hash["b"] = true | |
| + hash["c"] = false | |
| + hash["d"] = nil | |
| + | |
| + assert_equal 1, hash[:a] | |
| + assert_equal true, hash[:b] | |
| + assert_equal false, hash[:c] | |
| + assert_equal nil, hash[:d] | |
| + assert_equal nil, hash[:e] | |
| + end | |
| + | |
| + def test_reading_with_nonnil_default | |
| + hash = StrHash.new(1) | |
| + hash["a"] = 1 | |
| + hash["b"] = true | |
| + hash["c"] = false | |
| + hash["d"] = nil | |
| + | |
| + assert_equal 1, hash[:a] | |
| + assert_equal true, hash[:b] | |
| + assert_equal false, hash[:c] | |
| + assert_equal nil, hash[:d] | |
| + assert_equal 1, hash[:e] | |
| + end | |
| + | |
| + def test_writing | |
| + hash = StrHash.new | |
| + hash[:a] = 1 | |
| + hash['b'] = 2 | |
| + hash[3] = 3 | |
| + | |
| + assert_equal hash['a'], 1 | |
| + assert_equal hash['b'], 2 | |
| + assert_equal hash[:a], 1 | |
| + assert_equal hash[:b], 2 | |
| + assert_equal hash[3], 3 | |
| + end | |
| + | |
| + def test_update | |
| + hash = StrHash.new | |
| + hash[:a] = 'a' | |
| + hash['b'] = 'b' | |
| + | |
| + updated_with_strings = hash.update(@strings) | |
| + updated_with_symbols = hash.update(@symbols) | |
| + updated_with_mixed = hash.update(@mixed) | |
| + | |
| + assert_equal updated_with_strings[:a], 1 | |
| + assert_equal updated_with_strings['a'], 1 | |
| + assert_equal updated_with_strings['b'], 2 | |
| + | |
| + assert_equal updated_with_symbols[:a], 1 | |
| + assert_equal updated_with_symbols['b'], 2 | |
| + assert_equal updated_with_symbols[:b], 2 | |
| + | |
| + assert_equal updated_with_mixed[:a], 1 | |
| + assert_equal updated_with_mixed['b'], 2 | |
| + | |
| + assert [updated_with_strings, updated_with_symbols, updated_with_mixed].all? { |h| h.keys.size == 2 } | |
| + end | |
| + | |
| + def test_merging | |
| + hash = StrHash.new | |
| + hash[:a] = 'failure' | |
| + hash['b'] = 'failure' | |
| + | |
| + other = { 'a' => 1, :b => 2 } | |
| + | |
| + merged = hash.merge(other) | |
| + | |
| + assert_equal StrHash, merged.class | |
| + assert_equal 1, merged[:a] | |
| + assert_equal 2, merged['b'] | |
| + | |
| + hash.update(other) | |
| + | |
| + assert_equal 1, hash[:a] | |
| + assert_equal 2, hash['b'] | |
| + end | |
| + | |
| + def test_deleting | |
| + get_hash = proc{ StrHash[ :a => 'foo' ] } | |
| + hash = get_hash.call | |
| + assert_equal hash.delete(:a), 'foo' | |
| + assert_equal hash.delete(:a), nil | |
| + hash = get_hash.call | |
| + assert_equal hash.delete('a'), 'foo' | |
| + assert_equal hash.delete('a'), nil | |
| + end | |
| + | |
| + def test_to_hash | |
| + assert_instance_of Hash, @strings.to_hash | |
| + assert_equal %w(a b), @strings.to_hash.keys | |
| + # Should convert to a Hash with String keys. | |
| + assert_equal @strings, @mixed.strhash.to_hash | |
| + | |
| + # Should preserve the default value. | |
| + mixed_with_default = @mixed.dup | |
| + mixed_with_default.default = '1234' | |
| + roundtrip = mixed_with_default.strhash.to_hash | |
| + assert_equal @strings, roundtrip | |
| + assert_equal '1234', roundtrip.default | |
| + end | |
| + | |
| + def test_hash_with_array_of_hashes | |
| + hash = { "urls" => { "url" => [ { "address" => "1" }, { "address" => "2" } ] }} | |
| + ['user', :user].each {|user| [:id, 'id'].each {|id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5"}} | |
| + end | |
| + | |
| + def test_assorted_keys_not_stringified | |
| + original = {Object.new => 2, 1 => 2, [] => true} | |
| + indiff = original.strhash | |
| + assert(!indiff.keys.any? {|k| k.kind_of? String}, "A key was converted to a string!") | |
| + end | |
| + | |
| + def test_should_use_default_value_for_unknown_key | |
| + hash_wia = StrHash.new(3) | |
| + assert_equal 3, hash_wia[:new_key] | |
| + end | |
| + | |
| + def test_should_use_default_value_if_no_key_is_supplied | |
| + hash_wia = StrHash.new(3) | |
| + assert_equal 3, hash_wia.default | |
| + end | |
| + | |
| + def test_should_nil_if_no_default_value_is_supplied | |
| + hash_wia = StrHash.new | |
| + assert_nil hash_wia.default | |
| + end | |
| + | |
| + def test_should_copy_the_default_value_when_converting_to_hash_with_indifferent_access | |
| + hash = Hash.new(3) | |
| + hash_wia = hash.strhash | |
| + assert_equal 3, hash_wia.default | |
| + end | |
| +end | |
| \ No newline at end of file | |
| diff --git a/test/ruby/test_symbol.rb b/test/ruby/test_symbol.rb | |
| index f402da3..c181b19 100644 | |
| --- a/test/ruby/test_symbol.rb | |
| +++ b/test/ruby/test_symbol.rb | |
| @@ -1,6 +1,13 @@ | |
| require 'test/unit' | |
| class TestSymbol < Test::Unit::TestCase | |
| + def test_hash | |
| + assert_instance_of Fixnum, :symbol.hash | |
| + assert_equal 'symbol'.hash, :symbol.hash | |
| + assert_equal :"!".hash, :"!".hash | |
| + assert_not_equal :"$1".hash, :"@@1".hash | |
| + end | |
| + | |
| # [ruby-core:3573] | |
| def assert_eval_inspected(sym) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment