Adds support for trailing metadata

- removes the status class, replacing it with a Struct
- adds support for trailing metadata, merging into the call's initial metadata

- tracks []
	Change on 2014/12/15 by temiola <temiola@google.com>
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=82193372
diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c
index 3be7c3b..872f8e3 100644
--- a/src/ruby/ext/grpc/rb_call.c
+++ b/src/ruby/ext/grpc/rb_call.c
@@ -38,8 +38,8 @@
 #include <grpc/grpc.h>
 #include "rb_byte_buffer.h"
 #include "rb_completion_queue.h"
+#include "rb_event.h"
 #include "rb_metadata.h"
-#include "rb_status.h"
 #include "rb_grpc.h"
 
 /* id_cq is the name of the hidden ivar that preserves a reference to a
@@ -270,8 +270,8 @@
 
     Saves a status object on the call.  */
 static VALUE grpc_rb_call_set_status(VALUE self, VALUE status) {
-  if (!NIL_P(status) && rb_obj_class(status) != rb_cStatus) {
-    rb_raise(rb_eTypeError, "bad status: got:<%s> want: <Status>",
+  if (!NIL_P(status) && rb_obj_class(status) != rb_sStatus) {
+    rb_raise(rb_eTypeError, "bad status: got:<%s> want: <Struct::Status>",
              rb_obj_classname(status));
     return Qnil;
   }
@@ -344,6 +344,11 @@
 }
 
 /* Queue a status for writing.
+
+   call-seq:
+      tag = Object.new
+      call.write_status(200, "OK", tag)
+
    REQUIRES: No other writes are pending on the call. It is only safe to
    start the next write after the corresponding write_accepted event
    is received.
@@ -352,13 +357,13 @@
    Only callable on the server.
    Produces a GRPC_FINISHED event when the status is sent and the stream is
    fully closed */
-static VALUE grpc_rb_call_start_write_status(VALUE self, VALUE status,
-                                             VALUE tag) {
+static VALUE grpc_rb_call_start_write_status(VALUE self, VALUE code,
+                                             VALUE status, VALUE tag) {
   grpc_call *call = NULL;
-  grpc_status *sts = grpc_rb_get_wrapped_status(status);
   grpc_call_error err;
   Data_Get_Struct(self, grpc_call, call);
-  err = grpc_call_start_write_status(call, *sts, ROBJECT(tag));
+  err = grpc_call_start_write_status(call, NUM2UINT(code),
+                                     StringValueCStr(status), ROBJECT(tag));
   if (err != GRPC_CALL_OK) {
     rb_raise(rb_eCallError, "start write status: %s (code=%d)",
              grpc_call_error_detail_of(err), err);
@@ -522,7 +527,7 @@
   rb_define_method(rb_cCall, "start_read", grpc_rb_call_start_read, 1);
   rb_define_method(rb_cCall, "start_write", grpc_rb_call_start_write, -1);
   rb_define_method(rb_cCall, "start_write_status",
-                   grpc_rb_call_start_write_status, 2);
+                   grpc_rb_call_start_write_status, 3);
   rb_define_method(rb_cCall, "writes_done", grpc_rb_call_writes_done, 1);
   rb_define_method(rb_cCall, "status", grpc_rb_call_get_status, 0);
   rb_define_method(rb_cCall, "status=", grpc_rb_call_set_status, 1);
diff --git a/src/ruby/ext/grpc/rb_event.c b/src/ruby/ext/grpc/rb_event.c
index 93f36f8..76ea6ad 100644
--- a/src/ruby/ext/grpc/rb_event.c
+++ b/src/ruby/ext/grpc/rb_event.c
@@ -40,7 +40,6 @@
 #include "rb_byte_buffer.h"
 #include "rb_call.h"
 #include "rb_metadata.h"
-#include "rb_status.h"
 
 /* rb_mCompletionType is a ruby module that holds the completion type values */
 VALUE rb_mCompletionType = Qnil;
@@ -132,6 +131,11 @@
       metadata = event->data.client_metadata_read.elements;
       break;
 
+    case GRPC_FINISHED:
+      count = event->data.finished.metadata_count;
+      metadata = event->data.finished.metadata_elements;
+      break;
+
     case GRPC_SERVER_RPC_NEW:
       count = event->data.server_rpc_new.metadata_count;
       metadata = event->data.server_rpc_new.metadata_elements;
@@ -139,8 +143,9 @@
 
     default:
       rb_raise(rb_eRuntimeError,
-               "bug: bad event type reading server metadata. got %d; want %d",
-               event->type, GRPC_SERVER_RPC_NEW);
+               "bug: bad event type metadata. got %d; want %d|%d:%d",
+               event->type, GRPC_CLIENT_METADATA_READ, GRPC_FINISHED,
+               GRPC_SERVER_RPC_NEW);
       return Qnil;
   }
 
@@ -212,7 +217,13 @@
       return grpc_rb_event_metadata(self);
 
     case GRPC_FINISHED:
-      return grpc_rb_status_create_with_mark(self, &event->data.finished);
+      return rb_struct_new(
+          rb_sStatus,
+          UINT2NUM(event->data.finished.status),
+          (event->data.finished.details == NULL ?
+           Qnil : rb_str_new2(event->data.finished.details)),
+          grpc_rb_event_metadata(self),
+          NULL);
       break;
 
     case GRPC_SERVER_RPC_NEW:
@@ -237,6 +248,9 @@
 /* rb_sNewServerRpc is the struct that holds new server rpc details. */
 VALUE rb_sNewServerRpc = Qnil;
 
+/* rb_sStatus is the struct that holds status details. */
+VALUE rb_sStatus = Qnil;
+
 /* rb_cEvent is the Event class whose instances proxy grpc_event */
 VALUE rb_cEvent = Qnil;
 
@@ -250,6 +264,7 @@
   rb_cEvent = rb_define_class_under(rb_mGoogleRpcCore, "Event", rb_cObject);
   rb_sNewServerRpc = rb_struct_define("NewServerRpc", "method", "host",
                                       "deadline", "metadata", NULL);
+  rb_sStatus = rb_struct_define("Status", "code", "details", "metadata", NULL);
 
   /* Prevent allocation or inialization from ruby. */
   rb_define_alloc_func(rb_cEvent, grpc_rb_cannot_alloc);
diff --git a/src/ruby/ext/grpc/rb_event.h b/src/ruby/ext/grpc/rb_event.h
index c398b6c..459502c 100644
--- a/src/ruby/ext/grpc/rb_event.h
+++ b/src/ruby/ext/grpc/rb_event.h
@@ -39,6 +39,9 @@
 /* rb_sNewServerRpc is the struct that holds new server rpc details. */
 extern VALUE rb_sNewServerRpc;
 
+/* rb_sStruct is the struct that holds status details. */
+extern VALUE rb_sStatus;
+
 /* rb_cEvent is the Event class whose instances proxy grpc_event. */
 extern VALUE rb_cEvent;
 
diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c
index 9c54a05..f0e432a 100644
--- a/src/ruby/ext/grpc/rb_grpc.c
+++ b/src/ruby/ext/grpc/rb_grpc.c
@@ -48,7 +48,6 @@
 #include "rb_server.h"
 #include "rb_credentials.h"
 #include "rb_server_credentials.h"
-#include "rb_status.h"
 
 /* Define common vars and funcs declared in rb.h */
 const RUBY_DATA_FUNC GC_NOT_MARKED = NULL;
@@ -157,6 +156,39 @@
   return t;
 }
 
+void Init_google_status_codes() {
+  /* Constants representing the status codes or grpc_status_code in status.h */
+  VALUE rb_mStatusCodes = rb_define_module_under(rb_mGoogleRpcCore,
+                                                 "StatusCodes");
+  rb_define_const(rb_mStatusCodes, "OK", INT2NUM(GRPC_STATUS_OK));
+  rb_define_const(rb_mStatusCodes, "CANCELLED", INT2NUM(GRPC_STATUS_CANCELLED));
+  rb_define_const(rb_mStatusCodes, "UNKNOWN", INT2NUM(GRPC_STATUS_UNKNOWN));
+  rb_define_const(rb_mStatusCodes, "INVALID_ARGUMENT",
+                  INT2NUM(GRPC_STATUS_INVALID_ARGUMENT));
+  rb_define_const(rb_mStatusCodes, "DEADLINE_EXCEEDED",
+                  INT2NUM(GRPC_STATUS_DEADLINE_EXCEEDED));
+  rb_define_const(rb_mStatusCodes, "NOT_FOUND", INT2NUM(GRPC_STATUS_NOT_FOUND));
+  rb_define_const(rb_mStatusCodes, "ALREADY_EXISTS",
+                  INT2NUM(GRPC_STATUS_ALREADY_EXISTS));
+  rb_define_const(rb_mStatusCodes, "PERMISSION_DENIED",
+                  INT2NUM(GRPC_STATUS_PERMISSION_DENIED));
+  rb_define_const(rb_mStatusCodes, "UNAUTHENTICATED",
+                  INT2NUM(GRPC_STATUS_UNAUTHENTICATED));
+  rb_define_const(rb_mStatusCodes, "RESOURCE_EXHAUSTED",
+                  INT2NUM(GRPC_STATUS_RESOURCE_EXHAUSTED));
+  rb_define_const(rb_mStatusCodes, "FAILED_PRECONDITION",
+                  INT2NUM(GRPC_STATUS_FAILED_PRECONDITION));
+  rb_define_const(rb_mStatusCodes, "ABORTED", INT2NUM(GRPC_STATUS_ABORTED));
+  rb_define_const(rb_mStatusCodes, "OUT_OF_RANGE",
+                  INT2NUM(GRPC_STATUS_OUT_OF_RANGE));
+  rb_define_const(rb_mStatusCodes, "UNIMPLEMENTED",
+                  INT2NUM(GRPC_STATUS_UNIMPLEMENTED));
+  rb_define_const(rb_mStatusCodes, "INTERNAL", INT2NUM(GRPC_STATUS_INTERNAL));
+  rb_define_const(rb_mStatusCodes, "UNAVAILABLE",
+                  INT2NUM(GRPC_STATUS_UNAVAILABLE));
+  rb_define_const(rb_mStatusCodes, "DATA_LOSS", INT2NUM(GRPC_STATUS_DATA_LOSS));
+}
+
 /* id_at is the constructor method of the ruby standard Time class. */
 static ID id_at;
 
@@ -233,6 +265,6 @@
   Init_google_rpc_metadata();
   Init_google_rpc_server();
   Init_google_rpc_server_credentials();
-  Init_google_rpc_status();
+  Init_google_status_codes();
   Init_google_time_consts();
 }
