THRIFT-553. rb: thrift structs should be comparable (<=>)
This patch adds the spaceship operator to the struct and union base classes, enabling object comparisons between objects without regenerating code.
git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@911644 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/rb/Rakefile b/lib/rb/Rakefile
index bd38561..10383e7 100644
--- a/lib/rb/Rakefile
+++ b/lib/rb/Rakefile
@@ -82,7 +82,7 @@
p.summary = "Ruby libraries for Thrift (a language-agnostic RPC system)"
p.url = "http://incubator.apache.org/thrift/"
p.include_rakefile = true
- p.version = "0.2.4"
+ p.version = "0.2.5"
p.rubygems_version = ">= 1.2.0"
end
diff --git a/lib/rb/lib/thrift/struct.rb b/lib/rb/lib/thrift/struct.rb
index 48353d3..9ee6912 100644
--- a/lib/rb/lib/thrift/struct.rb
+++ b/lib/rb/lib/thrift/struct.rb
@@ -167,6 +167,30 @@
end
end
+ def <=>(other)
+ if self.class == other.class
+ each_field do |fid, field_info|
+ v1 = self.send(field_info[:name])
+ v1_set = !v1.nil?
+ v2 = other.send(field_info[:name])
+ v2_set = !v2.nil?
+ if v1_set && !v2_set
+ return -1
+ elsif !v1_set && v2_set
+ return 1
+ elsif v1_set && v2_set
+ cmp = v1 <=> v2
+ if cmp != 0
+ return cmp
+ end
+ end
+ end
+ 0
+ else
+ self.class <=> other.class
+ end
+ end
+
protected
def self.append_features(mod)
diff --git a/lib/rb/lib/thrift/struct_union.rb b/lib/rb/lib/thrift/struct_union.rb
index ddd7938..9bfbed5 100644
--- a/lib/rb/lib/thrift/struct_union.rb
+++ b/lib/rb/lib/thrift/struct_union.rb
@@ -155,6 +155,5 @@
end
"[" + buf.join(", ") + "]"
end
-
end
end
\ No newline at end of file
diff --git a/lib/rb/lib/thrift/union.rb b/lib/rb/lib/thrift/union.rb
index 9fde8fc..a7058f2 100644
--- a/lib/rb/lib/thrift/union.rb
+++ b/lib/rb/lib/thrift/union.rb
@@ -46,7 +46,11 @@
end
def inspect
- "<#{self.class} #{@setfield}: #{inspect_field(@value, struct_fields[name_to_id(@setfield.to_s)])}>"
+ if get_set_field
+ "<#{self.class} #{@setfield}: #{inspect_field(@value, struct_fields[name_to_id(@setfield.to_s)])}>"
+ else
+ "<#{self.class} >"
+ end
end
def read(iprot)
@@ -135,6 +139,30 @@
@value
end
+ def <=>(other)
+ if self.class == other.class
+ if get_set_field == other.get_set_field
+ if get_set_field.nil?
+ 0
+ else
+ get_value <=> other.get_value
+ end
+ else
+ if get_set_field && other.get_set_field.nil?
+ -1
+ elsif get_set_field.nil? && other.get_set_field
+ 1
+ elsif get_set_field.nil? && other.get_set_field.nil?
+ 0
+ else
+ name_to_id(get_set_field.to_s) <=> name_to_id(other.get_set_field.to_s)
+ end
+ end
+ else
+ self.class <=> other.class
+ end
+ end
+
protected
def handle_message(iprot, fid, ftype)
diff --git a/lib/rb/spec/struct_spec.rb b/lib/rb/spec/struct_spec.rb
index c1271b9..c937023 100644
--- a/lib/rb/spec/struct_spec.rb
+++ b/lib/rb/spec/struct_spec.rb
@@ -79,6 +79,16 @@
Foo.new(:my_bool => true).my_bool?.should be_true
end
+ it "should be comparable" do
+ s1 = StructWithSomeEnum.new(:some_enum => SomeEnum::ONE)
+ s2 = StructWithSomeEnum.new(:some_enum => SomeEnum::TWO)
+
+ (s1 <=> s2).should == -1
+ (s2 <=> s1).should == 1
+ (s1 <=> s1).should == 0
+ (s1 <=> StructWithSomeEnum.new()).should == -1
+ end
+
it "should read itself off the wire" do
struct = Foo.new
prot = BaseProtocol.new(mock("transport"))
diff --git a/lib/rb/spec/union_spec.rb b/lib/rb/spec/union_spec.rb
index 33df251..702e920 100644
--- a/lib/rb/spec/union_spec.rb
+++ b/lib/rb/spec/union_spec.rb
@@ -141,28 +141,53 @@
swu2.read(proto)
swu2.should == swu
end
-
+
it "should support old style constructor" do
union = My_union.new(:integer32 => 26)
union.get_set_field.should == :integer32
union.get_value.should == 26
end
-
+
+ it "should not throw an error when inspected and unset" do
+ lambda{TestUnion.new().inspect}.should_not raise_error
+ end
+
it "should print enum value name when inspected" do
My_union.new(:some_enum => SomeEnum::ONE).inspect.should == "<SpecNamespace::My_union some_enum: ONE (0)>"
-
+
My_union.new(:my_map => {SomeEnum::ONE => [SomeEnum::TWO]}).inspect.should == "<SpecNamespace::My_union my_map: {ONE (0): [TWO (1)]}>"
end
-
+
it "should offer field? methods" do
My_union.new.some_enum?.should be_false
My_union.new(:some_enum => SomeEnum::ONE).some_enum?.should be_true
My_union.new(:im_true => false).im_true?.should be_true
My_union.new(:im_true => true).im_true?.should be_true
end
-
+
it "should pretty print binary fields" do
TestUnion.new(:binary_field => "\001\002\003").inspect.should == "<SpecNamespace::TestUnion binary_field: 010203>"
end
+
+ it "should be comparable" do
+ relationships = [
+ [0, -1, -1, -1],
+ [1, 0, -1, -1],
+ [1, 1, 0, -1],
+ [1, 1, 1, 0]]
+
+ objs = [
+ TestUnion.new(:string_field, "blah"),
+ TestUnion.new(:string_field, "blahblah"),
+ TestUnion.new(:i32_field, 1),
+ TestUnion.new()]
+
+ for y in 0..3
+ for x in 0..3
+ # puts "#{objs[y].inspect} <=> #{objs[x].inspect} should == #{relationships[y][x]}"
+ (objs[y] <=> objs[x]).should == relationships[y][x]
+ end
+ end
+ end
end
end