aquarium 0.4.4 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/CHANGES +31 -6
  2. data/README +4 -1
  3. data/RELEASE-PLAN +2 -0
  4. data/Rakefile +20 -30
  5. data/UPGRADE +14 -4
  6. data/lib/aquarium/aspects/advice.rb +25 -10
  7. data/lib/aquarium/aspects/aspect.rb +8 -7
  8. data/lib/aquarium/aspects/join_point.rb +15 -5
  9. data/lib/aquarium/aspects/pointcut.rb +4 -4
  10. data/lib/aquarium/extensions.rb +0 -1
  11. data/lib/aquarium/finders/method_finder.rb +3 -10
  12. data/lib/aquarium/finders/pointcut_finder.rb +15 -5
  13. data/lib/aquarium/finders/type_finder.rb +0 -1
  14. data/lib/aquarium/utils/array_utils.rb +0 -1
  15. data/lib/aquarium/utils/method_utils.rb +13 -2
  16. data/lib/aquarium/utils/options_utils.rb +1 -0
  17. data/lib/aquarium/utils/type_utils.rb +21 -5
  18. data/lib/aquarium/version.rb +2 -2
  19. data/spec/aquarium/aspects/advice_chain_node_spec.rb +0 -1
  20. data/spec/aquarium/aspects/advice_spec.rb +80 -45
  21. data/spec/aquarium/aspects/aspect_invocation_spec.rb +66 -31
  22. data/spec/aquarium/aspects/aspect_spec.rb +88 -91
  23. data/spec/aquarium/aspects/concurrent_aspects_spec.rb +1 -1
  24. data/spec/aquarium/aspects/concurrent_aspects_with_objects_and_types_spec.rb +3 -1
  25. data/spec/aquarium/aspects/join_point_spec.rb +0 -1
  26. data/spec/aquarium/aspects/pointcut_spec.rb +21 -18
  27. data/spec/aquarium/extensions/hash_spec.rb +211 -219
  28. data/spec/aquarium/extensions/set_spec.rb +1 -1
  29. data/spec/aquarium/extras/design_by_contract_spec.rb +1 -1
  30. data/spec/aquarium/finders/finder_result_spec.rb +4 -4
  31. data/spec/aquarium/finders/method_finder_spec.rb +6 -9
  32. data/spec/aquarium/finders/type_finder_spec.rb +2 -2
  33. data/spec/aquarium/finders/type_finder_with_descendents_and_ancestors_spec.rb +12 -10
  34. data/spec/aquarium/spec_example_types.rb +2 -2
  35. data/spec/aquarium/spec_helper.rb +1 -1
  36. data/spec/aquarium/utils/array_utils_spec.rb +32 -5
  37. data/spec/aquarium/utils/hash_utils_spec.rb +1 -0
  38. data/spec/aquarium/utils/method_utils_spec.rb +18 -0
  39. data/spec/aquarium/utils/options_utils_spec.rb +16 -20
  40. data/spec/aquarium/utils/type_utils_sample_classes.rb +10 -1
  41. data/spec/aquarium/utils/type_utils_spec.rb +9 -7
  42. metadata +29 -35
  43. data/lib/aquarium/extensions/symbol.rb +0 -22
  44. data/spec/aquarium/extensions/symbol_spec.rb +0 -37
@@ -1,6 +1,7 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper'
2
2
  require 'aquarium/spec_example_types'
3
3
  require 'aquarium/aspects'
4
+ require 'aquarium/utils'
4
5
  require 'logger'
5
6
 
6
7
  include Aquarium::Aspects
@@ -23,10 +24,10 @@ end
23
24
 
24
25
 
25
26
  describe Aspect, " cannot advise the private implementation methods inserted by other aspects" do
27
+ class WithAspectLikeMethod
28
+ def _aspect_foo; end
29
+ end
26
30
  it "should have no affect." do
27
- class WithAspectLikeMethod
28
- def _aspect_foo; end
29
- end
30
31
  aspect = Aspect.new(:after, :pointcut => {:type => WithAspectLikeMethod, :methods => :_aspect_foo}) {|jp, obj, *args| fail}
31
32
  WithAspectLikeMethod.new._aspect_foo
32
33
  aspect.unadvise
@@ -40,7 +41,7 @@ describe Aspect, " when advising a type" do
40
41
  after(:each) do
41
42
  @aspect.unadvise
42
43
  end
43
-
44
+
44
45
  it "should not add new public instance or class methods that the advised type responds to." do
45
46
  all_public_methods_before = all_public_methods_of_type Watchful
46
47
  @aspect = Aspect.new :after, :pointcut => {:type => Watchful, :method_options => :exclude_ancestor_methods}, :advice => @advice
@@ -153,26 +154,25 @@ describe Aspect, " with :after advice" do
153
154
  do_watchful_public_protected_private
154
155
  end
155
156
 
156
- it "should ignore the value returned by the advice" do
157
- class ReturningValue
158
- def doit args
159
- args + ["d"]
160
- end
157
+ class ReturningValue
158
+ def doit args
159
+ args + ["d"]
161
160
  end
161
+ end
162
+
163
+ it "should ignore the value returned by the advice" do
162
164
  ary = %w[a b c]
163
165
  ReturningValue.new.doit(ary).should == %w[a b c d]
166
+ advise_called = false
164
167
  @aspect = Aspect.new :after, :type => ReturningValue, :method => :doit do |jp, obj, *args|
168
+ advise_called = true
165
169
  %w[aa] + jp.context.returned_value + %w[e]
166
170
  end