diff --git a/src/ruby/ext/grpc/rb_status.c b/src/ruby/ext/grpc/rb_status.c
deleted file mode 100644
index 4c1b6c7..0000000
--- a/src/ruby/ext/grpc/rb_status.c
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#include "rb_status.h"
-
-#include <ruby.h>
-#include <string.h>
-
-#include <grpc/grpc.h>
-#include <grpc/status.h>
-#include "rb_grpc.h"
-
-/* grpc_rb_status wraps a grpc_status.  It provides a peer ruby object, 'mark'
- * to minimize copying when a status is created from ruby. */
-typedef struct grpc_rb_status {
-  /* Holder of ruby objects involved in constructing the status */
-  VALUE mark;
-  /* The actual status */
-  grpc_status *wrapped;
-} grpc_rb_status;
-
-/* Destroys Status instances. */
-static void grpc_rb_status_free(void *p) {
-  grpc_rb_status *status = NULL;
-  if (p == NULL) {
-    return;
-  };
-  status = (grpc_rb_status *)p;
-
-  /* Delete the wrapped object if the mark object is Qnil, which indicates that
-   * no other object is the actual owner. */
-  if (status->wrapped != NULL && status->mark == Qnil) {
-    status->mark = Qnil;
-    if (status->wrapped->details) {
-      xfree(status->wrapped->details);
-    }
-    xfree(status->wrapped);
-  }
-
-  xfree(p);
-}
-
-/* Protects the mark object from GC */
-static void grpc_rb_status_mark(void *p) {
-  grpc_rb_status *status = NULL;
-  if (p == NULL) {
-    return;
-  }
-  status = (grpc_rb_status *)p;
-
-  /* If it's not already cleaned up, mark the mark object */
-  if (status->mark != Qnil) {
-    rb_gc_mark(status->mark);
-  }
-}
-
-/* Allocates Status instances.
-
-   Provides safe initial defaults for the instance fields. */
-static VALUE grpc_rb_status_alloc(VALUE cls) {
-  grpc_rb_status *wrapper = ALLOC(grpc_rb_status);
-  wrapper->wrapped = NULL;
-  wrapper->mark = Qnil;
-  return Data_Wrap_Struct(cls, grpc_rb_status_mark, grpc_rb_status_free,
-                          wrapper);
-}
-
-/* The name of the attribute used on the mark object to hold the details. */
-static ID id_details;
-
-/* Initializes Status instances. */
-static VALUE grpc_rb_status_init(VALUE self, VALUE code, VALUE details) {
-  grpc_rb_status *wrapper = NULL;
-  grpc_status *status = NULL;
-  Data_Get_Struct(self, grpc_rb_status, wrapper);
-
-  /* Use a direct pointer to the original detail value to avoid copying. Assume
-   * that details is null-terminated. */
-  status = ALLOC(grpc_status);
-  status->details = StringValueCStr(details);
-  status->code = NUM2INT(code);
-  wrapper->wrapped = status;
-
-  /* Create the mark and add the original details object to it. */
-  wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject);
-  rb_ivar_set(wrapper->mark, id_details, details);
-  return self;
-}
-
-/* Clones Status instances.
-
-   Gives Status a consistent implementation of Ruby's object copy/dup
-   protocol. */
-static VALUE grpc_rb_status_init_copy(VALUE copy, VALUE orig) {
-  grpc_rb_status *orig_status = NULL;
-  grpc_rb_status *copy_status = NULL;
-
-  if (copy == orig) {
-    return copy;
-  }
-
-  /* Raise an error if orig is not a Status object or a subclass. */
-  if (TYPE(orig) != T_DATA ||
-      RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_status_free) {
-    rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cStatus));
-  }
-
-  Data_Get_Struct(orig, grpc_rb_status, orig_status);
-  Data_Get_Struct(copy, grpc_rb_status, copy_status);
-  MEMCPY(copy_status, orig_status, grpc_rb_status, 1);
-  return copy;
-}
-
-/* Gets the Status code. */
-static VALUE grpc_rb_status_code(VALUE self) {
-  grpc_rb_status *status = NULL;
-  Data_Get_Struct(self, grpc_rb_status, status);
-  return INT2NUM(status->wrapped->code);
-}
-
-/* Gets the Status details. */
-static VALUE grpc_rb_status_details(VALUE self) {
-  VALUE from_ruby;
-  grpc_rb_status *wrapper = NULL;
-  grpc_status *status;
-
-  Data_Get_Struct(self, grpc_rb_status, wrapper);
-  if (wrapper->mark != Qnil) {
-    from_ruby = rb_ivar_get(wrapper->mark, id_details);
-    if (from_ruby != Qnil) {
-      return from_ruby;
-    }
-  }
-
-  status = wrapper->wrapped;
-  if (status == NULL || status->details == NULL) {
-    return Qnil;
-  }
-
-  return rb_str_new2(status->details);
-}
-
-void Init_google_status_codes() {
-  /* Constants representing the status codes or grpc_status_code in status.h */
-  VALUE rb_mStatusCodes = rb_define_module_under(rb_mGoogleRpcCore,
-                                                 "StatusCodes");
-  rb_define_const(rb_mStatusCodes, "OK", INT2NUM(GRPC_STATUS_OK));
-  rb_define_const(rb_mStatusCodes, "CANCELLED", INT2NUM(GRPC_STATUS_CANCELLED));
-  rb_define_const(rb_mStatusCodes, "UNKNOWN", INT2NUM(GRPC_STATUS_UNKNOWN));
-  rb_define_const(rb_mStatusCodes, "INVALID_ARGUMENT",
-                  INT2NUM(GRPC_STATUS_INVALID_ARGUMENT));
-  rb_define_const(rb_mStatusCodes, "DEADLINE_EXCEEDED",
-                  INT2NUM(GRPC_STATUS_DEADLINE_EXCEEDED));
-  rb_define_const(rb_mStatusCodes, "NOT_FOUND", INT2NUM(GRPC_STATUS_NOT_FOUND));
-  rb_define_const(rb_mStatusCodes, "ALREADY_EXISTS",
-                  INT2NUM(GRPC_STATUS_ALREADY_EXISTS));
-  rb_define_const(rb_mStatusCodes, "PERMISSION_DENIED",
-                  INT2NUM(GRPC_STATUS_PERMISSION_DENIED));
-  rb_define_const(rb_mStatusCodes, "UNAUTHENTICATED",
-                  INT2NUM(GRPC_STATUS_UNAUTHENTICATED));
-  rb_define_const(rb_mStatusCodes, "RESOURCE_EXHAUSTED",
-                  INT2NUM(GRPC_STATUS_RESOURCE_EXHAUSTED));
-  rb_define_const(rb_mStatusCodes, "FAILED_PRECONDITION",
-                  INT2NUM(GRPC_STATUS_FAILED_PRECONDITION));
-  rb_define_const(rb_mStatusCodes, "ABORTED", INT2NUM(GRPC_STATUS_ABORTED));
-  rb_define_const(rb_mStatusCodes, "OUT_OF_RANGE",
-                  INT2NUM(GRPC_STATUS_OUT_OF_RANGE));
-  rb_define_const(rb_mStatusCodes, "UNIMPLEMENTED",
-                  INT2NUM(GRPC_STATUS_UNIMPLEMENTED));
-  rb_define_const(rb_mStatusCodes, "INTERNAL", INT2NUM(GRPC_STATUS_INTERNAL));
-  rb_define_const(rb_mStatusCodes, "UNAVAILABLE",
-                  INT2NUM(GRPC_STATUS_UNAVAILABLE));
-  rb_define_const(rb_mStatusCodes, "DATA_LOSS", INT2NUM(GRPC_STATUS_DATA_LOSS));
-}
-
-/* rb_cStatus is the Status class whose instances proxy grpc_status. */
-VALUE rb_cStatus = Qnil;
-
-/* Initializes the Status class. */
-void Init_google_rpc_status() {
-  rb_cStatus = rb_define_class_under(rb_mGoogleRpcCore, "Status", rb_cObject);
-
-  /* Allocates an object whose memory is managed by the Ruby. */
-  rb_define_alloc_func(rb_cStatus, grpc_rb_status_alloc);
-
-  /* Provides a ruby constructor and support for dup/clone. */
-  rb_define_method(rb_cStatus, "initialize", grpc_rb_status_init, 2);
-  rb_define_method(rb_cStatus, "initialize_copy", grpc_rb_status_init_copy, 1);
-
-  /* Provides accessors for the code and details. */
-  rb_define_method(rb_cStatus, "code", grpc_rb_status_code, 0);
-  rb_define_method(rb_cStatus, "details", grpc_rb_status_details, 0);
-  id_details = rb_intern("__details");
-  Init_google_status_codes();
-}
-
-VALUE grpc_rb_status_create_with_mark(VALUE mark, grpc_status* s) {
-  grpc_rb_status *status = NULL;
-  if (s == NULL) {
-    return Qnil;
-  }
-  status = ALLOC(grpc_rb_status);
-  status->wrapped = s;
-  status->mark = mark;
-  return Data_Wrap_Struct(rb_cStatus, grpc_rb_status_mark, grpc_rb_status_free,
-                          status);
-}
-
-/* Gets the wrapped status from the ruby wrapper */
-grpc_status* grpc_rb_get_wrapped_status(VALUE v) {
-  grpc_rb_status *wrapper = NULL;
-  Data_Get_Struct(v, grpc_rb_status, wrapper);
-  return wrapper->wrapped;
-}
diff --git a/src/ruby/ext/grpc/rb_status.h b/src/ruby/ext/grpc/rb_status.h
deleted file mode 100644
index ceb6f9f..0000000
--- a/src/ruby/ext/grpc/rb_status.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- *
- * Copyright 2014, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#ifndef GRPC_RB_STATUS_H_
-#define GRPC_RB_STATUS_H_
-
-#include <grpc/grpc.h>
-#include <ruby.h>
-
-/* rb_cStatus is the Status class whose instances proxy grpc_status. */
-extern VALUE rb_cStatus;
-
-/* grpc_rb_status_create_with_mark creates a grpc_rb_status with a ruby mark
- * object that will be kept alive while the status is alive. */
-extern VALUE grpc_rb_status_create_with_mark(VALUE mark, grpc_status *s);
-
-/* Gets the wrapped status from the ruby wrapper object */
-grpc_status* grpc_rb_get_wrapped_status(VALUE v);
-
-/* Initializes the Status class. */
-void Init_google_rpc_status();
-
-#endif  /* GRPC_RB_STATUS_H_ */
diff --git a/src/ruby/lib/grpc/generic/active_call.rb b/src/ruby/lib/grpc/generic/active_call.rb
index 187c238..b16c8f8 100644
--- a/src/ruby/lib/grpc/generic/active_call.rb
+++ b/src/ruby/lib/grpc/generic/active_call.rb
@@ -207,6 +207,12 @@
     def finished
       ev = @cq.pluck(@finished_tag, INFINITE_FUTURE)
       raise "unexpected event: #{ev.inspect}" unless ev.type == FINISHED
