hwia 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+