gorillib 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/CHANGELOG.textile +6 -0
  2. data/README.textile +34 -11
  3. data/VERSION +1 -1
  4. data/gorillib.gemspec +63 -5
  5. data/lib/gorillib/enumerable/sum.rb +2 -2
  6. data/lib/gorillib/hash/compact.rb +2 -29
  7. data/lib/gorillib/hash/deep_compact.rb +2 -12
  8. data/lib/gorillib/hash/deep_dup.rb +4 -0
  9. data/lib/gorillib/hash/deep_merge.rb +2 -14
  10. data/lib/gorillib/hash/indifferent_access.rb +207 -0
  11. data/lib/gorillib/hash/keys.rb +2 -40
  12. data/lib/gorillib/hash/reverse_merge.rb +2 -24
  13. data/lib/gorillib/hash/slice.rb +2 -51
  14. data/lib/gorillib/hash/tree_merge.rb +4 -0
  15. data/lib/gorillib/hashlike.rb +824 -0
  16. data/lib/gorillib/hashlike/compact.rb +60 -0
  17. data/lib/gorillib/hashlike/deep_compact.rb +18 -0
  18. data/lib/gorillib/hashlike/deep_dup.rb +15 -0
  19. data/lib/gorillib/hashlike/deep_merge.rb +20 -0
  20. data/lib/gorillib/hashlike/hashlike_via_accessors.rb +169 -0
  21. data/lib/gorillib/hashlike/keys.rb +59 -0
  22. data/lib/gorillib/hashlike/reverse_merge.rb +31 -0
  23. data/lib/gorillib/hashlike/slice.rb +67 -0
  24. data/lib/gorillib/hashlike/tree_merge.rb +76 -0
  25. data/lib/gorillib/metaprogramming/mattr_accessor.rb +1 -1
  26. data/lib/gorillib/receiver.rb +315 -0
  27. data/lib/gorillib/receiver/active_model_shim.rb +19 -0
  28. data/lib/gorillib/receiver/acts_as_hash.rb +191 -0
  29. data/lib/gorillib/receiver/acts_as_loadable.rb +42 -0
  30. data/lib/gorillib/receiver/tree_diff.rb +74 -0
  31. data/lib/gorillib/receiver/validations.rb +30 -0
  32. data/lib/gorillib/struct/acts_as_hash.rb +108 -0
  33. data/lib/gorillib/struct/hashlike_iteration.rb +0 -0
  34. data/notes/fancy_hashes_and_receivers.textile +120 -0
  35. data/notes/hash_rdocs.textile +97 -0
  36. data/spec/hash/deep_merge_spec.rb +0 -2
  37. data/spec/hash/indifferent_access_spec.rb +391 -0
  38. data/spec/hash/slice_spec.rb +35 -12
  39. data/spec/hashlike/behave_same_as_hash_spec.rb +105 -0
  40. data/spec/hashlike/hashlike_behavior_spec.rb +824 -0
  41. data/spec/hashlike/hashlike_via_accessors_fuzzing_spec.rb +37 -0
  42. data/spec/hashlike/hashlike_via_accessors_spec.rb +262 -0
  43. data/spec/hashlike_spec.rb +302 -0
  44. data/spec/metaprogramming/aliasing_spec.rb +3 -0
  45. data/spec/metaprogramming/cattr_accessor_spec.rb +2 -0
  46. data/spec/metaprogramming/class_attribute_spec.rb +2 -0
  47. data/spec/metaprogramming/delegation_spec.rb +2 -0
  48. data/spec/metaprogramming/mattr_accessor_spec.rb +2 -0
  49. data/spec/metaprogramming/singleton_class_spec.rb +3 -0
  50. data/spec/receiver/acts_as_hash_spec.rb +286 -0
  51. data/spec/receiver_spec.rb +478 -0
  52. data/spec/spec_helper.rb +11 -6
  53. data/spec/string/truncate_spec.rb +1 -0
  54. data/spec/struct/acts_as_hash_fuzz_spec.rb +67 -0
  55. data/spec/struct/acts_as_hash_spec.rb +426 -0
  56. data/spec/support/hashlike_fuzzing_helper.rb +127 -0
  57. data/spec/support/hashlike_helper.rb +75 -0
  58. data/spec/support/hashlike_struct_helper.rb +37 -0
  59. data/spec/support/hashlike_via_delegation.rb +30 -0
  60. data/spec/support/matchers/be_array_eql.rb +12 -0
  61. data/spec/support/matchers/be_hash_eql.rb +14 -0
  62. data/spec/support/matchers/enumerate_method.rb +10 -0
  63. data/spec/support/matchers/evaluate_to_true.rb +5 -0
  64. metadata +62 -4