+      if @call.metadata.nil?
+        @call.metadata = ev.result.metadata
+      else
+        @call.metadata.merge!(ev.result.metadata)
+      end
+
       if ev.result.code != Core::StatusCodes::OK
         raise BadStatus.new(ev.result.code, ev.result.details)
       end
@@ -252,7 +258,7 @@
     # FINISHED.
     def send_status(code=OK, details='', assert_finished=false)
       assert_queue_is_ready
-      @call.start_write_status(Core::Status.new(code, details), self)
+      @call.start_write_status(code, details, self)
       ev = @cq.pluck(self, INFINITE_FUTURE)
       assert_event_type(ev, FINISH_ACCEPTED)
       logger.debug("Status sent: #{code}:'#{details}'")
@@ -310,7 +316,7 @@
       return enum_for(:each_remote_read) if !block_given?
       loop do
         resp = remote_read()
-        break if resp.is_a?Core::Status  # is an OK status, bad statii raise
+        break if resp.is_a?Struct::Status  # is an OK status, bad statii raise
         break if resp.nil?  # the last response was received
         yield resp
       end
@@ -340,7 +346,7 @@
       return enum_for(:each_remote_read_then_finish) if !block_given?
       loop do
         resp = remote_read
-        break if resp.is_a?Core::Status  # is an OK status, bad statii raise
+        break if resp.is_a?Struct::Status  # is an OK status, bad statii raise
         if resp.nil?  # the last response was received, but not finished yet
           finished
           break