167
171
  ReturningValue.new.doit(ary).should == %w[a b c d]
172
+ advise_called.should == true
168
173
  end
169
174
 
170
- it "should all the advice to assign a new return value" do
171
- class ReturningValue
172
- def doit args
173
- args + ["d"]
174
- end
175
- end
175
+ it "should allow the advice to assign a new return value" do
176
176
  ary = %w[a b c]
177
177
  ReturningValue.new.doit(ary).should == %w[a b c d]
178
178
  @aspect = Aspect.new :after, :type => ReturningValue, :method => :doit do |jp, obj, *args|
@@ -223,26 +223,25 @@ describe Aspect, " with :after_returning advice" do
223
223
  do_watchful_public_protected_private
224
224
  end
225
225
 
226
- it "should ignore the value returned by the advice" do
227
- class ReturningValue
228
- def doit args
229
- args + ["d"]
230
- end
226
+ class ReturningValue
227
+ def doit args
228
+ args + ["d"]
231
229
  end
230
+ end
231
+
232
+ it "should ignore the value returned by the advice" do
232
233
  ary = %w[a b c]
233
234
  ReturningValue.new.doit(ary).should == %w[a b c d]
235
+ advice_called = false
234
236
  @aspect = Aspect.new :after_returning, :type => ReturningValue, :method => :doit do |jp, obj, *args|
237
+ advice_called = true
235
238
  %w[aa] + jp.context.returned_value + %w[e]
236
239
  end
237
240
  ReturningValue.new.doit(ary).should == %w[a b c d]
241
+ advice_called.should == true
238
242
  end
239
243
 
240
244
  it "should allow the advice to change the returned value" do
241
- class ReturningValue
242
- def doit args
243
- args + ["d"]
244
- end
245
- end
246
245
  ary = %w[a b c]
247
246
  ReturningValue.new.doit(ary).should == %w[a b c d]
248
247
  @aspect = Aspect.new :after_returning, :type => ReturningValue, :method => :doit do |jp, obj, *args|
@@ -567,25 +566,25 @@ describe Aspect, " with :around advice" do
567
566
  advice_called.should be_true
568
567
  end
569
568
 
570
- it "should advise subclass invocations of methods advised in the superclass." do
571
- module AdvisingSuperClass
572
- class SuperClass
573
- def public_method *args
574
- # yield *args if block_given?
575
- end
576
- protected
577
- def protected_method *args
578
- yield *args if block_given?
579
- end
580
- private
581
- def private_method *args
582
- yield *args if block_given?
583
- end
569
+ module AdvisingSuperClass
570
+ class SuperClass
571
+ def public_method *args
572
+ # yield *args if block_given?
584
573
  end
585
- class SubClass < SuperClass
574
+ protected
575
+ def protected_method *args
576
+ yield *args if block_given?
577
+ end
578
+ private
579
+ def private_method *args
580
+ yield *args if block_given?
586
581
  end
587
582
  end
588
-
583
+ class SubClass < SuperClass
584
+ end
585
+ end
586
+
587
+ it "should advise subclass invocations of methods advised in the superclass." do
589
588
  child = AdvisingSuperClass::SubClass.new
590
589
  advice_called = false
591
590
  @aspect = Aspect.new :around, :pointcut => {:type => AdvisingSuperClass::SuperClass, :methods => [:public_method]} do |jp, obj, *args|
@@ -608,25 +607,25 @@ describe Aspect, " with :around advice" do
608
607
  advice_called.should be_true
609
608
  end
610
609
 
611
- it "should advise subclass invocations of methods advised in the subclass that are defined in the superclass." do
612
- module AdvisingSubClass
613
- class SuperClass
614
- def public_method *args
615
- # yield *args if block_given?
616
- end
617
- protected
618
- def protected_method *args
619
- yield *args if block_given?
620
- end
621
- private
622
- def private_method *args
623
- yield *args if block_given?
624
- end
610
+ module AdvisingSubClass
611
+ class SuperClass
612
+ def public_method *args
613
+ # yield *args if block_given?
625
614
  end
626
- class SubClass < SuperClass
615
+ protected
616
+ def protected_method *args
617
+ yield *args if block_given?
618
+ end
619
+ private
620
+ def private_method *args
621
+ yield *args if block_given?
627
622
  end
628
623
  end
629
-
624
+ class SubClass < SuperClass
625
+ end
626
+ end
627
+
628
+ it "should advise subclass invocations of methods advised in the subclass that are defined in the superclass." do
630
629
  child = AdvisingSubClass::SubClass.new
631
630
  advice_called = false
632
631
  @aspect = Aspect.new :around, :pointcut => {:type => AdvisingSubClass::SuperClass, :methods => [:public_method]} do |jp, obj, *args|
@@ -649,18 +648,19 @@ describe Aspect, " with :around advice" do
649
648
  advice_called.should be_true
650
649
  end
651
650
 
652
- it "should not advise subclass overrides of superclass methods, when advising superclasses (but calls to superclass methods are advised)." do
653
- class WatchfulChild2 < Watchful
654
- def public_watchful_method *args
655
- @override_called = true
656
- yield(*args) if block_given?
657
- end
658
- attr_reader :override_called
659
- def initialize
660
- super
661
- @override_called = false
662
- end
651
+ class WatchfulChild2 < Watchful
652
+ def public_watchful_method *args
653
+ @override_called = true
654
+ yield(*args) if block_given?
663
655
  end