@@ -2,16 +2,21 @@ require 'rubygems' unless defined?(Gem)
2
2
  require 'spork'
3
3
  require 'rspec'
4
4
 
5
- Spork.prefork do
6
- # You'll need to restart for changes to configuration or code from libraries loaded here
5
+ GORILLIB_ROOT_DIR = File.expand_path(File.join(File.dirname(__FILE__),'..'))
6
+ def GORILLIB_ROOT_DIR *paths
7
+ File.join(::GORILLIB_ROOT_DIR, *paths)
8
+ end
7
9
 
8
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
10
+ Spork.prefork do # Must restart for changes to config / code from libraries loaded here
11
+ $LOAD_PATH.unshift(GORILLIB_ROOT_DIR('lib'))
12
+ $LOAD_PATH.unshift(GORILLIB_ROOT_DIR('spec/support'))
13
+ Dir[GORILLIB_ROOT_DIR('spec/support/matchers/*.rb')].each {|f| require f}
10
14
 
11
15
  RSpec.configure do |config|
12
16
  end
13
17
  end
14
18
 
15
- Spork.each_run do
16
- # This code will be run each time you run your specs.
19
+ Spork.each_run do # This code will be run each time you run your specs.
20
+ RSpec.configure do |config|
21
+ end
17
22
  end
@@ -1,4 +1,5 @@
1
1
  require File.dirname(__FILE__)+'/../spec_helper'
2
+ require GORILLIB_ROOT_DIR('spec/support/kcode_test_helper')
2
3
  require 'gorillib/string/truncate'
3
4
 
4
5
  describe String do