@@ -363,7 +369,7 @@
       remote_send(req)
       writes_done(false)
       response = remote_read
-      if !response.is_a?(Core::Status)  # finish if status not yet received
+      if !response.is_a?(Struct::Status)  # finish if status not yet received
         finished
       end
       response
@@ -388,7 +394,7 @@
       requests.each { |r| remote_send(r) }
       writes_done(false)
       response = remote_read
-      if !response.is_a?(Core::Status)  # finish if status not yet received
+      if !response.is_a?(Struct::Status)  # finish if status not yet received
         finished
       end
       response
diff --git a/src/ruby/spec/call_spec.rb b/src/ruby/spec/call_spec.rb
index 1332b3c..8e8e3d3 100644
--- a/src/ruby/spec/call_spec.rb
+++ b/src/ruby/spec/call_spec.rb
@@ -88,29 +88,30 @@
 
   describe '#start_read' do
     it 'should fail if called immediately' do
-      expect { make_test_call.start_read(@tag) }.to raise_error GRPC::Core::CallError
+      blk = Proc.new { make_test_call.start_read(@tag) }
+      expect(&blk).to raise_error GRPC::Core::CallError
     end
   end
 
   describe '#start_write' do
     it 'should fail if called immediately' do
       bytes = GRPC::Core::ByteBuffer.new('test string')