656
+ attr_reader :override_called
657
+ def initialize
658
+ super
659
+ @override_called = false
660
+ end
661
+ end
662
+
663
+ it "should not advise subclass overrides of superclass methods, when advising superclasses (but calls to superclass methods are advised)." do
664
664
  @aspect = Aspect.new(:around, :pointcut => {:type => Watchful, :methods => [:public_watchful_method]}) {|jp, obj, *args| fail}
665
665
  child = WatchfulChild2.new
666
666
  public_block_called = false
@@ -697,12 +697,13 @@ describe Aspect, " with :around advice" do
697
697
  watchful.public_watchful_method_args.should == [:a4, :a5, :a6]
698
698
  end
699
699
 
700
- it "should return the value returned by the advice, NOT the value returned by the advised join point!" do
701
- class ReturningValue
702
- def doit args
703
- args + ["d"]
704
- end
700
+ class ReturningValue
701
+ def doit args
702
+ args + ["d"]
705
703
  end
704
+ end
705
+
706
+ it "should return the value returned by the advice, NOT the value returned by the advised join point!" do
706
707
  ary = %w[a b c]
707
708
  ReturningValue.new.doit(ary).should == %w[a b c d]
708
709
  @aspect = Aspect.new :around, :type => ReturningValue, :method => :doit do |jp, obj, *args|
@@ -713,11 +714,6 @@ describe Aspect, " with :around advice" do
713
714
  end
714
715
 
715
716
  it "should return the value returned by the advised join point only if the advice returns the value" do
716
- class ReturningValue
717
- def doit args
718
- args + ["d"]
719
- end
720
- end
721
717
  ary = %w[a b c]
722
718
  ReturningValue.new.doit(ary).should == %w[a b c d]
723
719
  @aspect = Aspect.new :around, :type => ReturningValue, :method => :doit do |jp, obj, *args|
@@ -923,8 +919,9 @@ end
923
919
  %w[public protected private].each do |protection|
924
920
  describe Aspect, " when advising and unadvising #{protection} methods" do
925
921
  it "should keep the protection level of the advised methods unchanged." do
926
- meta = "#{protection}_instance_methods"
927
- method = "#{protection}_watchful_method"
922
+ meta = :"#{protection}_instance_methods"
923
+ method_name = "#{protection}_watchful_method"
924
+ method = Aquarium::Utils::MethodUtils.to_name method_name
928
925
  Watchful.send(meta).should include(method)
929
926
  aspect = Aspect.new(:after, :type => Watchful, :method => method.intern, :method_options => [protection.intern]) {|jp, obj, *args| true }
930
927
  Watchful.send(meta).should include(method)
@@ -935,13 +932,18 @@ end
935
932
  end
936
933
 
937
934
 
935
+ class TypeDefinedMethodClass
936
+ def inititalize; @called = false; end
937
+ def m; @called = true; end
938
+ attr_reader :called
939
+ end
940
+
941
+ class InstanceDefinedMethodClass
942
+ def inititalize; @called = false; end
943
+ attr_reader :called
944
+ end
945
+
938
946
  describe Aspect, " when unadvising methods for instance-type pointcuts for type-defined methods" do
939
- class TypeDefinedMethodClass
940
- def inititalize; @called = false; end
941
- def m; @called = true; end
942
- attr_reader :called
943
- end
944
-
945
947
  it "should cause the object to respond to the type's original method." do
946
948
  object = TypeDefinedMethodClass.new
947
949
  aspect = Aspect.new(:before, :object => object, :method => :m) {true}
@@ -952,11 +954,6 @@ describe Aspect, " when unadvising methods for instance-type pointcuts for type-
952
954
  end
953
955
 
954
956
  describe Aspect, " when unadvising methods for instance-type pointcuts for instance-defined methods" do
955
- class InstanceDefinedMethodClass
956
- def inititalize; @called = false; end
957
- attr_reader :called
958
- end
959
-
960
957
  it "should cause the object to respond to the object's original method." do
961
958
  object = TypeDefinedMethodClass.new
962
959
  def object.m; @called = true; end
@@ -95,7 +95,7 @@ module ConcurrentAspectsSpecSupport
95
95
  end
96
96
  end
97
97
 
98
- describe "concurrent advice", :shared => true do
98
+ shared_examples_for "concurrent advice" do
99
99
  include ConcurrentAspectsSpecSupport
100
100
 
101
101
  before :all do
@@ -37,7 +37,9 @@ describe "Advising an object join point and then the corresponding type join poi
37
37
  rescue ConcurrentlyAccessed::Error
38
38
  fail unless advice_kind == :after_raising
39
39
  end
40
- @invoked.sort.should == [:object, :type]
40
+ @invoked.length.should == 2
41
+ @invoked.should include(:object)
42
+ @invoked.should include(:type)
41
43
  end
42
44
  end
43
45
 
@@ -269,7 +269,6 @@ describe JoinPoint, "#invoke_original_join_point" do
269
269
  end
270
270
  end
271
271
 
272
-
273
272
  describe JoinPoint, "#dup" do
274
273
  it "should duplicate the fields in the join point." do
275
274
  jp = JoinPoint.new :type => String, :method_name => :count
@@ -95,8 +95,11 @@ def before_pointcut_module_spec
95
95
  end
96
96
 
97
97
  def ignored_join_point jp
