Skip to content

Commit 9f86e0e

Browse files
committedJun 25, 2018
Add a SetLike module that contains the duck-typed comparisons.
This fixes #5227 for the methods mentioned there.
1 parent 459b812 commit 9f86e0e

File tree

2 files changed

+130
-14
lines changed

2 files changed

+130
-14
lines changed
 

Diff for: ‎core/src/main/java/org/jruby/ext/set/RubySet.java

+23-14
Original file line numberDiff line numberDiff line change
@@ -424,13 +424,8 @@ private void flattenMerge(final ThreadContext context, final IRubyObject set, fi
424424
}
425425
}
426426
else {
427-
set.callMethod(context, "each", IRubyObject.NULL_ARRAY, new Block(
428-
new EachBody(context.runtime) {
429-
IRubyObject yieldImpl(ThreadContext context, IRubyObject e) {
430-
addFlattened(context, seen, e); return context.nil;
431-
}
432-
})
433-
);
427+
// call on superclasses to get SetLike impl
428+
getType().getSuperClass().finvoke(context, this, "flatten_merge", set);
434429
}
435430
}
436431

@@ -495,7 +490,9 @@ public IRubyObject superset_p(final ThreadContext context, IRubyObject set) {
495490
size() >= ((RubySet) set).size() && allElementsIncluded((RubySet) set)
496491
);
497492
}
498-
throw context.runtime.newArgumentError("value must be a set");
493+
494+
// call on superclasses to get SetLike impl
495+
return getType().getSuperClass().finvoke(context, this, "superset?", set);
499496
}
500497

501498
// Returns true if the set is a proper superset of the given set.
@@ -510,7 +507,9 @@ public IRubyObject proper_superset_p(final ThreadContext context, IRubyObject se
510507
size() > ((RubySet) set).size() && allElementsIncluded((RubySet) set)
511508
);
512509
}
513-
throw context.runtime.newArgumentError("value must be a set");
510+
511+
// call on superclasses to get SetLike impl
512+
return getType().getSuperClass().finvoke(context, this, "proper_superset?", set);
514513
}
515514

516515
@JRubyMethod(name = "subset?", alias = { "<=" })
@@ -524,7 +523,9 @@ public IRubyObject subset_p(final ThreadContext context, IRubyObject set) {
524523
size() <= ((RubySet) set).size() && allElementsIncluded((RubySet) set)
525524
);
526525
}
527-
throw context.runtime.newArgumentError("value must be a set");
526+
527+
// call on superclasses to get SetLike impl
528+
return getType().getSuperClass().finvoke(context, this, "subset?", set);
528529
}
529530

530531
@JRubyMethod(name = "proper_subset?", alias = { "<" })
@@ -538,7 +539,9 @@ public IRubyObject proper_subset_p(final ThreadContext context, IRubyObject set)
538539
size() < ((RubySet) set).size() && allElementsIncluded((RubySet) set)
539540
);
540541
}
541-
throw context.runtime.newArgumentError("value must be a set");
542+
543+
// call on superclasses to get SetLike impl
544+
return getType().getSuperClass().finvoke(context, this, "proper_subset?", set);
542545
}
543546

