aquarium 0.4.4 → 0.5.1

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.
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