98
- # Ignore any types introduced by RSpec, other Aquarium types, and the "pretty printer" module (which Rake uses?)
99
- jp.target_type.name =~ /^Spec/ or
98
+ # Ignore any type ambiguities introduced by Ruby 1.9.X.
99
+ # Igore types introduced by RSpec, other Aquarium types,
100
+ # and the "pretty printer" module (which Rake uses?)
101
+ jp.target_type.name =~ /^(Basic)?Object/ or
102
+ jp.target_type.name =~ /^R?Spec/ or
100
103
  jp.target_type.name =~ /^Aquarium::(Aspects|Extras|Utils|PointcutFinderTestClasses)/ or
101
104
  jp.target_type.name =~ /^PP/ or
102
105
  jp.target_type.name =~ /InstanceExecHelper/
@@ -286,7 +289,7 @@ describe Pointcut, "methods" do
286
289
 
287
290
  it "should match classes specified and their ancestor and descendent modules and classes." do
288
291
  pc = Pointcut.new :types_and_ancestors => /^Class(Including|DerivedFrom).*Method/, :types_and_descendents => /^Class(Including|DerivedFrom).*Method/, :methods => :all, :method_options => :exclude_ancestor_methods
289
- expected_types = @example_modules_with_public_instance_method + [Kernel, Module, Object]
292
+ expected_types = @example_modules_with_public_instance_method + [Kernel, Module]
290
293
  pc.join_points_matched.each do |jp|
291
294
  next if ignored_join_point(jp)
292
295
  expected_types.should include(jp.target_type)
@@ -555,15 +558,14 @@ describe Pointcut, "methods" do
555
558
  expected_types = [
556
559
  ClassDerivedFromClassIncludingModuleWithPublicInstanceMethod,
557
560
  ClassIncludingModuleWithPublicInstanceMethod,
558
- Kernel,
559
- Object]
561
+ Kernel]
560
562
  found_types = {}
561
563
  pc.join_points_matched.each do |jp|
562
564
  next if ignored_join_point(jp)
563
565
  expected_types.should include(jp.target_type)
564
566
  found_types[jp.target_type] = true
565
567
  end
566
- found_types.size.should == 4
568
+ found_types.size.should == 3
567
569
  not_expected_types = @expected_modules_not_matched_jps.map {|jp| jp.target_type}
568
570
  pc.join_points_not_matched.each do |jp|
569
571
  next if ignored_join_point(jp)
@@ -583,14 +585,14 @@ describe Pointcut, "methods" do
583
585
  end
584
586
 
585
587
  def check_module_descendents pc
586
- expected_types = [Kernel, Object]
588
+ expected_types = [Kernel]
587
589
  found_types = {}
588
590
  pc.join_points_matched.each do |jp|
589
591
  next if ignored_join_point(jp)
590
592
  expected_types.should include(jp.target_type)
591
593
  found_types[jp.target_type] = true
592
594
  end
593
- found_types.size.should == 2
595
+ found_types.size.should == 1
594
596
  not_expected_types = @expected_modules_not_matched_jps.map {|jp| jp.target_type}
595
597
  pc.join_points_not_matched.each do |jp|
596
598
  next if ignored_join_point(jp)
@@ -637,14 +639,14 @@ describe Pointcut, "methods" do
637
639
  end
638
640
 
639
641
  def check_class_descendents pc
640
- expected_types = [Kernel, ModuleIncludingModuleWithPublicInstanceMethod, ModuleWithPublicInstanceMethod, Object]
642
+ expected_types = [Kernel, ModuleIncludingModuleWithPublicInstanceMethod, ModuleWithPublicInstanceMethod]
641
643
  found_types = {}
642
644
  pc.join_points_matched.each do |jp|
643
645
  next if ignored_join_point(jp)
644
646
  expected_types.should include(jp.target_type)
645
647
  found_types[jp.target_type] = true
646
648
  end
647
- found_types.size.should == 4
649
+ found_types.size.should == 3
648
650
  not_expected_types = @expected_modules_not_matched_jps.map {|jp| jp.target_type}
649
651
  pc.join_points_not_matched.each do |jp|
650
652
  next if ignored_join_point(jp)
@@ -1423,15 +1425,15 @@ describe Pointcut, "methods" do
1423
1425
  end
1424
1426
  end
1425
1427
 
1426
- describe Pointcut, ".new (methods that end in non-alphanumeric characters)" do
1427
- class ClassWithFunkyMethodNames
1428
- def huh?; true; end
1429
- def yes!; true; end
1430
- def x= other; false; end
1431
- def == other; false; end
1432
- def =~ other; false; end
1433
- end
1428
+ class ClassWithFunkyMethodNames
1429
+ def huh?; true; end
1430
+ def yes!; true; end
1431
+ def x= other; false; end
1432
+ def == other; false; end
1433
+ def =~ other; false; end
1434
+ end
1434
1435
 
1436
+ describe Pointcut, ".new (methods that end in non-alphanumeric characters)" do
1435
1437
  before(:each) do
1436
1438
  @funky = ClassWithFunkyMethodNames.new
1437
1439
  end
@@ -1725,6 +1727,7 @@ describe Pointcut, "methods" do
1725
1727
  pc1 = Pointcut.new :types => /Class.*Method/, :methods => /_test_method$/
1726
1728
  pc2 = Pointcut.new :types => /Class.*Method/, :methods => /_test_method$/
1727
1729
  pc3 = Pointcut.new :objects => [ClassWithPublicInstanceMethod.new, ClassWithPublicInstanceMethod.new]
1730
+
1728
1731
  pc1.should be_eql(pc1)
1729
1732
  pc1.should be_eql(pc2)
1730
1733
  pc1.should_not eql(pc3)
@@ -19,260 +19,252 @@ class CC
19
19
  end
20
20
  end
21
21
 
