hwia 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,93 @@
1
+ Attempt at a zippier HashWithIndifferentAccess for Ruby MRI
2
+ (c) 2009 Lourens Naudé (methodmissing), James Tucker (raggi)
3
+
4
+ http://github.com/methodmissing/hwia
5
+
6
+ This library works with Ruby MRI > 1.8.6 && 1.9.2 and is a more efficient implementation of
7
+ ActiveSupport::HashWithIndifferentAccess, which allows interchangeable use of string and symbol
8
+ hash keys.
9
+
10
+ The numbers ?
11
+
12
+ methodmissing:hwia lourens$ rake bench
13
+ (in /Users/lourens/projects/hwia)
14
+ /opt/local/bin/ruby extconf.rb
15
+ checking for rb_thread_blocking_region()... no
16
+ checking for rb_trap_immediate in ruby.h,rubysig.h... yes
17
+ creating Makefile
18
+ make
19
+ gcc -I. -I. -I/opt/local/lib/ruby/1.8/i686-darwin9.5.1 -I. -DHAVE_RB_TRAP_IMMEDIATE -DRUBY18 -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -fno-common -O2 -pipe -fno-common -c hwia.c
20
+ cc -dynamic -bundle -undefined suppress -flat_namespace -o hwia.bundle hwia.o -L. -L/opt/local/lib -L. -ldl -lobjc
21
+ /opt/local/bin/ruby bench/bench.rb
22
+ Rehearsal ------------------------------------------------------------------------------
23
+ StrHash#[:sym] 0.000000 0.000000 0.000000 ( 0.002999)
24
+ HashWithIndifferentAccess#[:sym] 0.020000 0.000000 0.020000 ( 0.021942)
25
+ StrHash#['str'] 0.010000 0.000000 0.010000 ( 0.004590)
26
+ HashWithIndifferentAccess#['str] 0.000000 0.000000 0.000000 ( 0.003842)
27
+ StrHash#key?(:sym) 0.010000 0.000000 0.010000 ( 0.002902)
28
+ HashWithIndifferentAccess#key?(:sym) 0.010000 0.000000 0.010000 ( 0.013906)
29
+ StrHash#key?('str') 0.000000 0.000000 0.000000 ( 0.004245)
30
+ HashWithIndifferentAccess#key?('str') 0.010000 0.000000 0.010000 ( 0.010769)
31
+ StrHash#fetch(:sym) 0.010000 0.000000 0.010000 ( 0.003285)
32
+ HashWithIndifferentAccess#fetch(:sym) 0.020000 0.000000 0.020000 ( 0.019375)
33
+ StrHash#fetch('str') 0.000000 0.000000 0.000000 ( 0.003811)
34
+ HashWithIndifferentAccess#fetch('str') 0.010000 0.000000 0.010000 ( 0.014550)
35
+ StrHash#values_at(:sym) 0.010000 0.000000 0.010000 ( 0.006830)
36
+ HashWithIndifferentAccess#values_at(:sym) 0.020000 0.000000 0.020000 ( 0.020391)
37
+ StrHash#values_at('str') 0.010000 0.000000 0.010000 ( 0.007286)
38
+ HashWithIndifferentAccess#values_at('str') 0.020000 0.000000 0.020000 ( 0.019495)
39
+ StrHash#['str']= 0.000000 0.000000 0.000000 ( 0.004249)
40
+ HashWithIndifferentAccess#['str]= 0.020000 0.000000 0.020000 ( 0.017406)
41
+ StrHash#[:sym]= 0.000000 0.000000 0.000000 ( 0.003898)
42
+ HashWithIndifferentAccess#[:sym]= 0.020000 0.000000 0.020000 ( 0.020366)
43
+ StrHash#update 0.010000 0.000000 0.010000 ( 0.005549)
44
+ HashWithIndifferentAccess#update 0.050000 0.000000 0.050000 ( 0.058935)
45
+ StrHash#dup 0.030000 0.000000 0.030000 ( 0.029752)
46
+ HashWithIndifferentAccess#dup 0.120000 0.000000 0.120000 ( 0.128600)
47
+ StrHash#merge 0.030000 0.000000 0.030000 ( 0.027572)
48
+ HashWithIndifferentAccess#merge 0.180000 0.000000 0.180000 ( 0.179814)
49
+ StrHash#to_hash 0.030000 0.000000 0.030000 ( 0.034352)
50
+ HashWithIndifferentAccess#to_hash 0.040000 0.000000 0.040000 ( 0.038520)
51
+ --------------------------------------------------------------------- total: 0.690000sec
52
+
53
+ user system total real
54
+ StrHash#[:sym] 0.010000 0.000000 0.010000 ( 0.003005)
55
+ HashWithIndifferentAccess#[:sym] 0.020000 0.000000 0.020000 ( 0.020551)
56
+ StrHash#['str'] 0.000000 0.000000 0.000000 ( 0.003312)
57
+ HashWithIndifferentAccess#['str] 0.000000 0.000000 0.000000 ( 0.003213)
58
+ StrHash#key?(:sym) 0.000000 0.000000 0.000000 ( 0.002851)
59
+ HashWithIndifferentAccess#key?(:sym) 0.010000 0.000000 0.010000 ( 0.012139)
60
+ StrHash#key?('str') 0.010000 0.000000 0.010000 ( 0.003496)
61
+ HashWithIndifferentAccess#key?('str') 0.010000 0.000000 0.010000 ( 0.009912)
62
+ StrHash#fetch(:sym) 0.000000 0.000000 0.000000 ( 0.003172)
63
+ HashWithIndifferentAccess#fetch(:sym) 0.020000 0.000000 0.020000 ( 0.017662)
64
+ StrHash#fetch('str') 0.000000 0.000000 0.000000 ( 0.003850)
65
+ HashWithIndifferentAccess#fetch('str') 0.020000 0.000000 0.020000 ( 0.014771)
66
+ StrHash#values_at(:sym) 0.000000 0.000000 0.000000 ( 0.004340)
67
+ HashWithIndifferentAccess#values_at(:sym) 0.030000 0.000000 0.030000 ( 0.020821)
68
+ StrHash#values_at('str') 0.000000 0.000000 0.000000 ( 0.004842)
69
+ HashWithIndifferentAccess#values_at('str') 0.020000 0.000000 0.020000 ( 0.017479)
70
+ StrHash#['str']= 0.010000 0.000000 0.010000 ( 0.004225)
71
+ HashWithIndifferentAccess#['str]= 0.010000 0.000000 0.010000 ( 0.018206)
72
+ StrHash#[:sym]= 0.010000 0.000000 0.010000 ( 0.003825)
73
+ HashWithIndifferentAccess#[:sym]= 0.020000 0.000000 0.020000 ( 0.019323)
74
+ StrHash#update 0.000000 0.000000 0.000000 ( 0.005478)
75
+ HashWithIndifferentAccess#update 0.050000 0.000000 0.050000 ( 0.048082)
76
+ StrHash#dup 0.030000 0.000000 0.030000 ( 0.023855)
77
+ HashWithIndifferentAccess#dup 0.110000 0.000000 0.110000 ( 0.120992)
78
+ StrHash#merge 0.030000 0.000000 0.030000 ( 0.027412)
79
+ HashWithIndifferentAccess#merge 0.180000 0.000000 0.180000 ( 0.173209)
80
+ StrHash#to_hash 0.030000 0.000000 0.030000 ( 0.034561)
81
+ HashWithIndifferentAccess#to_hash 0.030000 0.000000 0.030000 ( 0.025392)
82
+
83
+ Installation
84
+
85
+ 1. sudo gem install methodmissing-hwia
86
+ 2. 'require "hwia_rails"' from within an initializer
87
+
88
+ There's no step three, however, if the gem hasn't built due to github queue delays today (22/08/2009), clone, 'rake gem' and install.
89
+
90
+ It's unlikely you'd see these improvements for most apps as at the framework level, especially for Rails 3.0,
91
+ this mostly negates query params access overheads.Any pointers and refactoring suggestions much appreciated.
92
+
93
+ Have fun, happy hacking!
@@ -0,0 +1,73 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/clean'
4
+
5
+ HWIA_ROOT = 'ext/hwia'
6
+
7
+ desc 'Default: test'
8
+ task :default => :test
9
+
10
+ desc 'Run hwia tests.'
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs = [HWIA_ROOT]
13
+ t.pattern = 'test/test_*.rb'
14
+ t.verbose = true
15
+ end
16
+ task :test => :build
17
+
18
+ namespace :build do
19
+ file "#{HWIA_ROOT}/hwia.c"
20
+ file "#{HWIA_ROOT}/extconf.rb"
21
+ file "#{HWIA_ROOT}/Makefile" => %W(#{HWIA_ROOT}/hwia.c #{HWIA_ROOT}/extconf.rb) do
22
+ Dir.chdir(HWIA_ROOT) do
23
+ ruby 'extconf.rb'
24
+ end
25
+ end
26
+
27
+ desc "generate makefile"
28
+ task :makefile => %W(#{HWIA_ROOT}/Makefile #{HWIA_ROOT}/hwia.c)
29
+
30
+ dlext = Config::CONFIG['DLEXT']
31
+ file "#{HWIA_ROOT}/hwia.#{dlext}" => %W(#{HWIA_ROOT}/Makefile #{HWIA_ROOT}/hwia.c) do
32
+ Dir.chdir(HWIA_ROOT) do
33
+ sh 'make' # TODO - is there a config for which make somewhere?
34
+ end
35
+ end
36
+
37
+ desc "compile hwia extension"
38
+ task :compile => "#{HWIA_ROOT}/hwia.#{dlext}"
39
+
40
+ task :clean do
41
+ Dir.chdir(HWIA_ROOT) do
42
+ sh 'make clean'
43
+ end if File.exists?("#{HWIA_ROOT}/Makefile")
44
+ end
45
+
46
+ CLEAN.include("#{HWIA_ROOT}/Makefile")
47
+ CLEAN.include("#{HWIA_ROOT}/hwia.#{dlext}")
48
+ end
49
+
50
+ task :clean => %w(build:clean)
51
+
52
+ desc "compile"
53
+ task :build => %w(build:compile)
54
+
55
+ task :install do |t|
56
+ Dir.chdir(HWIA_ROOT) do
57
+ sh 'sudo make install'
58
+ end
59
+ end
60
+
61
+ desc "clean build install"
62
+ task :setup => %w(clean build install)
63
+
64
+ desc "run benchmarks"
65
+ task :bench do
66
+ ruby "bench/bench.rb"
67
+ end
68
+ task :bench => :build
69
+
70
+ desc "build gem"
71
+ task :gem do
72
+ sh "gem build hwia.gemspec"
73
+ end
@@ -0,0 +1,137 @@
1
+ # This class has dubious semantics and we only have it so that
2
+ # people can write params[:key] instead of params['key']
3
+ # and they get the same value for both keys.
4
+
5
+ module ActiveSupport
6
+ class HashWithIndifferentAccess < Hash
7
+ def initialize(constructor = {})
8
+ if constructor.is_a?(Hash)
9
+ super()
10
+ update(constructor)
11
+ else
12
+ super(constructor)
13
+ end
14
+ end
15
+
16
+ def default(key = nil)
17
+ if key.is_a?(Symbol) && include?(key = key.to_s)
18
+ self[key]
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
25
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
26
+
27
+ # Assigns a new value to the hash:
28
+ #
29
+ # hash = HashWithIndifferentAccess.new
30
+ # hash[:key] = "value"
31
+ #
32
+ def []=(key, value)
33
+ regular_writer(convert_key(key), convert_value(value))
34
+ end
35
+
36
+ # Updates the instantized hash with values from the second:
37
+ #
38
+ # hash_1 = HashWithIndifferentAccess.new
39
+ # hash_1[:key] = "value"
40
+ #
41
+ # hash_2 = HashWithIndifferentAccess.new
42
+ # hash_2[:key] = "New Value!"
43
+ #
44
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
45
+ #
46
+ def update(other_hash)
47
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
48
+ self
49
+ end
50
+
51
+ alias_method :merge!, :update
52
+
53
+ # Checks the hash for a key matching the argument passed in:
54
+ #
55
+ # hash = HashWithIndifferentAccess.new
56
+ # hash["key"] = "value"
57
+ # hash.key? :key # => true
58
+ # hash.key? "key" # => true
59
+ #
60
+ def key?(key)
61
+ super(convert_key(key))
62
+ end
63
+
64
+ alias_method :include?, :key?
65
+ alias_method :has_key?, :key?
66
+ alias_method :member?, :key?
67
+
68
+ # Fetches the value for the specified key, same as doing hash[key]
69
+ def fetch(key, *extras)
70
+ super(convert_key(key), *extras)
71
+ end
72
+
73
+ # Returns an array of the values at the specified indices:
74
+ #
75
+ # hash = HashWithIndifferentAccess.new
76
+ # hash[:a] = "x"
77
+ # hash[:b] = "y"
78
+ # hash.values_at("a", "b") # => ["x", "y"]
79
+ #
80
+ def values_at(*indices)
81
+ indices.collect {|key| self[convert_key(key)]}
82
+ end
83
+
84
+ # Returns an exact copy of the hash.
85
+ def dup
86
+ HashWithIndifferentAccess.new(self)
87
+ end
88
+
89
+ # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
90
+ # Does not overwrite the existing hash.
91
+ def merge(hash)
92
+ self.dup.update(hash)
93
+ end
94
+
95
+ # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
96
+ # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
97
+ def reverse_merge(other_hash)
98
+ super other_hash.with_indifferent_access
99
+ end
100
+
101
+ def reverse_merge!(other_hash)
102
+ replace(reverse_merge( other_hash ))
103
+ end
104
+
105
+ # Removes a specified key from the hash.
106
+ def delete(key)
107
+ super(convert_key(key))
108
+ end
109
+
110
+ def stringify_keys!; self end
111
+ def symbolize_keys!; self end
112
+ def to_options!; self end
113
+
114
+ # Convert to a Hash with String keys.
115
+ def to_hash
116
+ Hash.new(default).merge!(self)
117
+ end
118
+
119
+ protected
120
+ def convert_key(key)
121
+ key.kind_of?(Symbol) ? key.to_s : key
122
+ end
123
+
124
+ def convert_value(value)
125
+ case value
126
+ when Hash
127
+ value.with_indifferent_access
128
+ when Array
129
+ value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
130
+ else
131
+ value
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
@@ -0,0 +1,52 @@
1
+ require "benchmark"
2
+ $:.unshift "." #ruby 1.9.2
3
+ require File.dirname(__FILE__) + "/../ext/hwia/hwia"
4
+ require File.dirname(__FILE__) + "/as_hwia"
5
+
6
+ STR_HASH = { :a => 1, 'b' => 2, 1 => 1, [1] => 1 }.strhash
7
+ HWIA_HASH = HashWithIndifferentAccess.new({ :a => 1, 'b' => 2, 1 => 1, [1] => 1 })
8
+ HASH = { :d => :d, 'e' => :e }
9
+
10
+ TESTS = 100_000
11
+ Benchmark.bmbm do |results|
12
+ results.report("StrHash#[:sym]") { TESTS.times { STR_HASH[:sym] } }
13
+ results.report("HashWithIndifferentAccess#[:sym]") { TESTS.times { HWIA_HASH[:a] } }
14
+ results.report("StrHash#['str']") { TESTS.times { STR_HASH['b'] } }
15
+ results.report("HashWithIndifferentAccess#['str]") { TESTS.times { HWIA_HASH['b'] } }
16
+ results.report("StrHash#[1]") { TESTS.times { STR_HASH[1] } }
17
+ results.report("HashWithIndifferentAccess#[1]") { TESTS.times { HWIA_HASH[1] } }
18
+ results.report("StrHash#[[1]]") { TESTS.times { STR_HASH[[1]] } }
19
+ results.report("HashWithIndifferentAccess#[[1]]") { TESTS.times { HWIA_HASH[[1]] } }
20
+ results.report("StrHash#key?(:sym)") { TESTS.times { STR_HASH.key?(:a) } }
21
+ results.report("HashWithIndifferentAccess#key?(:sym)") { TESTS.times { HWIA_HASH.key?(:a) } }
22
+ results.report("StrHash#key?('str')") { TESTS.times { STR_HASH.key?('a') } }
23
+ results.report("HashWithIndifferentAccess#key?('str')") { TESTS.times { HWIA_HASH.key?('a') } }
24
+ results.report("StrHash#fetch(:sym)") { TESTS.times { STR_HASH.fetch(:a) } }
25
+ results.report("HashWithIndifferentAccess#fetch(:sym)") { TESTS.times { HWIA_HASH.fetch(:a) } }
26
+ results.report("StrHash#fetch('str')") { TESTS.times { STR_HASH.fetch('a') } }
27
+ results.report("HashWithIndifferentAccess#fetch('str')") { TESTS.times { HWIA_HASH.fetch('a') } }
28
+ results.report("StrHash#values_at(:sym)") { TESTS.times { STR_HASH.values_at(:a) } }
29
+ results.report("HashWithIndifferentAccess#values_at(:sym)") { TESTS.times { HWIA_HASH.values_at(:a) } }
30
+ results.report("StrHash#values_at('str')") { TESTS.times { STR_HASH.values_at('a') } }
31
+ results.report("HashWithIndifferentAccess#values_at('str')") { TESTS.times { HWIA_HASH.values_at('a') } }
32
+ results.report("StrHash#['str']=") { TESTS.times { STR_HASH['c'] = :c } }
33
+ results.report("HashWithIndifferentAccess#['str]=") { TESTS.times { HWIA_HASH['c'] = :c } }
34
+ results.report("StrHash#[:sym]=") { TESTS.times { STR_HASH[:c] = :c } }
35
+ results.report("HashWithIndifferentAccess#[:sym]=") { TESTS.times { HWIA_HASH[:c] = :c } }
36
+ results.report("StrHash#[2]=") { TESTS.times { STR_HASH[2] = 2 } }
37
+ results.report("HashWithIndifferentAccess#[2]=") { TESTS.times { HWIA_HASH[2] = 2 } }
38
+ results.report("StrHash#[[2]]=") { TESTS.times { STR_HASH[[2]] = 2 } }
39
+ results.report("HashWithIndifferentAccess#[[2]]=") { TESTS.times { HWIA_HASH[[2]] = 2 } }
40
+ results.report("StrHash#update") { TESTS.times { STR_HASH.update(HASH) } }
41
+ results.report("HashWithIndifferentAccess#update") { TESTS.times { HWIA_HASH.update(HASH) } }
42
+ results.report("StrHash#dup") { TESTS.times { STR_HASH.dup } }
43
+ results.report("HashWithIndifferentAccess#dup") { TESTS.times { HWIA_HASH.dup } }
44
+ results.report("StrHash#merge") { TESTS.times { STR_HASH.merge(HASH) } }
45
+ results.report("HashWithIndifferentAccess#merge") { TESTS.times { HWIA_HASH.merge(HASH) } }
46
+ results.report("StrHash#to_hash") { TESTS.times { STR_HASH.to_hash } }
47
+ results.report("HashWithIndifferentAccess#to_hash") { TESTS.times { HWIA_HASH.to_hash } }
48
+ results.report("StrHash#keys") { TESTS.times { STR_HASH.keys } }
49
+ results.report("HashWithIndifferentAccess#keys") { TESTS.times { HWIA_HASH.keys } }
50
+ results.report("StrHash#values") { TESTS.times { STR_HASH.values } }
51
+ results.report("HashWithIndifferentAccess#values") { TESTS.times { HWIA_HASH.values } }
52
+ end
@@ -0,0 +1,12 @@
1
+ require 'mkmf'
2
+
3
+ def add_define(name)
4
+ $defs.push("-D#{name}")
5
+ end
6
+
7
+ dir_config('hwia')
8
+
9
+ add_define 'RUBY19' if have_func('rb_thread_blocking_region') and have_macro('RUBY_UBF_IO', 'ruby.h')
10
+ add_define 'RUBY18' if have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
11
+
12
+ create_makefile('hwia')
@@ -0,0 +1,497 @@
1
+ #include "ruby.h"
2
+ #ifdef RUBY19
3
+ #include "ruby/st.h"
4
+ #else
5
+ #include "st.h"
6
+ #endif
7
+
8
+ VALUE rb_cStrHash;
9
+
10
+ static ID id_strhash, id_hash;
11
+ static VALUE hash_format;
12
+
13
+ #ifndef RSTRING_PTR
14
+ #define RSTRING_PTR(obj) RSTRING(obj)->ptr
15
+ #endif
16
+
17
+ #ifndef RSTRING_LEN
18
+ #define RSTRING_LEN(obj) RSTRING(obj)->len
19
+ #endif
20
+
21
+ #ifndef RARRAY_PTR
22
+ #define RARRAY_PTR(obj) RARRAY(obj)->heap.ptr
23
+ #endif
24
+
25
+ #ifndef RARRAY_LEN
26
+ #define RARRAY_LEN(obj) RARRAY(obj)->heap.len
27
+ #endif
28
+
29
+ #ifdef RUBY19
30
+ #define HASH_TBL(obj) RHASH(obj)->ntbl
31
+ #else
32
+ #define HASH_TBL(obj) RHASH(obj)->tbl
33
+ #endif
34
+
35
+ static int
36
+ strhash(register const char *string)
37
+ {
38
+ register int c;
39
+ register int val = 0;
40
+ while ((c = *string++) != '\0') {
41
+ val = val*997 + c;
42
+ }
43
+ return val + (val>>5);
44
+ }
45
+
46
+ int
47
+ rb_sym_strhash(VALUE *sym)
48
+ {
49
+ ID id = SYM2ID(*sym);
50
+ return strhash((char*)rb_id2name(id));
51
+ }
52
+
53
+ static VALUE
54
+ rb_sym_strhash_m(VALUE sym)
55
+ {
56
+ return INT2FIX(rb_sym_strhash(&sym));
57
+ }
58
+
59
+ int
60
+ rb_str_strhash(VALUE *str)
61
+ {
62
+ return strhash((char*)RSTRING_PTR(*str));
63
+ }
64
+
65
+ static VALUE
66
+ rb_str_strhash_m(VALUE str)
67
+ {
68
+ return INT2FIX(rb_str_strhash(&str));
69
+ }
70
+
71
+ int
72
+ rb_strhash_cmp(VALUE *s1,VALUE *s2)
73
+ {
74
+ int s1_hash = SYMBOL_P(*s1) ? rb_sym_strhash(s1) : rb_str_strhash(s1);
75
+ int s2_hash = SYMBOL_P(*s2) ? rb_sym_strhash(s2) : rb_str_strhash(s2);
76
+ if (s1_hash == s2_hash) return 0;
77
+ if (s1_hash > s2_hash) return 1;
78
+ return -1;
79
+ }
80
+
81
+ /* hash.c */
82
+ static VALUE
83
+ rb_hash_has_key(hash, key)
84
+ VALUE hash;
85
+ VALUE key;
86
+ {
87
+ #ifdef RUBY19
88
+ if (!HASH_TBL(hash))
89
+ return Qfalse;
90
+ #endif
91
+ if (st_lookup(HASH_TBL(hash), key, 0)) {
92
+ return Qtrue;
93
+ }
94
+ return Qfalse;
95
+ }
96
+
97
+ /* hash.c */
98
+ #ifdef RUBY18
99
+ static VALUE
100
+ eql(VALUE *args)
101
+ {
102
+ return (VALUE)rb_eql(args[0], args[1]);
103
+ }
104
+ #endif
105
+
106
+ /* hash.c */
107
+ static int
108
+ rb_strhash_hash_cmp(VALUE a, VALUE b)
109
+ {
110
+ #ifdef RUBY18
111
+ VALUE args[2];
112
+ #endif
113
+ if (a == b) return 0;
114
+ if (FIXNUM_P(a) && FIXNUM_P(b)) return a != b;
115
+ if (a == Qundef || b == Qundef) return -1;
116
+ if (SYMBOL_P(a) && SYMBOL_P(b)) return a != b;
117
+ 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))) {
118
+ return rb_strhash_cmp(&a, &b);
119
+ }
120
+ if (TYPE(a) == T_STRING && RBASIC(a)->klass == rb_cString &&
121
+ TYPE(b) == T_STRING && RBASIC(b)->klass == rb_cString) {
122
+ return rb_str_cmp(a, b);
123
+ }
124
+ #ifdef RUBY18
125
+ args[0] = a;
126
+ args[1] = b;
127
+ return !rb_with_disable_interrupt(eql, (VALUE)args);
128
+ #else
129
+ return !rb_eql(a, b);
130
+ #endif
131
+ }
132
+
133
+ /* hash.c */
134
+ static int
135
+ rb_strhash_hash(VALUE a)
136
+ {
137
+ VALUE hval;
138
+ int hnum;
139
+
140
+ switch (TYPE(a)) {
141
+ case T_FIXNUM:
142
+ #ifdef RUBY18
143
+ return (int)a;
144
+ #else
145
+ hnum = (int)a;
146
+ break;
147
+ #endif
148
+ case T_SYMBOL:
149
+ hnum = rb_sym_strhash(&a);
150
+ break;
151
+ case T_STRING:
152
+ hnum = rb_str_strhash(&a);
153
+ break;
154
+
155
+ default:
156
+ hval = rb_hash(a);
157
+ #ifdef RUBY18
158
+ if (!FIXNUM_P(hval)) {
159
+ hval = rb_funcall(hval, '%', 1, hash_format);
160
+ }
161
+ #endif
162
+ hnum = (int)hval;
163
+ }
164
+ #ifdef RUBY18
165
+ return hnum;
166
+ #else
167
+ hnum <<= 1;
168
+ return RSHIFT(hnum, 1);
169
+ #endif
170
+ }
171
+
172
+ static struct st_hash_type objstrhash = {
173
+ rb_strhash_hash_cmp,
174
+ rb_strhash_hash,
175
+ };
176
+
177
+ static void
178
+ rb_hash_modify_check(VALUE *hash){
179
+ if (OBJ_FROZEN(*hash)) rb_error_frozen("hash");
180
+ if (!OBJ_TAINTED(*hash) && rb_safe_level() >= 4)
181
+ rb_raise(rb_eSecurityError, "Insecure: can't modify hash");
182
+ }
183
+
184
+ /* hash.c */
185
+ static void
186
+ rb_hash_modify(VALUE hash)
187
+ {
188
+ #ifdef RUBY18
189
+ if (!HASH_TBL(hash)) rb_raise(rb_eTypeError, "uninitialized Hash");
190
+ #endif
191
+ rb_hash_modify_check(&hash);
192
+ #ifdef RUBY19
193
+ if (!HASH_TBL(hash)) HASH_TBL(hash) = st_init_table(&objstrhash);
194
+ #endif
195
+ }
196
+
197
+ static VALUE strhash_alloc0 _((VALUE*));
198
+ static VALUE strhash_alloc _((VALUE));
199
+ /* hash.c */
200
+ static VALUE
201
+ strhash_alloc0(VALUE *klass)
202
+ {
203
+ NEWOBJ(hash, struct RHash);
204
+ OBJSETUP(hash, *klass, T_HASH);
205
+
206
+ hash->ifnone = Qnil;
207
+
208
+ return (VALUE)hash;
209
+ }
210
+
211
+ static VALUE
212
+ strhash_alloc(VALUE klass)
213
+ {
214
+ VALUE hash = strhash_alloc0(&klass);
215
+
216
+ HASH_TBL(hash) = st_init_table(&objstrhash);
217
+
218
+ return hash;
219
+ }
220
+
221
+ static VALUE rb_hash_strhash(VALUE hash);
222
+ static void rb_strhash_convert(VALUE *value);
223
+
224
+ /* hash.c */
225
+ static int
226
+ rb_hash_rehash_i(VALUE key, VALUE value, st_table *tbl)
227
+ {
228
+ if (key != Qundef){
229
+ rb_strhash_convert(&value);
230
+ st_insert(tbl, key, value);
231
+ }
232
+ return ST_CONTINUE;
233
+ }
234
+
235
+ /* hash.c */
236
+ static VALUE
237
+ rb_strhash_rehash(VALUE hash)
238
+ {
239
+ st_table *tbl;
240
+ #ifdef RUBY19
241
+ if (RHASH(hash)->iter_lev > 0) {
242
+ rb_raise(rb_eRuntimeError, "rehash during iteration");
243
+ }
244
+ rb_hash_modify_check(&hash);
245
+ if (!RHASH(hash)->ntbl)
246
+ return hash;
247
+ #endif
248
+ rb_hash_modify(hash);
249
+ tbl = st_init_table_with_size(&objstrhash, HASH_TBL(hash)->num_entries);
250
+ rb_hash_foreach(hash, rb_hash_rehash_i, (st_data_t)tbl);
251
+ st_free_table(HASH_TBL(hash));
252
+ HASH_TBL(hash) = tbl;
253
+
254
+ return hash;
255
+ }
256
+
257
+ static void
258
+ rb_strhash_convert(VALUE *val)
259
+ {
260
+ int i;
261
+ VALUE values;
262
+
263
+ switch (TYPE(*val)) {
264
+ case T_HASH:
265
+ *val = rb_hash_strhash(*val);
266
+ break;
267
+ case T_ARRAY:
268
+ values = rb_ary_new2(RARRAY_LEN(*val));
269
+ for (i = 0; i < RARRAY_LEN(*val); i++) {
270
+ VALUE el = RARRAY_PTR(*val)[i];
271
+ rb_ary_push(values, (TYPE(el) == T_HASH) ? rb_hash_strhash(el) : el);
272
+ }
273
+ *val = values;
274
+ break;
275
+ }
276
+ }
277
+
278
+ static VALUE
279
+ rb_strhash_aset(VALUE hash, VALUE key, VALUE val){
280
+ rb_strhash_convert(&val);
281
+ rb_hash_aset(hash, key, val);
282
+ return val;
283
+ }
284
+
285
+ /* hash.c */
286
+ static VALUE
287
+ rb_strhash_s_create(int argc, VALUE *argv, VALUE klass)
288
+ {
289
+ VALUE hash;
290
+ int i;
291
+
292
+ if (argc == 1 && TYPE(argv[0]) == T_HASH) {
293
+ hash = strhash_alloc0(&klass);
294
+ HASH_TBL(hash) = st_copy(HASH_TBL(argv[0]));
295
+ HASH_TBL(hash)->type = &objstrhash;
296
+ RHASH(hash)->ifnone = RHASH(argv[0])->ifnone;
297
+ return rb_strhash_rehash(hash);
298
+ }
299
+
300
+ if (argc % 2 != 0) {
301
+ rb_raise(rb_eArgError, "odd number of arguments for Hash");
302
+ }
303
+
304
+ hash = strhash_alloc(klass);
305
+ for (i=0; i<argc; i+=2) {
306
+ rb_strhash_aset(hash, argv[i], argv[i + 1]);
307
+ }
308
+
309
+ return hash;
310
+ }
311
+
312
+ static VALUE
313
+ rb_strhash_strhash(VALUE hash)
314
+ {
315
+ return hash;
316
+ }
317
+
318
+ static VALUE
319
+ rb_hash_strhash(VALUE hash)
320
+ {
321
+ VALUE args[1];
322
+ args[0] = hash;
323
+ return rb_strhash_s_create(1, (VALUE *)args, rb_cStrHash);
324
+ }
325
+
326
+ /* hash.c */
327
+ static VALUE
328
+ to_strhash(hash)
329
+ VALUE hash;
330
+ {
331
+ return rb_convert_type(hash, T_HASH, "StrHash", "to_hash");
332
+ }
333
+
334
+ /* hash.c */
335
+ static int
336
+ rb_strhash_update_i(VALUE key, VALUE value, VALUE *hash)
337
+ {
338
+ if (key == Qundef) return ST_CONTINUE;
339
+ rb_strhash_convert(&value);
340
+ st_insert(HASH_TBL(*hash), key, value);
341
+ return ST_CONTINUE;
342
+ }
343
+
344
+ /* hash.c */
345
+ static int
346
+ rb_strhash_update_block_i(VALUE key, VALUE value, VALUE *hash)
347
+ {
348
+ if (key == Qundef) return ST_CONTINUE;
349
+ if (rb_hash_has_key(*hash, key)) {
350
+ value = rb_yield_values(3, key, rb_hash_aref(*hash, key), value);
351
+ }
352
+ rb_strhash_convert(&value);
353
+ st_insert(HASH_TBL(*hash), key, value);
354
+ return ST_CONTINUE;
355
+ }
356
+
357
+ /* hash.c */
358
+ static VALUE
359
+ rb_strhash_update(VALUE hash1, VALUE hash2)
360
+ {
361
+ #ifdef RUBY19
362
+ rb_hash_modify(hash1);
363
+ #endif
364
+ hash2 = to_strhash(hash2);
365
+ if (rb_block_given_p()) {
366
+ rb_hash_foreach(hash2, rb_strhash_update_block_i, &hash1);
367
+ }
368
+ else {
369
+ rb_hash_foreach(hash2, rb_strhash_update_i, &hash1);
370
+ }
371
+ return hash1;
372
+ }
373
+
374
+ static VALUE
375
+ rb_strhash_initialize(int argc, VALUE *argv, VALUE hash){
376
+ VALUE constructor;
377
+ rb_scan_args(argc, argv, "01", &constructor);
378
+ if(TYPE(constructor) == T_HASH){
379
+ return rb_strhash_update(hash,constructor);
380
+ }else{
381
+ return rb_call_super(argc,argv);
382
+ }
383
+ }
384
+
385
+ static VALUE
386
+ rb_strhash_merge(VALUE hash1, VALUE hash2){
387
+ /* see note in Init */
388
+ return rb_strhash_update(hash1,hash2);
389
+ }
390
+
391
+ /*hash.c, see rb_strhash_to_hash*/
392
+ static VALUE
393
+ to_hash(hash)
394
+ VALUE hash;
395
+ {
396
+ return rb_convert_type(hash, T_HASH, "Hash", "to_hash");
397
+ }
398
+
399
+ /*hash.c, see rb_strhash_to_hash*/
400
+ static int
401
+ rb_hash_update_i(key, value, hash)
402
+ VALUE key, value;
403
+ VALUE *hash;
404
+ {
405
+ if (key == Qundef) return ST_CONTINUE;
406
+ #ifdef RUBY19
407
+ st_insert(RHASH(*hash)->ntbl, key, value);
408
+ #else
409
+ rb_hash_aset(*hash, key, value);
410
+ #endif
411
+ return ST_CONTINUE;
412
+ }
413
+
414
+ /*hash.c, see rb_strhash_to_hash*/
415
+ static int
416
+ rb_hash_update_block_i(key, value, hash)
417
+ VALUE key, value;
418
+ VALUE *hash;
419
+ {
420
+ if (key == Qundef) return ST_CONTINUE;
421
+ if (rb_hash_has_key(*hash, key)) {
422
+ value = rb_yield_values(3, key, rb_hash_aref(*hash, key), value);
423
+ }
424
+ #ifdef RUBY19
425
+ st_insert(RHASH(*hash)->ntbl, key, value);
426
+ #else
427
+ rb_hash_aset(*hash, key, value);
428
+ #endif
429
+ return ST_CONTINUE;
430
+ }
431
+
432
+ /*hash.c, see rb_strhash_to_hash*/
433
+ static VALUE
434
+ rb_hash_update(hash1, hash2)
435
+ VALUE hash1, hash2;
436
+ {
437
+ #ifdef RUBY19
438
+ rb_hash_modify(hash1);
439
+ #endif
440
+ hash2 = to_hash(hash2);
441
+ if (rb_block_given_p()) {
442
+ rb_hash_foreach(hash2, rb_hash_update_block_i, &hash1);
443
+ }
444
+ else {
445
+ rb_hash_foreach(hash2, rb_hash_update_i, &hash1);
446
+ }
447
+ return hash1;
448
+ }
449
+
450
+ static int
451
+ rb_strhash_to_hash_i(VALUE key, VALUE value, VALUE *hash){
452
+ if (SYMBOL_P(key)){
453
+ rb_hash_delete(*hash,key);
454
+ VALUE str = rb_str_new2(rb_id2name(SYM2ID(key)));
455
+ OBJ_FREEZE(str);
456
+ rb_hash_aset(*hash, str, value);
457
+ }
458
+ return ST_CONTINUE;
459
+ }
460
+
461
+ static VALUE
462
+ rb_strhash_to_hash(VALUE hash){
463
+ VALUE hsh = rb_hash_update(rb_hash_new(), hash);
464
+ RHASH(hsh)->ifnone = RHASH(hash)->ifnone;
465
+ rb_hash_foreach(hsh, rb_strhash_to_hash_i, &hsh);
466
+ return hsh;
467
+ }
468
+
469
+ void
470
+ Init_hwia()
471
+ {
472
+ id_hash = rb_intern("hash");
473
+ id_strhash = rb_intern("strhash");
474
+ hash_format = INT2FIX(536870923);
475
+
476
+ rb_cStrHash = rb_define_class("StrHash", rb_cHash);
477
+
478
+ rb_undef_alloc_func(rb_cStrHash);
479
+ rb_define_alloc_func(rb_cStrHash, strhash_alloc);
480
+ rb_define_singleton_method(rb_cStrHash, "[]", rb_strhash_s_create, -1);
481
+
482
+ rb_define_method(rb_cString, "strhash", rb_str_strhash_m, 0);
483
+ rb_define_method(rb_cSymbol, "strhash", rb_sym_strhash_m, 0);
484
+
485
+ rb_define_method(rb_cStrHash,"initialize", rb_strhash_initialize, -1);
486
+ rb_define_method(rb_cStrHash, "rehash", rb_strhash_rehash, 0);
487
+ /* revist, same API, but may be clobbered */
488
+ rb_define_method(rb_cStrHash, "dup", rb_hash_strhash, 0);
489
+ rb_define_method(rb_cStrHash, "strhash", rb_strhash_strhash, 0);
490
+ rb_define_method(rb_cStrHash, "[]=", rb_strhash_aset, 2);
491
+ rb_define_method(rb_cStrHash, "store", rb_strhash_aset, 2);
492
+ rb_define_method(rb_cStrHash, "update", rb_strhash_update, 1);
493
+ rb_define_method(rb_cStrHash, "merge!", rb_strhash_update, 1);
494
+ rb_define_method(rb_cStrHash, "merge", rb_strhash_merge, 1);
495
+ rb_define_method(rb_cStrHash, "to_hash", rb_strhash_to_hash, 0);
496
+ rb_define_method(rb_cHash, "strhash", rb_hash_strhash, 0);
497
+ }
@@ -0,0 +1,25 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "hwia"
3
+ s.version = "1.0.2"
4
+ s.date = "2009-08-23"
5
+ s.summary = "A faster HashWithIndifferentAccess (hwia) for MRI"
6
+ s.email = "lourens@methodmissing.com"
7
+ s.homepage = "http://github.com/methodmissing/hwia"
8
+ s.description = "A faster HashWithIndifferentAccess (hwia) for MRI (1.8.{6,7} and 1.9.2)"
9
+ s.has_rdoc = true
10
+ s.authors = ["Lourens Naudé (methodmissing)"]
11
+ s.platform = Gem::Platform::RUBY
12
+ s.files = %w[
13
+ README
14
+ Rakefile
15
+ bench/bench.rb
16
+ bench/as_hwia.rb
17
+ ext/hwia/extconf.rb
18
+ ext/hwia/hwia.c
19
+ lib/hwia_rails.rb
20
+ hwia.gemspec
21
+ ] + Dir.glob('test/*')
22
+ s.rdoc_options = ["--main", "README"]
23
+ s.extra_rdoc_files = ["README"]
24
+ s.extensions << "ext/hwia/extconf.rb"
25
+ end
@@ -0,0 +1,26 @@
1
+ require 'hwia'
2
+ raise LoadError.new("Rails environment required!") unless Object.const_defined?(:ActiveSupport)
3
+
4
+ class Hash
5
+ alias hash_with_indifferent_access strhash
6
+ end
7
+
8
+ class StrHash
9
+ def stringify_keys!; self end
10
+ def symbolize_keys!; self end
11
+ def to_options!; self end
12
+
13
+ protected
14
+ # AS test suite compat only
15
+ def convert_key(key)
16
+ key.kind_of?(Symbol) ? key.to_s : key
17
+ end
18
+ end
19
+
20
+ begin
21
+ old, $VERBOSE = $VERBOSE, nil
22
+ ActiveSupport::HashWithIndifferentAccess = StrHash
23
+ HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
24
+ ensure
25
+ $VERBOSE = old
26
+ end
@@ -0,0 +1,2 @@
1
+ require 'test/unit'
2
+ require 'hwia'
@@ -0,0 +1,284 @@
1
+ $:.unshift "." # 1.9.2
2
+ require File.dirname(__FILE__) + '/helper'
3
+
4
+ class TestSymbolAndStringHash < Test::Unit::TestCase
5
+ def test_hwia_hash
6
+ assert_hash_keys 'key', :key
7
+ assert_hash_keys 'under_scored_key', :under_scored_key
8
+ assert_hash_keys '@ivar', :@ivar
9
+ end
10
+
11
+ private
12
+ def assert_hash_keys(str,sym)
13
+ assert_equal str.strhash, sym.strhash
14
+ end
15
+ end
16
+
17
+ class HashToStrHash < Test::Unit::TestCase
18
+ def test_strhash
19
+ hash = { 'a' => 1, 'b' => 2 }
20
+ assert_instance_of StrHash, hash.strhash
21
+ assert_equal %w(a b), hash.keys
22
+ assert_equal [1,2], hash.values
23
+ end
24
+ end
25
+
26
+ class TestStrHash < Test::Unit::TestCase
27
+ def setup
28
+ @strings = { 'a' => 1, 'b' => 2 }.strhash
29
+ @symbols = { :a => 1, :b => 2 }.strhash
30
+ @mixed = { :a => 1, 'b' => 2 }.strhash
31
+ @fixnums = { 0 => 1, 1 => 2 }.strhash
32
+ end
33
+
34
+ def test_inherits_hash
35
+ assert_equal Hash, StrHash.superclass
36
+ end
37
+
38
+ def test_strhash
39
+ assert_equal @strings.object_id, @strings.strhash.object_id
40
+ assert_instance_of StrHash, { 'a' => 1, 'b' => 2 }.strhash
41
+ end
42
+
43
+ def test_initialize
44
+ strhash = StrHash.new({ 'a' => 1, 'b' => 2 })
45
+ assert_equal 1, strhash[:a]
46
+ strhash = StrHash.new
47
+ strhash[:a] = 'a'
48
+ assert_equal 'a', strhash[:a]
49
+ end
50
+
51
+ def test_convert
52
+ assert_equal 'a', @strings['a'] = 'a'
53
+ hash = { 'a' => 1, 'b' => 2 }
54
+ @strings[:str_hash] = hash
55
+ assert_instance_of StrHash, @strings[:str_hash]
56
+ assert_equal %w(a b), @strings[:str_hash].keys
57
+ assert_equal [:a,:b,:c], @strings[:array] = [:a,:b,:c]
58
+ array_with_hash = [{ 'a' => 1, 'b' => 2 }, [:a,:b,:c]]
59
+ @strings[:array_with_hash] = array_with_hash
60
+ assert_instance_of StrHash, @strings[:array_with_hash].shift
61
+ assert_equal [:a,:b,:c], @strings[:array_with_hash].pop
62
+ @strings[:other_hash] = { 'a' => 1, 'b' => 2 }
63
+ assert_instance_of StrHash, @strings[:other_hash]
64
+ nested_hash = { "urls" => { "url" => [ { "address" => "1" }, { "address" => "2" } ] }}
65
+ @strings[:nested_hash] = nested_hash
66
+ assert_instance_of Array, @strings[:nested_hash][:urls][:url]
67
+ end
68
+
69
+ def test_set
70
+ array = [{ 'a' => 1, 'b' => 2 }, [:a,:b,:c]]
71
+ @strings[:array] = array
72
+ assert_instance_of StrHash, @strings[:array].shift
73
+ assert_instance_of Array, @strings[:array] = array
74
+ assert_instance_of StrHash, @strings[:hash] = { 'a' => 1, 'b' => 2 }
75
+ end
76
+
77
+ def test_dup
78
+ assert_equal @strings, @strings.dup
79
+ assert_equal @mixed, @mixed.dup
80
+ assert_not_equal @mixed.object_id, @mixed.dup.object_id
81
+ end
82
+
83
+ def test_keys
84
+ assert_equal ["a", "b"], @strings.keys
85
+ assert_equal [:a, :b], @symbols.keys
86
+ assert_equal [:a, "b"], @mixed.keys
87
+ assert_equal [0, 1], @fixnums.keys
88
+ end
89
+
90
+ def test_values
91
+ assert_equal [1, 2], @strings.values
92
+ assert_equal [1, 2], @symbols.values
93
+ assert_equal [1, 2], @mixed.values
94
+ assert_equal [1, 2], @fixnums.values
95
+ end
96
+
97
+ def test_fetch
98
+ assert_equal 1, @strings.fetch('a')
99
+ assert_equal 1, @strings.fetch(:a.to_s)
100
+ assert_equal 1, @strings.fetch(:a)
101
+ end
102
+
103
+ def test_key?
104
+ assert @strings.key?(:a)
105
+ assert @strings.include?('a')
106
+ assert @mixed.has_key?('b')
107
+ end
108
+
109
+ def test_delete
110
+ @strings.delete('a')
111
+ assert !@strings.key?(:a)
112
+ end
113
+
114
+ def test_assorted
115
+ hashes = { :@strings => @strings, :@symbols => @symbols, :@mixed => @mixed }
116
+ method_map = { :'[]' => 1, :fetch => 1, :values_at => [1],
117
+ :has_key? => true, :include? => true, :key? => true,
118
+ :member? => true }
119
+
120
+ hashes.each do |name, hash|
121
+ method_map.sort_by { |m| m.to_s }.each do |meth, expected|
122
+ assert_equal(expected, hash.__send__(meth, 'a'),
123
+ "Calling #{name}.#{meth} 'a'")
124
+ assert_equal(expected, hash.__send__(meth, :a),
125
+ "Calling #{name}.#{meth} :a")
126
+ end
127
+ end
128
+
129
+ assert_equal [1, 2], @strings.values_at('a', 'b')
130
+ assert_equal [1, 2], @strings.values_at(:a, :b)
131
+ assert_equal [1, 2], @symbols.values_at('a', 'b')
132
+ assert_equal [1, 2], @symbols.values_at(:a, :b)
133
+ assert_equal [1, 2], @mixed.values_at('a', 'b')
134
+ assert_equal [1, 2], @mixed.values_at(:a, :b)
135
+ end
136
+
137
+ def test_reading
138
+ hash = StrHash.new
139
+ hash["a"] = 1
140
+ hash["b"] = true
141
+ hash["c"] = false
142
+ hash["d"] = nil
143
+
144
+ assert_equal 1, hash[:a]
145
+ assert_equal true, hash[:b]
146
+ assert_equal false, hash[:c]
147
+ assert_equal nil, hash[:d]
148
+ assert_equal nil, hash[:e]
149
+ end
150
+
151
+ def test_reading_with_nonnil_default
152
+ hash = StrHash.new(1)
153
+ hash["a"] = 1
154
+ hash["b"] = true
155
+ hash["c"] = false
156
+ hash["d"] = nil
157
+
158
+ assert_equal 1, hash[:a]
159
+ assert_equal true, hash[:b]
160
+ assert_equal false, hash[:c]
161
+ assert_equal nil, hash[:d]
162
+ assert_equal 1, hash[:e]
163
+ end
164
+
165
+ def test_writing
166
+ hash = StrHash.new
167
+ hash[:a] = 1
168
+ hash['b'] = 2
169
+ hash[3] = 3
170
+
171
+ assert_equal hash['a'], 1
172
+ assert_equal hash['b'], 2
173
+ assert_equal hash[:a], 1
174
+ assert_equal hash[:b], 2
175
+ assert_equal hash[3], 3
176
+ end
177
+
178
+ def test_update
179
+ hash = StrHash.new
180
+ hash[:a] = 'a'
181
+ hash['b'] = 'b'
182
+
183
+ updated_with_strings = hash.update(@strings)
184
+ updated_with_symbols = hash.update(@symbols)
185
+ updated_with_mixed = hash.update(@mixed)
186
+
187
+ assert_equal updated_with_strings[:a], 1
188
+ assert_equal updated_with_strings['a'], 1
189
+ assert_equal updated_with_strings['b'], 2
190
+
191
+ assert_equal updated_with_symbols[:a], 1
192
+ assert_equal updated_with_symbols['b'], 2
193
+ assert_equal updated_with_symbols[:b], 2
194
+
195
+ assert_equal updated_with_mixed[:a], 1
196
+ assert_equal updated_with_mixed['b'], 2
197
+
198
+ assert [updated_with_strings, updated_with_symbols, updated_with_mixed].all? { |h| h.keys.size == 2 }
199
+ end
200
+
201
+ def test_merging
202
+ hash = StrHash.new
203
+ hash[:a] = 'failure'
204
+ hash['b'] = 'failure'
205
+
206
+ other = { 'a' => 1, :b => 2 }
207
+
208
+ merged = hash.merge(other)
209
+
210
+ assert_equal StrHash, merged.class
211
+ assert_equal 1, merged[:a]
212
+ assert_equal 2, merged['b']
213
+
214
+ hash.update(other)
215
+
216
+ assert_equal 1, hash[:a]
217
+ assert_equal 2, hash['b']
218
+ end
219
+
220
+ def test_deleting
221
+ get_hash = proc{ StrHash[ :a => 'foo' ] }
222
+ hash = get_hash.call
223
+ assert_equal hash.delete(:a), 'foo'
224
+ assert_equal hash.delete(:a), nil
225
+ hash = get_hash.call
226
+ assert_equal hash.delete('a'), 'foo'
227
+ assert_equal hash.delete('a'), nil
228
+ end
229
+
230
+ def test_to_hash
231
+ assert_instance_of Hash, @strings.to_hash
232
+ assert_equal %w(a b), @strings.to_hash.keys
233
+ # Should convert to a Hash with String keys.
234
+ assert_equal @strings, @mixed.strhash.to_hash
235
+
236
+ # Should preserve the default value.
237
+ mixed_with_default = @mixed.dup
238
+ mixed_with_default.default = '1234'
239
+ roundtrip = mixed_with_default.strhash.to_hash
240
+ assert_equal @strings, roundtrip
241
+ assert_equal '1234', roundtrip.default
242
+ end
243
+
244
+ def test_hash_with_array_of_hashes
245
+ hash = { "urls" => { "url" => [ { "address" => "1" }, { "address" => "2" } ] }}
246
+ hwia = StrHash[hash]
247
+ assert_equal "1", hwia[:urls][:url].first[:address]
248
+ end
249
+
250
+ def test_indifferent_subhashes
251
+ h = {'user' => {'id' => 5}}.strhash
252
+ ['user', :user].each {|user| [:id, 'id'].each {|id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5"}}
253
+
254
+ h = {:user => {:id => 5}}.strhash
255
+ ['user', :user].each {|user| [:id, 'id'].each {|id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5"}}
256
+ end
257
+
258
+ def test_assorted_keys_not_stringified
259
+ original = {Object.new => 2, 1 => 2, [] => true}
260
+ indiff = original.strhash
261
+ assert(!indiff.keys.any? {|k| k.kind_of? String}, "A key was converted to a string!")
262
+ end
263
+
264
+ def test_should_use_default_value_for_unknown_key
265
+ hash_wia = StrHash.new(3)
266
+ assert_equal 3, hash_wia[:new_key]
267
+ end
268
+
269
+ def test_should_use_default_value_if_no_key_is_supplied
270
+ hash_wia = StrHash.new(3)
271
+ assert_equal 3, hash_wia.default
272
+ end
273
+
274
+ def test_should_nil_if_no_default_value_is_supplied
275
+ hash_wia = StrHash.new
276
+ assert_nil hash_wia.default
277
+ end
278
+
279
+ def test_should_copy_the_default_value_when_converting_to_hash_with_indifferent_access
280
+ hash = Hash.new(3)
281
+ hash_wia = hash.strhash
282
+ assert_equal 3, hash_wia.default
283
+ end
284
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hwia
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - "Lourens Naud\xC3\xA9 (methodmissing)"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-23 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A faster HashWithIndifferentAccess (hwia) for MRI (1.8.{6,7} and 1.9.2)
17
+ email: lourens@methodmissing.com
18
+ executables: []
19
+
20
+ extensions:
21
+ - ext/hwia/extconf.rb
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - README
26
+ - Rakefile
27
+ - bench/bench.rb
28
+ - bench/as_hwia.rb
29
+ - ext/hwia/extconf.rb
30
+ - ext/hwia/hwia.c
31
+ - lib/hwia_rails.rb
32
+ - hwia.gemspec
33
+ - test/helper.rb
34
+ - test/test_hwia.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/methodmissing/hwia
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --main
42
+ - README
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.3.5
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: A faster HashWithIndifferentAccess (hwia) for MRI
64
+ test_files: []
65
+