544547
/**
@@ -549,7 +552,9 @@ public IRubyObject intersect_p(final ThreadContext context, IRubyObject set) {
549552
if ( set instanceof RubySet ) {
550553
return context.runtime.newBoolean( intersect((RubySet) set) );
551554
}
552-
throw context.runtime.newArgumentError("value must be a set");
555+
556+
// call on superclasses to get SetLike impl
557+
return getType().getSuperClass().finvoke(context, this, "intersect?", set);
553558
}
554559

555560
public boolean intersect(final RubySet set) {
@@ -577,7 +582,9 @@ public IRubyObject disjoint_p(final ThreadContext context, IRubyObject set) {
577582
if ( set instanceof RubySet ) {
578583
return context.runtime.newBoolean( ! intersect((RubySet) set) );
579584
}
580-
throw context.runtime.newArgumentError("value must be a set");
585+
586+
// call on superclasses to get SetLike impl
587+
return context.runtime.newBoolean(!getType().getSuperClass().finvoke(context, this, "intersect?", set).isTrue());
581588
}
582589

583590
@JRubyMethod
@@ -873,7 +880,9 @@ public IRubyObject op_equal(ThreadContext context, IRubyObject other) {
873880
return context.tru;
874881
}
875882
}
876-
return context.fals;
883+
884+
// call on superclasses to get SetLike impl
885+
return getType().getSuperClass().finvoke(context, this, "==", other);
877886
}
878887

879888
@JRubyMethod(name = "reset")

Diff for: ‎core/src/main/ruby/jruby/set.rb

+107
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,114 @@
22
#
33
# .rb part for JRuby's native Set impl (taken from set.rb)
44

5+
module SetLike
Has a conversation. Original line has a conversation.
6+
def flatten_merge(set, seen = Set.new) # :nodoc:
7+
set.each { |e|
8+
if e.is_a?(Set)
9+
if seen.include?(e_id = e.object_id)
10+
raise ArgumentError, "tried to flatten recursive Set"
11+
end
12+
13+
seen.add(e_id)
14+
flatten_merge(e, seen)
15+
seen.delete(e_id)
16+
else
17+
add(e)
18+
end
19+
}
20+
21+
self
22+
end
23+
protected :flatten_merge
24+
25+
# Returns true if the set is a superset of the given set.
26+
def superset?(set)
27+
case
28+
when set.instance_of?(self.class) && @hash.respond_to?(:>=)
29+
@hash >= set.instance_variable_get(:@hash)
30+
when set.is_a?(Set)
31+
size >= set.size && set.all? { |o| include?(o) }
32+
else
33+
raise ArgumentError, "value must be a set"
34+
end
35+
end
36+
alias >= superset?
37+
38+
# Returns true if the set is a proper superset of the given set.
39+
def proper_superset?(set)
40+
case
41+
when set.instance_of?(self.class) && @hash.respond_to?(:>)
42+
@hash > set.instance_variable_get(:@hash)
43+
when set.is_a?(Set)
44+
size > set.size && set.all? { |o| include?(o) }
45+
else
46+
raise ArgumentError, "value must be a set"
47+
end
48+
end
49+
alias > proper_superset?
50+
51+
# Returns true if the set is a subset of the given set.
52+
def subset?(set)
53+
case
54+
when set.instance_of?(self.class) && @hash.respond_to?(:<=)
55+
@hash <= set.instance_variable_get(:@hash)
56+
when set.is_a?(Set)
57+
size <= set.size && all? { |o| set.include?(o) }
58+
else
59+
raise ArgumentError, "value must be a set"
60+
end
61+
end
62+
alias <= subset?
63+
64+
# Returns true if the set is a proper subset of the given set.
65+
def proper_subset?(set)
66+
case
67+
when set.instance_of?(self.class) && @hash.respond_to?(:<)
68+
@hash < set.instance_variable_get(:@hash)
69+
when set.is_a?(Set)
70+
size < set.size && all? { |o| set.include?(o) }
71+
else
72+
raise ArgumentError, "value must be a set"
73+
end
74+
end
75+
alias < proper_subset?
76+
77+
# Returns true if the set and the given set have at least one
78+
# element in common.
79+
#
80+
# Set[1, 2, 3].intersect? Set[4, 5] #=> false
81+
# Set[1, 2, 3].intersect? Set[3, 4] #=> true
82+
def intersect?(set)
83+
set.is_a?(Set) or raise ArgumentError, "value must be a set"
84+
if size < set.size
85+
any? { |o| set.include?(o) }
86+
else
87+
set.any? { |o| include?(o) }
88+
end
89+
end
90+
91+
# Returns true if two sets are equal. The equality of each couple
92+
# of elements is defined according to Object#eql?.
93+
#
94+
# Set[1, 2] == Set[2, 1] #=> true
95+
# Set[1, 3, 5] == Set[1, 5] #=> false
96+
# Set['a', 'b', 'c'] == Set['a', 'c', 'b'] #=> true
97+
# Set['a', 'b', 'c'] == ['a', 'c', 'b'] #=> false
98+
def ==(other)
99+
if self.equal?(other)
100+
true
101+
elsif other.instance_of?(self.class)
102+
@hash == other.instance_variable_get(:@hash)
103+
elsif other.is_a?(Set) && self.size == other.size
104+
other.all? { |o| @hash.include?(o) }
105+
else
106+
false
107+
end
108+
end
109+
end
110+
5111
class Set
112+
include SetLike
6113

7114
def pretty_print(pp) # :nodoc:
8115
pp.text sprintf('#<%s: {', self.class.name)

0 commit comments

Comments
 (0)
Please sign in to comment.