22
- def before_hash_spec
23
- @c1 = CC.new(1)
24
- @c2 = CC.new(2)
25
- @c3 = CC.new(3)
26
- @cc1 = [@c1, @c2]
27
- @cc2 = [@c2, @c3]
28
- @hash = {:a => ['a1'], :b => [:b1, :b2], :c => @cc1}
29
- end
30
-
31
- describe "intersection of hashes", :shared => true do
32
- include Aquarium::Utils::ArrayUtils
33
- include Aquarium::Utils::HashUtils
34
-
22
+ describe "hash extensions" do
35
23
  before(:each) do
36
- before_hash_spec
37
- end
38
-
39
- it "should return the same hash if intersected with itself." do
40
- @hash.and(@hash).should == @hash
41
- end
42
-
43
- it "should return the same hash if intersected with an equivalent hash." do
44
- @hash.and({:a => ['a1'], :b => [:b1, :b2], :c => @cc1}).should == @hash
45
- end
46
-
47
- it "should return an empty hash if one of the input hashes is empty." do
48
- {}.and(@hash).should == {}
49
- end
50
-
51
- it "should return the common subset hash for two, if the values respond to #&." do
52
- hash2 = {:b => [:b1], :c => @cc2, :d => ['d']}
53
- @hash.and(hash2).should == {:b => [:b1], :c => [@c2]}
54
- end
55
-
56
- it "should return the common subset of hash values for partially-overlapping keys as specified by a given block." do
57
- hash2 = {:b =>:b1, :c => @cc2, :d => 'd'}
58
- @hash.and(hash2){|value1, value2| Set.new(make_array(value1)).intersection(Set.new(make_array(value2)))}.should == {:b => Set.new([:b1]), :c => Set.new([@c2])}
24
+ @c1 = CC.new(1)
25
+ @c2 = CC.new(2)
26
+ @c3 = CC.new(3)
27
+ @cc1 = [@c1, @c2]
28
+ @cc2 = [@c2, @c3]
29
+ @hash = {:a => ['a1'], :b => [:b1, :b2], :c => @cc1}
59
30
  end
60
- end
61
31
 
62
- describe Hash, "#intersection" do
63
- it_should_behave_like "intersection of hashes"
64
- end
32
+ shared_examples_for "intersection of hashes" do
33
+ include Aquarium::Utils::ArrayUtils
34
+ include Aquarium::Utils::HashUtils
65
35
 
66
- describe Hash, "#and" do
67
- it_should_behave_like "intersection of hashes"
68
- end
36
+ it "should return the same hash if intersected with itself." do
37
+ @hash.and(@hash).should == @hash
38
+ end
69
39
 
70
- describe Hash, "#&" do
71
- it_should_behave_like "intersection of hashes"
72
-
73
- it "should support operator-style semantics" do
74
- ({:a => ['a1', 'a2'], :c => @cc1} & {:a => ['a1'], :b => [:b1, :b2], :c => @cc2}).should == {:a => ['a1'], :c => [@c2]}
75
- end
76
- end
40
+ it "should return the same hash if intersected with an equivalent hash." do
41
+ @hash.and({:a => ['a1'], :b => [:b1, :b2], :c => @cc1}).should == @hash
42
+ end
77
43
 
78
- describe "union of hashes", :shared => true do
79
- include Aquarium::Utils::ArrayUtils
80
- include Aquarium::Utils::HashUtils
44
+ it "should return an empty hash if one of the input hashes is empty." do
45
+ {}.and(@hash).should == {}
46
+ end
81
47
 
82
- before(:each) do
83
- before_hash_spec
84
- end
85
-
86
- it "should return the same hash if unioned with itself." do
87
- @hash.union(@hash).should == @hash
88
- end
89
-
90
- it "should return the same hash if unioned with an equivalent hash." do
91
- @hash.union({:a => ['a1'], :b => [:b1, :b2], :c => @cc1}).should == @hash
92
- end
93
-
94
- it "should return a hash that is equivalent to the non-empty hash if the other hash is empty." do
95
- {}.union(@hash).should == @hash
96
- @hash.union({}).should == @hash
97
- end
98
-
99
- it "should return the same hash if unioned with nil." do
100
- @hash.union(nil).should == @hash
101
- end
102
-
103
- it "should return the combined hash value, if the values respond to #|." do
104
- hash2 = {:b => [:b3], :c => @cc2, :d => ['d']}
105
- @hash.union(hash2).should == {:a => ['a1'], :b => [:b1, :b2, :b3], :c => [@c1, @c2, @c3], :d => ['d']}
106
- end
107
-
108
- it "should return a hash equivalent to the output of Hash#merge for two, non-equivalent hashes, with no block given and values don't respond to #|." do
109
- hash2 = {:b => :b3, :c => @cc2, :d => 'd'}
110
- @hash.union(hash2).should == {:a => ['a1'], :b => :b3, :c => [@c1, @c2, @c3], :d => 'd'}
111
- end
112
-
113
- it "should return the combined hash values as specified by a given block." do
114
- hash2 = {:b => :b3, :c => @cc2, :d => 'd'}
115
- @hash.union(hash2){|value1, value2| Set.new(make_array(value1)).union(Set.new(make_array(value2)))}.should == {:a => Set.new(['a1']), :b => Set.new([:b1, :b2, :b3]), :c => Set.new([@c1, @c2, @c3]), :d => Set.new(['d'])}
116
- end
117
- end
48
+ it "should return the common subset hash for two, if the values respond to #&." do
49
+ hash2 = {:b => [:b1], :c => @cc2, :d => ['d']}
50
+ @hash.and(hash2).should == {:b => [:b1], :c => [@c2]}
51
+ end
118
52
 