-      expect { make_test_call.start_write(bytes, @tag) }
-          .to raise_error GRPC::Core::CallError
+      blk = Proc.new { make_test_call.start_write(bytes, @tag) }
+      expect(&blk).to raise_error GRPC::Core::CallError
     end
   end
 
   describe '#start_write_status' do
     it 'should fail if called immediately' do
-      sts = GRPC::Core::Status.new(153, 'test detail')
-      expect { make_test_call.start_write_status(sts, @tag) }
-          .to raise_error GRPC::Core::CallError
+      blk = Proc.new { make_test_call.start_write_status(153, 'x', @tag) }
+      expect(&blk).to raise_error GRPC::Core::CallError
     end
   end
 
   describe '#writes_done' do
     it 'should fail if called immediately' do
-      expect { make_test_call.writes_done(@tag) }.to raise_error GRPC::Core::CallError
+      blk = Proc.new { make_test_call.writes_done(Object.new) }
+      expect(&blk).to raise_error GRPC::Core::CallError
     end
   end
 
@@ -153,9 +154,9 @@
   describe '#status' do
     it 'can save the status and read it back' do
       call = make_test_call
-      sts = GRPC::Core::Status.new(OK, 'OK')
+      sts = Struct::Status.new(OK, 'OK')
       expect { call.status = sts }.not_to raise_error
