소스 검색

More Ruby-eqsue interface

Marcin Wyszynski 9 년 전
부모
커밋
3bdaaa5dda
2개의 변경된 파일54개의 추가작업 그리고 0개의 파일을 삭제
  1. 44 0
      ruby/ext/google/protobuf_c/message.c
  2. 10 0
      ruby/tests/basic.rb

+ 44 - 0
ruby/ext/google/protobuf_c/message.c

@@ -178,6 +178,45 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
   }
 }
 
+VALUE Message_respond_to_missing(int argc, VALUE* argv, VALUE _self) {
+  MessageHeader* self;
+  VALUE method_name, method_str;
+  char* name;
+  size_t name_len;
+  bool setter;
+  const upb_oneofdef* o;
+  const upb_fielddef* f;
+
+  TypedData_Get_Struct(_self, MessageHeader, &Message_type, self);
+  if (argc < 1) {
+    rb_raise(rb_eArgError, "Expected method name as first argument.");
+  }
+  method_name = argv[0];
+  if (!SYMBOL_P(method_name)) {
+    rb_raise(rb_eArgError, "Expected symbol as method name.");
+  }
+  method_str = rb_id2str(SYM2ID(method_name));
+  name = RSTRING_PTR(method_str);
+  name_len = RSTRING_LEN(method_str);
+  setter = false;
+
+  // Setters have names that end in '='.
+  if (name[name_len - 1] == '=') {
+    setter = true;
+    name_len--;
+  }
+
+  // See if this name corresponds to either a oneof or field in this message.
+  if (!upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len, &f,
+                             &o)) {
+    return rb_call_super(argc, argv);
+  }
+  if (o != NULL) {
+    return setter ? Qfalse : Qtrue;
+  }
+  return Qtrue;
+}
+
 int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) {
   MessageHeader* self;
   VALUE method_str;
@@ -303,6 +342,9 @@ VALUE Message_deep_copy(VALUE _self) {
  * field is of a primitive type).
  */
 VALUE Message_eq(VALUE _self, VALUE _other) {
+  if (TYPE(_self) != TYPE(_other)) {
+    return Qfalse;
+  }
   MessageHeader* self;
   MessageHeader* other;
   TypedData_Get_Struct(_self, MessageHeader, &Message_type, self);
@@ -459,6 +501,8 @@ VALUE build_class_from_descriptor(Descriptor* desc) {
 
   rb_define_method(klass, "method_missing",
                    Message_method_missing, -1);
+  rb_define_method(klass, "respond_to_missing?",
+                   Message_respond_to_missing, -1);
   rb_define_method(klass, "initialize", Message_initialize, -1);
   rb_define_method(klass, "dup", Message_dup, 0);
   // Also define #clone so that we don't inherit Object#clone.

+ 10 - 0
ruby/tests/basic.rb

@@ -1181,5 +1181,15 @@ module BasicTest
       m2 = MapMessage.decode_json(MapMessage.encode_json(m))
       assert m == m2
     end
+
+    def test_comparison_with_arbitrary_object
+      assert_false MapMessage.new == nil
+    end
+
+    def test_respond_to
+      msg = MapMessage.new
+      assert msg.respond_to?(:map_string_int32)
+      assert_false msg.respond_to?(:map_string_int32)
+    end
   end
 end