119
- describe Hash, "#union" do
120
- it_should_behave_like "union of hashes"
121
- end
53
+ it "should return the common subset of hash values for partially-overlapping keys as specified by a given block." do
54
+ hash2 = {:b =>:b1, :c => @cc2, :d => 'd'}
55
+ @hash.and(hash2){|value1, value2| Set.new(make_array(value1)).intersection(Set.new(make_array(value2)))}.should == {:b => Set.new([:b1]), :c => Set.new([@c2])}
56
+ end
57
+ end
122
58
 
123
- describe Hash, "#or" do
124
- it_should_behave_like "union of hashes"
125
- end
59
+ describe Hash, "#intersection" do
60
+ it_should_behave_like "intersection of hashes"
61
+ end
126
62
 
127
- describe Hash, "#|" do
128
- it_should_behave_like "union of hashes"
129
-
130
- it "should support operator-style semantics" do
131
- ({:a => ['a1'], :d => ['d']} | {:a => ['a2'], :b => [:b1, :b2], :c => @cc2}).should == {:a => ['a1', 'a2'], :b => [:b1, :b2], :c => @cc2, :d => ['d']}
63
+ describe Hash, "#and" do
64
+ it_should_behave_like "intersection of hashes"
132
65
  end
133
- end
134
66
 
67
+ describe Hash, "#&" do
68
+ it_should_behave_like "intersection of hashes"
69
+
70
+ it "should support operator-style semantics" do
71
+ ({:a => ['a1', 'a2'], :c => @cc1} & {:a => ['a1'], :b => [:b1, :b2], :c => @cc2}).should == {:a => ['a1'], :c => [@c2]}
72
+ end
73
+ end
135
74
 
136
- describe "subtraction of hashes", :shared => true do
137
- include Aquarium::Utils::ArrayUtils
138
- include Aquarium::Utils::HashUtils
139
-
140
- before(:each) do
141
- before_hash_spec
142
- # override value:
143
- @hash = {:a => ['a1', 'a2'], :b => [:b1, :b2], :c => @cc1}
75
+ shared_examples_for "union of hashes" do
76
+ include Aquarium::Utils::ArrayUtils
77
+ include Aquarium::Utils::HashUtils
78
+
79
+ it "should return the same hash if unioned with itself." do
80
+ @hash.union(@hash).should == @hash
81
+ end
82
+
83
+ it "should return the same hash if unioned with an equivalent hash." do
84
+ @hash.union({:a => ['a1'], :b => [:b1, :b2], :c => @cc1}).should == @hash
85
+ end
86
+
87
+ it "should return a hash that is equivalent to the non-empty hash if the other hash is empty." do
88
+ {}.union(@hash).should == @hash
89
+ @hash.union({}).should == @hash
90
+ end
91
+
92
+ it "should return the same hash if unioned with nil." do
93
+ @hash.union(nil).should == @hash
94
+ end
95
+
96
+ it "should return the combined hash value, if the values respond to #|." do
97
+ hash2 = {:b => [:b3], :c => @cc2, :d => ['d']}
98
+ @hash.union(hash2).should == {:a => ['a1'], :b => [:b1, :b2, :b3], :c => [@c1, @c2, @c3], :d => ['d']}
99
+ end
100
+
101
+ it "should return a hash equivalent to the output of Hash#merge for two, non-equivalent hashes, with no block given and values don't respond to #|." do
102
+ hash2 = {:b => :b3, :c => @cc2, :d => 'd'}
103
+ @hash.union(hash2).should == {:a => ['a1'], :b => :b3, :c => [@c1, @c2, @c3], :d => 'd'}
104
+ end
105
+
106
+ it "should return the combined hash values as specified by a given block." do
107
+ hash2 = {:b => :b3, :c => @cc2, :d => 'd'}
108
+ @hash.union(hash2){|value1, value2| Set.new(make_array(value1)).union(Set.new(make_array(value2)))}.should == {:a => Set.new(['a1']), :b => Set.new([:b1, :b2, :b3]), :c => Set.new([@c1, @c2, @c3]), :d => Set.new(['d'])}
109
+ end
144
110
  end
145
-
146
- it "should return an empty hash if subtracted from itself." do
147
- (@hash - @hash).should be_empty
148
- end
149
111
 
150
- it "should return an empty hash if an equivalent hash is subtracted from it." do
151
- (@hash - {:a => ['a1', 'a2'], :b => [:b1, :b2], :c => @cc1}).should be_empty
152
- end
112
+ describe Hash, "#union" do
113
+ it_should_behave_like "union of hashes"
114
+ end
153
115
 
154
- it "should return an equivalent hash if the second hash is empty." do
155
- (@hash - {}).should == @hash
156
- end
116
+ describe Hash, "#or" do
117
+ it_should_behave_like "union of hashes"
118
+ end
157
119
 