@@ -0,0 +1,67 @@
1
+ require File.dirname(__FILE__)+'/../spec_helper'
2
+ require 'gorillib/hashlike'
3
+ require 'gorillib/struct/acts_as_hash'
4
+ require 'gorillib/hash/indifferent_access'
5
+ require GORILLIB_ROOT_DIR('spec/support/hashlike_fuzzing_helper')
6
+ require GORILLIB_ROOT_DIR('spec/support/hashlike_helper')
7
+ require GORILLIB_ROOT_DIR('spec/support/hashlike_struct_helper')
8
+
9
+ #
10
+ # Don't test the built-in Struct methods.
11
+ #
12
+ # Also, all the enumerable methods behave differently -- they build off each
13
+ # (which iterates like an array), not each_pair (which iterates like a hash)
14
+ #
15
+ STRUCT_HASHLIKE_METHODS_TO_SKIP = [:each, :flatten, :clear, :values_at] +
16
+ Enumerable.public_instance_methods.map(&:to_sym) +
17
+ HashlikeHelper::HASH_METHODS_MISSING_FROM_VERSION
18
+
19
+ include HashlikeFuzzingHelper
20
+
21
+ describe "Hash vs Gorillib::Struct::ActsAsHash" do
22
+ before do
23
+ @total = 0
24
+ @hsh = HashlikeHelper::HASH_TO_TEST_HASHLIKE_STRUCT.dup
25
+ @hshlike = StructUsingHashlike.new.merge(HashlikeHelper::HASH_TO_TEST_HASHLIKE_STRUCT)
26
+ end
27
+
28
+ ( HashlikeHelper::METHODS_TO_TEST - STRUCT_HASHLIKE_METHODS_TO_SKIP ).each do |method_to_test|
29
+ describe "##{method_to_test}" do
30
+
31
+ (HashlikeFuzzingHelper::INPUTS_FOR_ALL_HASHLIKES).each do |input|
32
+ next if HashlikeFuzzingHelper::SPECIAL_CASES_FOR_HASHLIKE_STRUCT[method_to_test].include?(input)
33
+
34
+ it "on #{input.inspect}" do
35
+ behaves_the_same(@hsh, @hshlike, method_to_test, input)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ describe "Gorillib::HashWithIndifferentSymbolKeys vs Gorillib::Struct::ActsAsHash" do
43
+ before do
44
+ @total = 0
45
+ @hsh = Gorillib::HashWithIndifferentSymbolKeys.new_from_hash_copying_default(
46
+ HashlikeHelper::HASH_TO_TEST_HASHLIKE_STRUCT)
47
+ @hshlike = StructUsingHashlike.new.merge(
48
+ HashlikeHelper::HASH_TO_TEST_HASHLIKE_STRUCT)
49
+ end
50
+
51
+ ( HashlikeHelper::METHODS_TO_TEST - STRUCT_HASHLIKE_METHODS_TO_SKIP
52
+ ).each do |method_to_test|
53
+ describe "##{method_to_test}" do
54
+
55
+ ( HashlikeFuzzingHelper::INPUTS_WHEN_INDIFFERENT_ACCESS +
56
+ HashlikeFuzzingHelper::INPUTS_FOR_ALL_HASHLIKES
57
+ ).each do |input|
58
+ next if HashlikeFuzzingHelper::SPECIAL_CASES_FOR_HASHLIKE_STRUCT[method_to_test].include?(input)
59
+
60
+ it "on #{input.inspect}" do
61
+ behaves_the_same(@hsh, @hshlike, method_to_test, input)
62
+ end
63
+
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,426 @@
1
+ require File.dirname(__FILE__)+'/../spec_helper'
2
+ require 'gorillib/hashlike'
3
+ require 'gorillib/struct/acts_as_hash'
4
+ require GORILLIB_ROOT_DIR('spec/support/hashlike_via_delegation')
5
+ require GORILLIB_ROOT_DIR('spec/support/hashlike_helper')
6
+ require GORILLIB_ROOT_DIR('spec/support/hashlike_fuzzing_helper')
7
+ require GORILLIB_ROOT_DIR('spec/support/hashlike_struct_helper')
8
+ require GORILLIB_ROOT_DIR('spec/hashlike/hashlike_behavior_spec')
9
+
10
+
11
+
12
+
13
+
14
+ describe Gorillib::Struct::ActsAsHash do
15
+
16
+ before do
17
+ @hshlike = StructUsingHashlike.new.merge(HashlikeHelper::BASE_HSH.dup)
18
+ @empty_hshlike = StructUsingHashlike.new
19
+ @hshlike_with_array_vals = StructUsingHashlike.new.merge(HashlikeHelper::BASE_HSH_WITH_ARRAY_VALS.dup)
20
+ #
21
+ @hshlike_subklass = Class.new(StructUsingHashlike)
22
+ @hshlike_subklass_inst = @hshlike_subklass.new.merge(HashlikeHelper::BASE_HSH.dup)
23
+ end
24
+
25
+
26
+ # ===========================================================================
27
+ #
28
+ # Fundamental behavior
29
+
30
+ describe '#[] and #[]= and #store' do
31
+ it_should_behave_like :hashlike_store_and_retrieve
32
+ it_should_behave_like :references_string_and_symbol_keys_equivalently, ArgumentError
33
+ it 'reject unknown keys' do
34
+ lambda{ @hshlike[:fnord] = 69 }.should raise_error NameError, /no member 'fnord' in struct/
35
+ lambda{ @hshlike[:fnord] }.should raise_error NameError, /no member 'fnord' in struct/
36
+ @hshlike.delete(:fnord).should be_nil
37
+ end
38
+ it 'accepts defined but unset keys' do
39
+ @hshlike[:new_key].should be_nil
40
+ @hshlike[:new_key] = 69
41
+ @hshlike[:new_key].should == 69
42
+ end
43
+ it 'does not allow nil, Object, and other non-stringy keys' do
44
+ lambda{ @hshlike[300] = :i_haz_num }.should raise_error(IndexError, /offset 300 too large for struct/)
45
+ lambda{ @hshlike[nil] = :i_haz_nil }.should raise_error(TypeError, "no implicit conversion from nil to integer")
46
+ obj = Object.new
47
+ lambda{ @hshlike[obj] = :i_haz_obj }.should raise_error(TypeError, "can't convert Object into Integer")
48
+ def obj.to_sym() :c ; end
49
+ lambda{ @hshlike[obj] = :i_haz_obj }.should raise_error(TypeError, "can't convert Object into Integer")
50
+ end
51
+ end
52
+
53
+ describe '#delete' do
54
+ it 'removes the key/value association and returns the value' do
55
+ @hshlike.delete(:a).should == 100
56
+ @hshlike.delete(:is_missing).should be_nil
57
+ @hshlike.delete(:false_val).should == false
58
+ @hshlike.should be_hash_eql({ :b => 200, :c => 300, :nil_val => nil })
59
+ end
60
+ describe 'with optional code block' do
61
+ it 'returns the value of executing the block (passing in the key)' do
62
+ set_in_block = nil
63
+ ret_val = @hshlike.delete(:is_missing){|k| set_in_block = "got: #{k}" ; "hello!" }
64
+ set_in_block.should == "got: is_missing"
65
+ ret_val.should == "hello!"
66
+ end
67
+ end
68
+ it 'will have a nil value but will still include? key after deleting' do
69
+ @hshlike.should include(:a)
70
+ @hshlike.delete(:a)
71
+ @hshlike.should include(:a)
72
+ @hshlike[:a].should be_nil
73
+ end
74
+ end
75
+
76
+ describe '#keys' do
77
+ it 'lists keys, even where values are nil' do
78
+ @hshlike.keys.should be_array_eql([:a, :b, :c, :nil_val, :false_val, :new_key])
79
+ @hshlike[:nil_val].should be_nil
80
+ end
81
+ it 'is the full list of members, even when nothing has been set' do
82
+ @empty_hshlike.keys.should be_array_eql([:a, :b, :c, :nil_val, :false_val, :new_key])
83
+ end
84
+ it 'is the symbolized members list' do
85
+ @empty_hshlike.keys.map(&:to_s).should == @empty_hshlike.members.map(&:to_s)
86
+ end
87
+ end
88
+
89
+ # ===========================================================================
90
+ #
91
+ # Iteration
92
+
93
+ describe '#each_pair' do
94
+ describe 'with block' do
95
+ it_should_behave_like :each_pair_on_stringlike_fixed_keys, :each_pair
96
+ end
97
+ it_should_behave_like :with_no_block_returns_enumerator, :each_pair
98
+ end
99
+
100
+ describe '#each' do
101
+ describe 'with block' do
102
+ it 'calls block once for each *val* in hsh !like array not hash!' do
103
+ seen_arg1 = []
104
+ seen_arg2 = []
105
+ @hshlike.each{|arg1,arg2| seen_arg1 << arg1 ; seen_arg2 << arg2 }
106
+ seen_arg1.should be_array_eql([100, 200, 300, nil, false, nil])
107
+ seen_arg2.should be_array_eql([nil, nil, nil, nil, nil, nil])
108
+ end
109
+ it 'with arity 1, returns keys only' do
110
+ seen_args = []
111
+ @hshlike.each{|arg| seen_args << arg }
112
+ seen_args.should be_array_eql([100, 200, 300, nil, false, nil])
113
+ end
114
+ it 'handles array keys' do
115
+ seen_args = []
116
+ @hshlike_with_array_vals.each{|arg1, arg2, arg3| seen_args << [arg1, arg2, arg3] }
117
+ seen_args.should be_array_eql([[100, 111, nil], [200, nil, nil], [1, [2, 3, [4, 5, 6]], nil], [nil, nil, nil], [nil, nil, nil], [nil, nil, nil]])
118
+ seen_args = []
119
+ @hshlike_with_array_vals.each{|(arg1, arg2), arg3| seen_args << [arg1, arg2, arg3] }
120
+ seen_args.should be_array_eql([[100, nil, 111], [200, nil, nil], [1, nil, [2, 3, [4, 5, 6]]], [nil, nil, nil], [nil, nil, nil], [nil, nil, nil]])
121
+ end
122
+ it 'returns self' do
123
+ ret_val = @hshlike.each{|k,v| 3 }
124
+ ret_val.should equal(@hshlike)
125
+ end
126
+ end
127
+ it_should_behave_like :with_no_block_returns_enumerator, :each
128
+ end
129
+
130
+ describe '#each_key' do
131
+ describe 'with block' do
132
+ it_should_behave_like :each_key_on_stringlike_fixed_keys
133
+ end
134
+ it_should_behave_like :with_no_block_returns_enumerator, :each_key
135
+ end
136
+
137
+ describe '#each_value' do
138
+ describe 'with block' do
139
+ it_should_behave_like :each_value_on_stringlike_fixed_keys
140
+ end
141
+ it_should_behave_like :with_no_block_returns_enumerator, :each_value
142
+ end
143
+
144
+ # ===========================================================================
145
+ #
146
+ # Retrieval and Membership
147
+
148
+ describe '#values' do
149
+ it 'returns a new array populated with the values from hsh even when they were never set' do
150
+ @hshlike.values.should be_array_eql([100, 200, 300, nil, false, nil])
151
+ end
152
+ end
153
+
154
+ describe '#values_at' do
155
+ it 'takes positional, not symbol args' do
156
+ @hshlike.values_at(1, 0, 3, 5).should == [200, 100, nil, nil]
157
+ @hshlike.values_at(1, 1, 5, 3, 2, 5).should == [200, 200, nil, nil, 300, nil]
158
+ end
159
+ end
160
+
161
+ describe '#values_of' do
162
+ it_should_behave_like :hashlike_values_at_or_of, :values_of
163
+ end
164
+
165
+ describe '#length' do
166
+ it 'returns the number of key/value pairs in the hashlike' do
167
+ @hshlike.length.should == 6
168
+ @hshlike.length.should == @hshlike.members.length
169
+ @hshlike.delete(:a)
170
+ @hshlike.delete(:b)
171
+ @hshlike.length.should == 6
172
+ end
173
+ it 'is always the length of #members, regardless of contents' do
174
+ @empty_hshlike.length.should == 6
175
+ end
176
+ end
177
+
178
+ describe '#size' do
179
+ it 'returns the number of key/value pairs in the hashlike' do
180
+ @hshlike.size.should == 6
181
+ @hshlike.size.should == @hshlike.members.size
182
+ @hshlike.delete(:a)
183
+ @hshlike.delete(:b)
184
+ @hshlike.size.should == 6
185
+ end
186
+ it 'is always the length of #members, regardless of contents' do
187
+ @empty_hshlike.size.should == 6
188
+ end
189
+ end
190
+
191
+ describe '#has_key?' do
192
+ it_should_behave_like :hashlike_has_key_predefined_always_present, :has_key?
193
+ it_should_behave_like :hashlike_has_key_string_and_symbol_equivalent, :has_key?
194
+ end
195
+
196
+ describe '#include?' do
197
+ it_should_behave_like :hashlike_has_key_predefined_always_present, :include?
198
+ it_should_behave_like :hashlike_has_key_string_and_symbol_equivalent, :include?
199
+ end
200
+
201
+ describe '#key?' do
202
+ it_should_behave_like :hashlike_has_key_predefined_always_present, :key?
203
+ it_should_behave_like :hashlike_has_key_string_and_symbol_equivalent, :key?
204
+ end
205
+
206
+ describe '#member?' do
207
+ it_should_behave_like :hashlike_has_key_predefined_always_present, :member?
208
+ it_should_behave_like :hashlike_has_key_string_and_symbol_equivalent, :member?
209
+ end
210
+
211
+ describe '#has_value?' do
212
+ it_should_behave_like :hashlike_has_value?, :has_value?
213
+ end
214
+
215
+ describe '#value?' do
216
+ it_should_behave_like :hashlike_has_value?, :value?
217
+ end
218
+
219
+ describe '#fetch' do
220
+ it 'returns a value from the hashlike for the given key' do
221
+ @hshlike.fetch(:a).should == 100
222
+ @hshlike.fetch(:c).should == 300
223
+ @hshlike.fetch(:nil_val).should == nil
224
+ end
225
+ describe 'on a missing key' do
226
+ it 'with no other arguments, raises a +KeyError+ exception' do
227
+ lambda{ @hshlike.fetch(:is_missing) }.should raise_error(KeyError, 'key not found: :is_missing')
228
+ end
229
+ it 'if block given, runs the block with the given key and returns its value' do
230
+ set_in_block = nil
231
+ ret_val = @hshlike.fetch(:is_missing){|k| set_in_block = "got: #{k}" ; "hello!" }
232
+ ret_val.should == "hello!"
233
+ set_in_block.should == "got: is_missing"
234
+ end
235
+ it 'if default given, returns the default arg' do
236
+ ret_val = @hshlike.fetch(:is_missing, :returned_as_default)
237
+ ret_val.should == :returned_as_default
238
+ end
239
+ it 'if block and default are both given, issues a warning and runs the block' do
240
+ set_in_block = nil
241
+ ret_val = @hshlike.fetch(:is_missing, :spurious_default){|k| set_in_block = "got: #{k}" ; "hello!" }
242
+ ret_val.should == "hello!"
243
+ set_in_block.should == "got: is_missing"
244
+ end
245
+ end
246
+ it 'something something convert_key'
247
+ end
248
+
249
+ describe '#key' do
250
+ it_should_behave_like :hashlike_key
251
+ end
252
+
253
+ describe '#assoc' do
254
+ it_should_behave_like :hashlike_assoc
255
+ end
256
+
257
+ describe '#rassoc' do
258
+ it_should_behave_like :hashlike_rassoc
259
+ end
260
+
261
+ describe '#empty?' do
262
+ it 'returns true if the hashlike contains only nil values, false otherwise' do
263
+ @hshlike.empty?.should == false
264
+ @empty_hshlike.empty?.should == true
265
+ @empty_hshlike[:a] = false
266
+ @empty_hshlike.empty?.should == false
267
+ end
268
+ end
269
+
270
+ # ===========================================================================
271
+ #
272
+ # Update, merge!, merge
273
+
274
+
275
+ describe 'update' do
276
+ it_should_behave_like :merging_method, :update
277
+ it_should_behave_like :merging_method_with_struct_keys, :update
278
+ it_should_behave_like :merging_method_inplace, :update
279
+ end
280
+
281
+ describe '#merge!' do
282
+ it_should_behave_like :merging_method, :merge!
283
+ it_should_behave_like :merging_method_with_struct_keys, :merge!
284
+ it_should_behave_like :merging_method_inplace, :merge!
285
+ end
286
+
287
+ describe '#merge' do
288
+ it_should_behave_like :merging_method, :merge
289
+ it_should_behave_like :merging_method_with_struct_keys, :merge
290
+ it_should_behave_like :merging_method_returning_new, :merge
291
+ end
292
+
293
+ # ===========================================================================
294
+ #
295
+ # Filters
296
+
297
+ shared_examples_for :hashlike_filter_fixed_keys do |method_to_test|
298
+ it 'passes every key-value pair to block' do
299
+ seen_args = []
300
+ ret_val = @hshlike.send(method_to_test){|key,val| seen_args << [key, val] ; val && (val.to_i > 150) }
301
+ #
302
+ seen_args.should be_array_eql([[:a, 100], [:b, 200], [:c, 300], [:nil_val, nil], [:false_val, false], [:new_key, nil]])
303
+ end
304
+ it 'adapts to the arity of the block' do
305
+ seen_args = []
306
+ ret_val = @hshlike.send(method_to_test){|arg| seen_args << [arg] ; @hshlike[arg] && (@hshlike[arg].to_i > 150) }
307
+ #
308
+ seen_args.should be_array_eql([[:a], [:b], [:c], [:nil_val], [:false_val], [:new_key]])
309
+ end
310
+ describe 'with no block' do
311
+ it('returns an enumerator'){ @hshlike.send(method_to_test).should enumerate_method(@hshlike, method_to_test) }
312
+ end
313
+ end
314
+
315
+ describe '#reject!' do
316
+ it_should_behave_like :hashlike_filter_fixed_keys, :reject!
317
+ it_should_behave_like :rejection_filter, :reject!
318
+ it_should_behave_like :filter_modifies_self_returns_nil_if_unchanged, :reject!, false
319
+ end
320
+
321
+ describe '#select!' do
322
+ it_should_behave_like :hashlike_filter_fixed_keys, :select!
323
+ it_should_behave_like :selection_filter, :select!
324
+ it_should_behave_like :filter_modifies_self_returns_nil_if_unchanged, :select!, true
325
+ end
326
+
327
+ describe '#delete_if' do
328
+ it_should_behave_like :hashlike_filter_fixed_keys, :delete_if
329
+ it_should_behave_like :rejection_filter, :delete_if
330
+ it_should_behave_like :filter_modifies_self_returns_self, :delete_if, false
331
+ end
332
+
333
+ describe '#keep_if' do
334
+ it_should_behave_like :hashlike_filter_fixed_keys, :keep_if
335
+ it_should_behave_like :selection_filter, :keep_if
336
+ it_should_behave_like :filter_modifies_self_returns_self, :keep_if, true
337
+ end
338
+
339
+ # describe '#reject' do
340
+ # it_should_behave_like :hashlike_filter_fixed_keys, :select
341
+ # it_should_behave_like :selection_filter, :select
342
+ # it_should_behave_like :filter_does_not_modify_self_returns_same_class, :reject, false
343
+ # end
344
+ #
345
+ # describe '#select' do
346
+ # it_should_behave_like :hashlike_filter_fixed_keys, :select
347
+ # it_should_behave_like :selection_filter, :select
348
+ # it_should_behave_like :filter_does_not_modify_self_returns_same_class, :select, true
349
+ # end
350
+
351
+ describe '#clear' do
352
+ it_should_behave_like :hashlike_clear
353
+ end
354
+
355
+ # ===========================================================================
356
+ #
357
+ # Conversion
358
+
359
+ describe '#to_hash' do
360
+ it 'returns a new Hash with each key set to its associated value' do
361
+ ret_val = @hshlike.to_hash
362
+ ret_val.should be_an_instance_of(Hash)
363
+ ret_val.should == {:a=>100, :b=>200, :c=>300, :nil_val=>nil, :false_val=>false, :new_key=>nil}
364
+ end
365
+ end
366
+
367
+ if (RUBY_VERSION >= '1.9')
368
+ describe '#invert' do
369
+ it 'returns a new Hash using the values as keys, and the keys as values' do
370
+ ret_val = @hshlike.invert
371
+ ret_val.should == { 100 => :a, 200 => :b, 300 => :c, nil => :new_key, false => :false_val }
372
+ end
373
+ it 'with duplicate values, the result will contain only one of them as a key' do
374
+ @hshlike[:a] = 999
375
+ @hshlike[:new_key] = 999
376
+ @hshlike.invert.should == { 999 => :new_key, 200 => :b, 300 => :c, nil => :nil_val, false => :false_val }
377
+ end
378
+ it 'returns a Hash, not a self.class' do
379
+ ret_val = @hshlike.invert
380
+ ret_val.should be_an_instance_of(Hash)
381
+ end
382
+ end
383
+
384
+ describe '#flatten' do
385
+ it 'with no arg returns a one-dimensional flattening' do
386
+ ret_val = @hshlike_with_array_vals.flatten
387
+ ret_val.should == [ :a, [100, 111], :b, 200, :c, [1, [2, 3, [4, 5, 6]]], :nil_val, nil, :false_val, nil, :new_key, nil ]
388
+ end
389
+ it 'with no arg is same as level = 1' do
390
+ @hshlike_with_array_vals.flatten(1).should == @hshlike_with_array_vals.flatten
391
+ end
392
+ it 'with level == nil, returns a complete flattening' do
393
+ ret_val = @hshlike_with_array_vals.flatten(nil)
394
+ ret_val.should == [ :a, 100, 111, :b, 200, :c, 1, 2, 3, 4, 5, 6, :nil_val, nil, :false_val, nil, :new_key, nil ]
395
+ end
396
+ it 'with an arg, flattens to that level (0)' do
397
+ ret_val = @hshlike_with_array_vals.flatten(0)
398
+ ret_val.should == [ [:a, [100, 111]], [:b, 200], [:c, [1, [2, 3, [4, 5, 6]]]], [:nil_val, nil], [:false_val, nil], [:new_key, nil]]
399
+ end
400
+ it 'with an arg, flattens to that level (3)' do
401
+ ret_val = @hshlike_with_array_vals.flatten(3)
402
+ ret_val.should == [ :a, 100, 111, :b, 200, :c, 1, 2, 3, [4, 5, 6], :nil_val, nil, :false_val, nil, :new_key, nil ]
403
+ end
404
+ it 'with an arg, flattens to that level (4)' do
405
+ ret_val = @hshlike_with_array_vals.flatten(4)
406
+ ret_val.should == [ :a, 100, 111, :b, 200, :c, 1, 2, 3, 4, 5, 6, :nil_val, nil, :false_val, nil, :new_key, nil ]
407
+ ret_val.should == @hshlike_with_array_vals.flatten(999)
408
+ end
409
+ end
410
+ end
411
+
412
+ # ===========================================================================
413
+ #
414
+ # Sanity check
415
+
416
+ it 'built test objects correctly' do
417
+ @hshlike_subklass .should < @hshlike.class
418
+ @hshlike_subklass .should_not == @hshlike.class
419
+ @hshlike_subklass_inst .should be_a(StructUsingHashlike)
420
+ @hshlike_subklass_inst .should be_a(@hshlike_subklass)
421
+ @hshlike_subklass_inst .should_not be_an_instance_of(StructUsingHashlike)
422
+ @hshlike .should_not be_a(@hshlike_subklass)
423
+ @hshlike .should be_an_instance_of(StructUsingHashlike)
424
+ end
425
+
426
+ end