-      expect(call.status).to be(sts)
+      expect(call.status).to eq(sts)
     end
 
     it 'must be set to a status' do
diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb
index 91abfc0..5e68f52 100644
--- a/src/ruby/spec/client_server_spec.rb
+++ b/src/ruby/spec/client_server_spec.rb
@@ -154,8 +154,8 @@
     server_call = ev.call
     server_call.server_accept(@server_queue, @server_finished_tag)
     server_call.server_end_initial_metadata()
-    sts = Status.new(StatusCodes::NOT_FOUND, 'not found')
-    server_call.start_write_status(sts, @server_tag)
+    server_call.start_write_status(StatusCodes::NOT_FOUND, 'not found',
+                                   @server_tag)
 
     # client gets an empty response for the read, preceeded by some metadata.
     call.start_read(@tag)
@@ -175,8 +175,7 @@
     call = new_client_call
     client_sends(call)
     server_call = server_receives_and_responds_with('server_response')
-    sts = Status.new(10101, 'status code is 10101')
-    server_call.start_write_status(sts, @server_tag)
+    server_call.start_write_status(10101, 'status code is 10101', @server_tag)
 
     # first the client says writes are done
     call.start_read(@tag)
@@ -187,7 +186,7 @@
     # but nothing happens until the server sends a status
     expect_next_event_on(@server_queue, FINISH_ACCEPTED, @server_tag)
     ev = expect_next_event_on(@server_queue, FINISHED, @server_finished_tag)