158
- it "should return an equivalent hash if the second hash is nil." do
159
- (@hash - nil).should == @hash
160
- end
161
-
162
- it "should return an empty hash if the first hash is empty, independent of the second hash." do
163
- ({} - @hash).should be_empty
164
- end
165
-
166
- it "should return a hash with all keys in the second hash removed, independent of the corresponding values, if no block is given." do
167
- hash2 = {:b =>:b3, :c => 'c', :d => 'd'}
168
- (@hash - hash2).should == {:a => ['a1', 'a2']}
169
- end
170
-
171
- it "should return a hash with the values subtraced for partially-overlapping keys as specified by a given block." do
172
- hash2 = {:b =>:b2, :c => @cc2, :d => 'd'}
173
- @hash.minus(hash2) do |value1, value2|
174
- Set.new(make_array(value1)) - Set.new(make_array(value2))
175
- end.should == {:a => Set.new(['a1', 'a2']), :b => Set.new([:b1]), :c => Set.new([@c1])}
176
- end
177
-
178
- it "should be not associative." do
179
- hash1 = {:a => :a1, :b =>:b1, :c => :c1, :d => :d1}
180
- hash2 = {:b =>:b3, :c => 'c'}
181
- hash3 = {:a =>:a4, :c => 'c'}
182
- ((hash1 - hash2) - hash3).should == {:d => :d1}
183
- (hash1 - (hash2 - hash3)).should == {:a => :a1, :c => :c1, :d => :d1}
184
- end
185
- end
120
+ describe Hash, "#|" do
121
+ it_should_behave_like "union of hashes"
122
+
123
+ it "should support operator-style semantics" do
124
+ ({:a => ['a1'], :d => ['d']} | {:a => ['a2'], :b => [:b1, :b2], :c => @cc2}).should == {:a => ['a1', 'a2'], :b => [:b1, :b2], :c => @cc2, :d => ['d']}
125
+ end
126
+ end
186
127
 
187
- describe Hash, "#minus" do
188
- it_should_behave_like "subtraction of hashes"
189
- end
190
128
 
191
- describe Hash, "#-" do
192
- it_should_behave_like "subtraction of hashes"
129
+ shared_examples_for "subtraction of hashes" do
130
+ include Aquarium::Utils::ArrayUtils
131
+ include Aquarium::Utils::HashUtils
132
+
133
+ before(:each) do
134
+ # override value:
135
+ @hash = {:a => ['a1', 'a2'], :b => [:b1, :b2], :c => @cc1}
136
+ end
137
+
138
+ it "should return an empty hash if subtracted from itself." do
139
+ (@hash - @hash).should be_empty
140
+ end
193
141
 
194
- it "should support operator-style semantics" do
195
- hash1 = {:a => :a1, :b =>:b1, :c => :c1, :d => :d1}
196
- hash2 = {:b =>:b3, :c => 'c'}
197
- hash3 = {:a =>:a4, :c => 'c'}
198
- ((hash1 - hash2) - hash3).should == {:d => :d1}
199
- (hash1 - (hash2 - hash3)).should == {:a => :a1, :c => :c1, :d => :d1}
200
- end
201
- end
142
+ it "should return an empty hash if an equivalent hash is subtracted from it." do
143
+ (@hash - {:a => ['a1', 'a2'], :b => [:b1, :b2], :c => @cc1}).should be_empty
144
+ end
202
145
 
203
- describe Hash, "#eql_when_keys_compared?" do
204
- include Aquarium::Utils::ArrayUtils
205
- include Aquarium::Utils::HashUtils
146
+ it "should return an equivalent hash if the second hash is empty." do
147
+ (@hash - {}).should == @hash
148
+ end
206
149
 
207
- it "should return true when comparing a hash to itself." do
208
- h1={"1" => :a1, "2" => :a2, "3" => :a3}
209
- h1.eql_when_keys_compared?(h1).should == true
150
+ it "should return an equivalent hash if the second hash is nil." do
151
+ (@hash - nil).should == @hash
152
+ end
153
+
154
+ it "should return an empty hash if the first hash is empty, independent of the second hash." do
155
+ ({} - @hash).should be_empty
156
+ end
157
+
158
+ it "should return a hash with all keys in the second hash removed, independent of the corresponding values, if no block is given." do
159
+ hash2 = {:b =>:b3, :c => 'c', :d => 'd'}
160
+ (@hash - hash2).should == {:a => ['a1', 'a2']}
161
+ end
162
+
163
+ it "should return a hash with the values subtraced for partially-overlapping keys as specified by a given block." do
164
+ hash2 = {:b =>:b2, :c => @cc2, :d => 'd'}
165
+ @hash.minus(hash2) do |value1, value2|
166
+ Set.new(make_array(value1)) - Set.new(make_array(value2))
167
+ end.should == {:a => Set.new(['a1', 'a2']), :b => Set.new([:b1]), :c => Set.new([@c1])}
168
+ end
169
+
170
+ it "should be not associative." do
171
+ hash1 = {:a => :a1, :b =>:b1, :c => :c1, :d => :d1}
172
+ hash2 = {:b =>:b3, :c => 'c'}
173
+ hash3 = {:a =>:a4, :c => 'c'}
174
+ ((hash1 - hash2) - hash3).should == {:d => :d1}
175
+ (hash1 - (hash2 - hash3)).should == {:a => :a1, :c => :c1, :d => :d1}
176
+ end
210
177
  end
211
178
 
212
- it "should return true for hashes with string keys that satisfy String#==." do
213
- h1={"1" => :a1, "2" => :a2, "3" => :a3}
214
- h2={"1" => :a1, "2" => :a2, "3" => :a3}
215
- h1.eql_when_keys_compared?(h2).should == true
179
+ describe Hash, "#minus" do
180
+ it_should_behave_like "subtraction of hashes"
216
181
  end
217
182
 
218
- it "should return false for hashes with matching keys, but different values." do
219
- h1={"1" => :a1, "2" => :a2, "3" => /a/}
220
- h2={"1" => :a1, "2" => :a2, "3" => /b/}
221
- h1.eql_when_keys_compared?(h2).should == false
222
- end
183
+ describe Hash, "#-" do
184
+ it_should_behave_like "subtraction of hashes"
223
185
 
224
- it "should return false for hashes where one hash is a subset of the other." do
225
- h1={"1" => :a1, "2" => :a2}
226
- h2={"1" => :a1, "2" => :a2, "3" => :a3}
227
- h1.eql_when_keys_compared?(h2).should == false
228
- h2.eql_when_keys_compared?(h1).should == false
186
+ it "should support operator-style semantics" do
187
+ hash1 = {:a => :a1, :b =>:b1, :c => :c1, :d => :d1}
188
+ hash2 = {:b =>:b3, :c => 'c'}
189
+ hash3 = {:a =>:a4, :c => 'c'}
190
+ ((hash1 - hash2) - hash3).should == {:d => :d1}
191
+ (hash1 - (hash2 - hash3)).should == {:a => :a1, :c => :c1, :d => :d1}
192
+ end
229
193
  end
230
-
231
- it "should return true for hashes with Object keys that define a #<=> method, while Hash#eql? would return false." do
232
- class Key
233
- def initialize key
234
- @key = key
235
- end
236
- attr_reader :key
237
- def eql? other
238
- key.eql? other.key
239
- end
240
- def <=> other
241
- key <=> other.key
242
- end
194
+
195
+ describe Hash, "#eql_when_keys_compared?" do
196
+ include Aquarium::Utils::ArrayUtils
197
+ include Aquarium::Utils::HashUtils
198
+
199
+ it "should return true when comparing a hash to itself." do
200
+ h1={"1" => :a1, "2" => :a2, "3" => :a3}
201
+ h1.eql_when_keys_compared?(h1).should == true
202
+ end
203
+
204
+ it "should return true for hashes with string keys that satisfy String#==." do
205
+ h1={"1" => :a1, "2" => :a2, "3" => :a3}
206
+ h2={"1" => :a1, "2" => :a2, "3" => :a3}
207
+ h1.eql_when_keys_compared?(h2).should == true
208
+ end
209
+
210
+ it "should return false for hashes with matching keys, but different values." do
211
+ h1={"1" => :a1, "2" => :a2, "3" => /a/}
212
+ h2={"1" => :a1, "2" => :a2, "3" => /b/}
213
+ h1.eql_when_keys_compared?(h2).should == false
214
+ end
215
+
216
+ it "should return false for hashes where one hash is a subset of the other." do
217
+ h1={"1" => :a1, "2" => :a2}
218
+ h2={"1" => :a1, "2" => :a2, "3" => :a3}
219
+ h1.eql_when_keys_compared?(h2).should == false
220
+ h2.eql_when_keys_compared?(h1).should == false
243
221
  end
244
222
 
245
- h1 = {}; h2 = {}
246
- (0...4).each do |index|
247
- h1[Key.new(index)] = {index.to_s => [index, index+1]}
248
- h2[Key.new(index)] = {index.to_s => [index, index+1]}
223
+ it "should return true for hashes with Object keys that define a #<=> method, while Hash#eql? would return false." do
224
+ class Key
225
+ def initialize key
226
+ @key = key
227
+ end
228
+ attr_reader :key
229
+ def eql? other
230
+ key.eql? other.key
231
+ end
232
+ def <=> other
233
+ key <=> other.key
234
+ end
235
+ end
236
+
237
+ h1 = {}; h2 = {}
238
+ (0...4).each do |index|
239
+ h1[Key.new(index)] = {index.to_s => [index, index+1]}
240
+ h2[Key.new(index)] = {index.to_s => [index, index+1]}
241
+ end
242
+ h1.eql_when_keys_compared?(h2).should == true
243
+ h1.eql?(h2).should == false
249
244
  end
250
- h1.eql_when_keys_compared?(h2).should == true
251
- h1.eql?(h2).should == false
252
245
  end
253
- end
254
246
 
255
- describe Hash, "#equivalent_key" do
256
- it "should return the key in the hash for which input_value#==(key) is true." do
257
- class Key
258
- def initialize key
259
- @key = key
247
+ describe Hash, "#equivalent_key" do
248
+ it "should return the key in the hash for which input_value#==(key) is true." do
249
+ class Key
250
+ def initialize key
251
+ @key = key
252
+ end
253
+ attr_reader :key
254
+ def eql? other
255
+ key.eql? other.key
256
+ end
257
+ alias :== :eql?
260
258
  end
261
- attr_reader :key
262
- def eql? other
263
- key.eql? other.key
259
+
260
+ h1 = {}; h2 = {}
261
+ (0...4).each do |index|
262
+ h1[Key.new(index)] = {index.to_s => [index, index+1]}
263
+ h2[Key.new(index)] = {index.to_s => [index, index+1]}
264
264
  end
265
- alias :== :eql?
265
+ h1[Key.new(0)].should be_nil
266
+ h1.equivalent_key(Key.new(0)).should_not be_nil
267
+ h1.equivalent_key(Key.new(5)).should be_nil
266
268
  end
267
-
268
- h1 = {}; h2 = {}
269
- (0...4).each do |index|
270
- h1[Key.new(index)] = {index.to_s => [index, index+1]}
271
- h2[Key.new(index)] = {index.to_s => [index, index+1]}
272
- end
273
- h1[Key.new(0)].should be_nil
274
- h1.equivalent_key(Key.new(0)).should_not be_nil
275
- h1.equivalent_key(Key.new(5)).should be_nil
276
269
  end
277
-
278
270
  end