-    expect(ev.result).to be_a(Status)
+    expect(ev.result).to be_a(Struct::Status)
 
     # client gets FINISHED
     expect_next_event_on(@client_queue, FINISH_ACCEPTED, @tag)
diff --git a/src/ruby/spec/status_spec.rb b/src/ruby/spec/status_spec.rb
deleted file mode 100644
index 63dcefb..0000000
--- a/src/ruby/spec/status_spec.rb
+++ /dev/null
@@ -1,166 +0,0 @@
-# Copyright 2014, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-#     * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#     * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-#     * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-require 'grpc'
-
-
-describe GRPC::Core::StatusCodes do
-
-  StatusCodes = GRPC::Core::StatusCodes
-
-  before(:each) do
-    @known_types = {
-      :OK => 0,
-      :CANCELLED => 1,
-      :UNKNOWN => 2,
-      :INVALID_ARGUMENT => 3,
-      :DEADLINE_EXCEEDED => 4,
-      :NOT_FOUND => 5,
-      :ALREADY_EXISTS => 6,
-      :PERMISSION_DENIED => 7,
-      :RESOURCE_EXHAUSTED => 8,
-      :FAILED_PRECONDITION => 9,
-      :ABORTED => 10,
-      :OUT_OF_RANGE => 11,
-      :UNIMPLEMENTED => 12,
-      :INTERNAL => 13,
-      :UNAVAILABLE => 14,
-      :DATA_LOSS => 15,
-      :UNAUTHENTICATED => 16
-    }
-  end
-
-  it 'should have symbols for all the known status codes' do
-    m = StatusCodes
-    syms_and_codes = m.constants.collect { |c| [c, m.const_get(c)] }
-    expect(Hash[syms_and_codes]).to eq(@known_types)
-  end
-
-end
-
-
-describe GRPC::Core::Status do
-
-  Status = GRPC::Core::Status
-
-  describe '#new' do
-    it 'should create new instances' do
-      expect { Status.new(142, 'test details') }.to_not raise_error
-    end
-  end
-
-  describe '#details' do
-    it 'return the detail' do
-      sts = Status.new(142, 'test details')
-      expect(sts.details).to eq('test details')
-    end
-  end
-
-  describe '#code' do
-    it 'should return the code' do
-      sts = Status.new(142, 'test details')
-      expect(sts.code).to eq(142)
-    end
-  end
-
-  describe '#dup' do
-    it 'should create a copy that returns the correct details' do
-      sts = Status.new(142, 'test details')
-      expect(sts.dup.code).to eq(142)
-    end
-
-    it 'should create a copy that returns the correct code' do
-      sts = Status.new(142, 'test details')
-      expect(sts.dup.details).to eq('test details')
-    end
-  end
-
-
-end
-
-
-describe GRPC::BadStatus do
-
-  BadStatus = GRPC::BadStatus
-
-  describe '#new' do
-    it 'should create new instances' do
-      expect { BadStatus.new(142, 'test details') }.to_not raise_error
-    end
-  end
-
-  describe '#details' do
-    it 'return the detail' do
-      err = BadStatus.new(142, 'test details')
-      expect(err.details).to eq('test details')
-    end
-  end
-
-  describe '#code' do
-    it 'should return the code' do
-      err = BadStatus.new(142, 'test details')
-      expect(err.code).to eq(142)
-    end
-  end
-
-  describe '#dup' do
-    it 'should create a copy that returns the correct details' do
-      err = BadStatus.new(142, 'test details')
-      expect(err.dup.code).to eq(142)
-    end
-
-    it 'should create a copy that returns the correct code' do
-      err = BadStatus.new(142, 'test details')
-      expect(err.dup.details).to eq('test details')
-    end
-  end
-
-  describe '#to_status' do
-    it 'should create a Status with the same code and details' do
-      err = BadStatus.new(142, 'test details')
-      sts = err.to_status
-      expect(sts.code).to eq(142)
-      expect(sts.details).to eq('test details')
-    end
-
-    it 'should create a copy that returns the correct code' do
-      err = BadStatus.new(142, 'test details')
-      expect(err.dup.details).to eq('test details')
-    end
-  end
-
-  describe 'as an exception' do
-
-    it 'can be raised' do
-      blk = Proc.new { raise BadStatus.new(343, 'status 343') }
-      expect(&blk).to raise_error(BadStatus)
-    end
-  end
-
-end