Incorporating ruby into the master grpc repository.
	Change on 2014/12/01 by nnoble <nnoble@google.com>
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=81111468
diff --git a/src/ruby/ext/grpc/extconf.rb b/src/ruby/ext/grpc/extconf.rb
new file mode 100644
index 0000000..06bfad9
--- /dev/null
+++ b/src/ruby/ext/grpc/extconf.rb
@@ -0,0 +1,92 @@
+# 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 'mkmf'
+
+LIBDIR = RbConfig::CONFIG['libdir']
+INCLUDEDIR = RbConfig::CONFIG['includedir']
+
+HEADER_DIRS = [
+    # First search the local development dir
+    ENV['HOME'] + '/grpc_dev/include',
+
+    # Then search /opt/local (Mac)
+    '/opt/local/include',
+
+    # Then search /usr/local (Source install)
+    '/usr/local/include',
+
+    # Check the ruby install locations
+    INCLUDEDIR,
+
+    # Finally fall back to /usr
+    '/usr/include'
+]
+
+LIB_DIRS = [
+    # First search the local development dir
+    ENV['HOME'] + '/grpc_dev/lib',
+
+    # Then search /opt/local for (Mac)
+    '/opt/local/lib',
+
+    # Then search /usr/local (Source install)
+    '/usr/local/lib',
+
+    # Check the ruby install locations
+    LIBDIR,
+
+    # Finally fall back to /usr
+    '/usr/lib'
+]
+
+def crash(msg)
+  print(" extconf failure: %s\n" % msg)
+  exit 1
+end
+
+dir_config('grpc', HEADER_DIRS, LIB_DIRS)
+
+$CFLAGS << ' -std=c89 '
+$CFLAGS << ' -Wno-implicit-function-declaration '
+$CFLAGS << ' -Wno-pointer-sign '
+$CFLAGS << ' -Wno-return-type '
+$CFLAGS << ' -Wall '
+$CFLAGS << ' -pedantic '
+
+$LDFLAGS << ' -lgrpc -lgpr -levent -levent_pthreads -levent_core'
+
+# crash('need grpc lib') unless have_library('grpc', 'grpc_channel_destroy')
+#
+# TODO(temiola): figure out why this stopped working, but the so is built OK
+# and the tests pass
+
+have_library('grpc', 'grpc_channel_destroy')
+crash('need gpr lib') unless have_library('gpr', 'gpr_now')
+create_makefile('grpc/grpc')
diff --git a/src/ruby/ext/grpc/rb_byte_buffer.c b/src/ruby/ext/grpc/rb_byte_buffer.c
new file mode 100644
index 0000000..a520ca4
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_byte_buffer.c
@@ -0,0 +1,243 @@
+/*
+ *
+ * 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_byte_buffer.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/slice.h>
+#include "rb_grpc.h"
+
+/* grpc_rb_byte_buffer wraps a grpc_byte_buffer.  It provides a peer ruby
+ * object, 'mark' to minimize copying when a byte_buffer is created from
+ * ruby. */
+typedef struct grpc_rb_byte_buffer {
+  /* Holder of ruby objects involved in constructing the status */
+  VALUE mark;
+  /* The actual status */
+  grpc_byte_buffer *wrapped;
+} grpc_rb_byte_buffer;
+
+
+/* Destroys ByteBuffer instances. */
+static void grpc_rb_byte_buffer_free(void *p) {
+  grpc_rb_byte_buffer *bb = NULL;
+  if (p == NULL) {
+    return;
+  };
+  bb = (grpc_rb_byte_buffer *)p;
+
+  /* Deletes the wrapped object if the mark object is Qnil, which indicates
+   * that no other object is the actual owner. */
+  if (bb->wrapped != NULL && bb->mark == Qnil) {
+    grpc_byte_buffer_destroy(bb->wrapped);
+  }
+
+  xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_byte_buffer_mark(void *p) {
+  grpc_rb_byte_buffer *bb = NULL;
+  if (p == NULL) {
+    return;
+  }
+  bb = (grpc_rb_byte_buffer *)p;
+
+  /* If it's not already cleaned up, mark the mark object */
+  if (bb->mark != Qnil && BUILTIN_TYPE(bb->mark) != T_NONE) {
+    rb_gc_mark(bb->mark);
+  }
+}
+
+/* id_source is the name of the hidden ivar the preserves the original
+ * byte_buffer source string */
+static ID id_source;
+
+/* Allocates ByteBuffer instances.
+
+   Provides safe default values for the byte_buffer fields. */
+static VALUE grpc_rb_byte_buffer_alloc(VALUE cls) {
+  grpc_rb_byte_buffer *wrapper = ALLOC(grpc_rb_byte_buffer);
+  wrapper->wrapped = NULL;
+  wrapper->mark = Qnil;
+  return Data_Wrap_Struct(cls, grpc_rb_byte_buffer_mark,
+                          grpc_rb_byte_buffer_free, wrapper);
+}
+
+/* Clones ByteBuffer instances.
+
+   Gives ByteBuffer a consistent implementation of Ruby's object copy/dup
+   protocol. */
+static VALUE grpc_rb_byte_buffer_init_copy(VALUE copy, VALUE orig) {
+  grpc_rb_byte_buffer *orig_bb = NULL;
+  grpc_rb_byte_buffer *copy_bb = NULL;
+
+  if (copy == orig) {
+    return copy;
+  }
+
+  /* Raise an error if orig is not a metadata object or a subclass. */
+  if (TYPE(orig) != T_DATA ||
+      RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_byte_buffer_free) {
+    rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cByteBuffer));
+  }
+
+  Data_Get_Struct(orig, grpc_rb_byte_buffer, orig_bb);
+  Data_Get_Struct(copy, grpc_rb_byte_buffer, copy_bb);
+
+  /* use ruby's MEMCPY to make a byte-for-byte copy of the metadata wrapper
+   * object. */
+  MEMCPY(copy_bb, orig_bb, grpc_rb_byte_buffer, 1);
+  return copy;
+}
+
+/* id_empty is used to return the empty string from to_s when necessary. */
+static ID id_empty;
+
+static VALUE grpc_rb_byte_buffer_to_s(VALUE self) {
+  grpc_rb_byte_buffer *wrapper = NULL;
+  grpc_byte_buffer *bb = NULL;
+  grpc_byte_buffer_reader *reader = NULL;
+  char *output = NULL;
+  size_t length = 0;
+  size_t offset = 0;
+  VALUE output_obj = Qnil;
+  gpr_slice next;
+
+  Data_Get_Struct(self, grpc_rb_byte_buffer, wrapper);
+  output_obj = rb_ivar_get(wrapper->mark, id_source);
+  if (output_obj != Qnil) {
+    /* From ruby, ByteBuffers are immutable so if a source is set, return that
+     * as the to_s value */
+    return output_obj;
+  }
+
+  /* Read the bytes. */
+  bb = wrapper->wrapped;
+  if (bb == NULL) {
+    return rb_id2str(id_empty);
+  }
+  length = grpc_byte_buffer_length(bb);
+  if (length == 0) {
+    return rb_id2str(id_empty);
+  }
+  reader = grpc_byte_buffer_reader_create(bb);
+  output = xmalloc(length);
+  while (grpc_byte_buffer_reader_next(reader, &next) != 0) {
+    memcpy(output + offset, GPR_SLICE_START_PTR(next), GPR_SLICE_LENGTH(next));
+    offset += GPR_SLICE_LENGTH(next);
+  }
+  output_obj = rb_str_new(output, length);
+
+  /* Save a references to the computed string in the mark object so that the
+   * calling to_s does not do any allocations. */
+  wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject);
+  rb_ivar_set(wrapper->mark, id_source, output_obj);
+
+  return output_obj;
+}
+
+
+/* Initializes ByteBuffer instances. */
+static VALUE grpc_rb_byte_buffer_init(VALUE self, VALUE src) {
+  gpr_slice a_slice;
+  grpc_rb_byte_buffer *wrapper = NULL;
+  grpc_byte_buffer *byte_buffer = NULL;
+
+  if (TYPE(src) != T_STRING) {
+    rb_raise(rb_eTypeError, "bad byte_buffer arg: got <%s>, want <String>",
+             rb_obj_classname(src));
+    return Qnil;
+  }
+  Data_Get_Struct(self, grpc_rb_byte_buffer, wrapper);
+  a_slice = gpr_slice_malloc(RSTRING_LEN(src));
+  memcpy(GPR_SLICE_START_PTR(a_slice), RSTRING_PTR(src), RSTRING_LEN(src));
+  byte_buffer = grpc_byte_buffer_create(&a_slice, 1);
+  gpr_slice_unref(a_slice);
+
+  if (byte_buffer == NULL) {
+    rb_raise(rb_eArgError, "could not create a byte_buffer, not sure why");
+    return Qnil;
+  }
+  wrapper->wrapped = byte_buffer;
+
+  /* Save a references to the original string in the mark object so that the
+   * pointers used there is valid for the lifetime of the object. */
+  wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject);
+  rb_ivar_set(wrapper->mark, id_source, src);
+
+  return self;
+}
+
+/* rb_cByteBuffer is the ruby class that proxies grpc_byte_buffer. */
+VALUE rb_cByteBuffer = Qnil;
+
+void Init_google_rpc_byte_buffer() {
+  rb_cByteBuffer = rb_define_class_under(rb_mGoogleRPC, "ByteBuffer",
+                                         rb_cObject);
+
+  /* Allocates an object managed by the ruby runtime */
+  rb_define_alloc_func(rb_cByteBuffer, grpc_rb_byte_buffer_alloc);
+
+  /* Provides a ruby constructor and support for dup/clone. */
+  rb_define_method(rb_cByteBuffer, "initialize", grpc_rb_byte_buffer_init, 1);
+  rb_define_method(rb_cByteBuffer, "initialize_copy",
+                   grpc_rb_byte_buffer_init_copy, 1);
+
+  /* Provides a to_s method that returns the buffer value */
+  rb_define_method(rb_cByteBuffer, "to_s", grpc_rb_byte_buffer_to_s, 0);
+
+  id_source = rb_intern("__source");
+  id_empty = rb_intern("");
+}
+
+VALUE grpc_rb_byte_buffer_create_with_mark(VALUE mark, grpc_byte_buffer* bb) {
+  grpc_rb_byte_buffer *byte_buffer = NULL;
+  if (bb == NULL) {
+    return Qnil;
+  }
+  byte_buffer = ALLOC(grpc_rb_byte_buffer);
+  byte_buffer->wrapped = bb;
+  byte_buffer->mark = mark;
+  return Data_Wrap_Struct(rb_cByteBuffer, grpc_rb_byte_buffer_mark,
+                          grpc_rb_byte_buffer_free, byte_buffer);
+}
+
+/* Gets the wrapped byte_buffer from the ruby wrapper */
+grpc_byte_buffer* grpc_rb_get_wrapped_byte_buffer(VALUE v) {
+  grpc_rb_byte_buffer *wrapper = NULL;
+  Data_Get_Struct(v, grpc_rb_byte_buffer, wrapper);
+  return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_byte_buffer.h b/src/ruby/ext/grpc/rb_byte_buffer.h
new file mode 100644
index 0000000..1bdcfe4
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_byte_buffer.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * 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_BYTE_BUFFER_H_
+#define GRPC_RB_BYTE_BUFFER_H_
+
+#include <grpc/grpc.h>
+#include <ruby.h>
+
+/* rb_cByteBuffer is the ByteBuffer class whose instances proxy
+   grpc_byte_buffer. */
+extern VALUE rb_cByteBuffer;
+
+/* Initializes the ByteBuffer class. */
+void Init_google_rpc_byte_buffer();
+
+/* grpc_rb_byte_buffer_create_with_mark creates a grpc_rb_byte_buffer with a
+ * ruby mark object that will be kept alive while the byte_buffer is alive. */
+VALUE grpc_rb_byte_buffer_create_with_mark(VALUE mark, grpc_byte_buffer* bb);
+
+/* Gets the wrapped byte_buffer from its ruby object. */
+grpc_byte_buffer* grpc_rb_get_wrapped_byte_buffer(VALUE v);
+
+#endif  /* GRPC_RB_BYTE_BUFFER_H_ */
diff --git a/src/ruby/ext/grpc/rb_call.c b/src/ruby/ext/grpc/rb_call.c
new file mode 100644
index 0000000..07f70e0
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_call.c
@@ -0,0 +1,542 @@
+/*
+ *
+ * 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_call.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include "rb_byte_buffer.h"
+#include "rb_completion_queue.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
+ * completion queue */
+static ID id_cq;
+
+/* id_flags is the name of the hidden ivar that preserves the value of
+ * the flags used to create metadata from a Hash */
+static ID id_flags;
+
+/* id_input_md is the name of the hidden ivar that preserves the hash used to
+ * create metadata, so that references to the strings it contains last as long
+ * as the call the metadata is added to. */
+static ID id_input_md;
+
+/* id_metadata is name of the attribute used to access the metadata hash
+ * received by the call and subsequently saved on it. */
+static ID id_metadata;
+
+/* id_status is name of the attribute used to access the status object
+ * received by the call and subsequently saved on it. */
+static ID id_status;
+
+/* hash_all_calls is a hash of Call address -> reference count that is used to
+ * track the creation and destruction of rb_call instances.
+ */
+static VALUE hash_all_calls;
+
+/* Destroys a Call. */
+void grpc_rb_call_destroy(void *p) {
+  grpc_call *call = NULL;
+  VALUE ref_count = Qnil;
+  if (p == NULL) {
+    return;
+  };
+  call = (grpc_call *)p;
+
+  ref_count = rb_hash_aref(hash_all_calls, OFFT2NUM((VALUE)call));
+  if (ref_count == Qnil) {
+    return;  /* No longer in the hash, so already deleted */
+  } else if (NUM2UINT(ref_count) == 1) {
+    rb_hash_delete(hash_all_calls, OFFT2NUM((VALUE)call));
+    grpc_call_destroy(call);
+  } else {
+    rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)call),
+                 UINT2NUM(NUM2UINT(ref_count) - 1));
+  }
+}
+
+/* Error code details is a hash containing text strings describing errors */
+VALUE rb_error_code_details;
+
+/* Obtains the error detail string for given error code */
+const char* grpc_call_error_detail_of(grpc_call_error err) {
+  VALUE detail_ref = rb_hash_aref(rb_error_code_details, UINT2NUM(err));
+  const char* detail = "unknown error code!";
+  if (detail_ref != Qnil) {
+    detail = StringValueCStr(detail_ref);
+  }
+  return detail;
+}
+
+/* grpc_rb_call_add_metadata_hash_cb is the hash iteration callback used by
+   grpc_rb_call_add_metadata.
+*/
+int grpc_rb_call_add_metadata_hash_cb(VALUE key, VALUE val, VALUE call_obj) {
+  grpc_call *call = NULL;
+  grpc_metadata *md = NULL;
+  VALUE md_obj = Qnil;
+  VALUE md_obj_args[2];
+  VALUE flags = rb_ivar_get(call_obj, id_flags);
+  grpc_call_error err;
+  int array_length;
+  int i;
+
+  /* Construct a metadata object from key and value and add it */
+  Data_Get_Struct(call_obj, grpc_call, call);
+  md_obj_args[0] = key;
+
+  if (TYPE(val) == T_ARRAY) {
+    /* If the value is an array, add each value in the array separately */
+    array_length = RARRAY_LEN(val);
+    for (i = 0; i < array_length; i++) {
+      md_obj_args[1] = rb_ary_entry(val, i);
+      md_obj = rb_class_new_instance(2, md_obj_args, rb_cMetadata);
+      md = grpc_rb_get_wrapped_metadata(md_obj);
+      err = grpc_call_add_metadata(call, md, NUM2UINT(flags));
+      if (err != GRPC_CALL_OK) {
+        rb_raise(rb_eCallError, "add metadata failed: %s (code=%d)",
+                 grpc_call_error_detail_of(err), err);
+        return ST_STOP;
+      }
+    }
+  } else {
+    md_obj_args[1] = val;
+    md_obj = rb_class_new_instance(2, md_obj_args, rb_cMetadata);
+    md = grpc_rb_get_wrapped_metadata(md_obj);
+    err = grpc_call_add_metadata(call, md, NUM2UINT(flags));
+    if (err != GRPC_CALL_OK) {
+      rb_raise(rb_eCallError, "add metadata failed: %s (code=%d)",
+               grpc_call_error_detail_of(err), err);
+      return ST_STOP;
+    }
+  }
+
+  return ST_CONTINUE;
+}
+
+/*
+  call-seq:
+     call.add_metadata(completion_queue, hash_elements, flags=nil)
+
+  Add metadata elements to the call from a ruby hash, to be sent upon
+  invocation. flags is a bit-field combination of the write flags defined
+  above.  REQUIRES: grpc_call_start_invoke/grpc_call_accept have not been
+  called on this call.  Produces no events. */
+
+static VALUE grpc_rb_call_add_metadata(int argc, VALUE *argv, VALUE self) {
+  VALUE metadata;
+  VALUE flags = Qnil;
+  ID id_size = rb_intern("size");
+
+  /* "11" == 1 mandatory args, 1 (flags) is optional */
+  rb_scan_args(argc, argv, "11", &metadata, &flags);
+  if (NIL_P(flags)) {
+    flags = UINT2NUM(0);  /* Default to no flags */
+  }
+  if (TYPE(metadata) != T_HASH) {
+    rb_raise(rb_eTypeError, "add metadata failed: metadata should be a hash");
+    return Qnil;
+  }
+  if (NUM2UINT(rb_funcall(metadata, id_size, 0)) == 0) {
+    return Qnil;
+  }
+  rb_ivar_set(self, id_flags, flags);
+  rb_ivar_set(self, id_input_md, metadata);
+  rb_hash_foreach(metadata, grpc_rb_call_add_metadata_hash_cb, self);
+  return Qnil;
+}
+
+/* Called by clients to cancel an RPC on the server.
+   Can be called multiple times, from any thread. */
+static VALUE grpc_rb_call_cancel(VALUE self) {
+  grpc_call *call = NULL;
+  grpc_call_error err;
+  Data_Get_Struct(self, grpc_call, call);
+  err = grpc_call_cancel(call);
+  if (err != GRPC_CALL_OK) {
+    rb_raise(rb_eCallError, "cancel failed: %s (code=%d)",
+             grpc_call_error_detail_of(err), err);
+  }
+
+  return Qnil;
+}
+
+/*
+  call-seq:
+     call.start_invoke(completion_queue, tag, flags=nil)
+
+   Invoke the RPC. Starts sending metadata and request headers on the wire.
+   flags is a bit-field combination of the write flags defined above.
+   REQUIRES: Can be called at most once per call.
+             Can only be called on the client.
+   Produces a GRPC_INVOKE_ACCEPTED event on completion. */
+static VALUE grpc_rb_call_start_invoke(int argc, VALUE *argv, VALUE self) {
+  VALUE cqueue = Qnil;
+  VALUE invoke_accepted_tag = Qnil;
+  VALUE metadata_read_tag = Qnil;
+  VALUE finished_tag = Qnil;
+  VALUE flags = Qnil;
+  grpc_call *call = NULL;
+  grpc_completion_queue *cq = NULL;
+  grpc_call_error err;
+
+  /* "41" == 4 mandatory args, 1 (flags) is optional */
+  rb_scan_args(argc, argv, "41", &cqueue, &invoke_accepted_tag,
+               &metadata_read_tag, &finished_tag, &flags);
+  if (NIL_P(flags)) {
+    flags = UINT2NUM(0);  /* Default to no flags */
+  }
+  cq = grpc_rb_get_wrapped_completion_queue(cqueue);
+  Data_Get_Struct(self, grpc_call, call);
+  err = grpc_call_start_invoke(call, cq, ROBJECT(invoke_accepted_tag),
+                               ROBJECT(metadata_read_tag),
+                               ROBJECT(finished_tag),
+                               NUM2UINT(flags));
+  if (err != GRPC_CALL_OK) {
+    rb_raise(rb_eCallError, "invoke failed: %s (code=%d)",
+             grpc_call_error_detail_of(err), err);
+  }
+
+  /* Add the completion queue as an instance attribute, prevents it from being
+   * GCed until this call object is GCed */
+  rb_ivar_set(self, id_cq, cqueue);
+
+  return Qnil;
+}
+
+/* Initiate a read on a call. Output event contains a byte buffer with the
+   result of the read.
+   REQUIRES: No other reads are pending on the call. It is only safe to start
+   the next read after the corresponding read event is received. */
+static VALUE grpc_rb_call_start_read(VALUE self, VALUE tag) {
+  grpc_call *call = NULL;
+  grpc_call_error err;
+  Data_Get_Struct(self, grpc_call, call);
+  err = grpc_call_start_read(call, ROBJECT(tag));
+  if (err != GRPC_CALL_OK) {
+    rb_raise(rb_eCallError, "start read failed: %s (code=%d)",
+             grpc_call_error_detail_of(err), err);
+  }
+
+  return Qnil;
+}
+
+/*
+  call-seq:
+    status = call.status
+
+    Gets the status object saved the call.  */
+static VALUE grpc_rb_call_get_status(VALUE self) {
+  return rb_ivar_get(self, id_status);
+}
+
+/*
+  call-seq:
+    call.status = status
+
+    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>",
+             rb_obj_classname(status));
+    return Qnil;
+  }
+
+  return rb_ivar_set(self, id_status, status);
+}
+
+/*
+  call-seq:
+    metadata = call.metadata
+
+    Gets the metadata object saved the call.  */
+static VALUE grpc_rb_call_get_metadata(VALUE self) {
+  return rb_ivar_get(self, id_metadata);
+}
+
+/*
+  call-seq:
+    call.metadata = metadata
+
+    Saves the metadata hash on the call.  */
+static VALUE grpc_rb_call_set_metadata(VALUE self, VALUE metadata) {
+  if (!NIL_P(metadata) && TYPE(metadata) != T_HASH) {
+    rb_raise(rb_eTypeError, "bad metadata: got:<%s> want: <Hash>",
+             rb_obj_classname(metadata));
+    return Qnil;
+  }
+
+  return rb_ivar_set(self, id_metadata, metadata);
+}
+
+/*
+  call-seq:
+     call.start_write(byte_buffer, tag, flags=nil)
+
+   Queue a byte buffer for writing.
+   flags is a bit-field combination of the write flags defined above.
+   A write with byte_buffer null is allowed, and will not send any bytes on the
+   wire. If this is performed without GRPC_WRITE_BUFFER_HINT flag it provides
+   a mechanism to flush any previously buffered writes to outgoing flow control.
+   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.
+             GRPC_INVOKE_ACCEPTED must have been received by the application
+             prior to calling this on the client. On the server,
+             grpc_call_accept must have been called successfully.
+   Produces a GRPC_WRITE_ACCEPTED event. */
+static VALUE grpc_rb_call_start_write(int argc, VALUE *argv, VALUE self) {
+  VALUE byte_buffer = Qnil;
+  VALUE tag = Qnil;
+  VALUE flags = Qnil;
+  grpc_call *call = NULL;
+  grpc_byte_buffer *bfr = NULL;
+  grpc_call_error err;
+
+  /* "21" == 2 mandatory args, 1 (flags) is optional */
+  rb_scan_args(argc, argv, "21", &byte_buffer, &tag, &flags);
+  if (NIL_P(flags)) {
+    flags = UINT2NUM(0);  /* Default to no flags */
+  }
+  bfr = grpc_rb_get_wrapped_byte_buffer(byte_buffer);
+  Data_Get_Struct(self, grpc_call, call);
+  err = grpc_call_start_write(call, bfr, ROBJECT(tag), NUM2UINT(flags));
+  if (err != GRPC_CALL_OK) {
+    rb_raise(rb_eCallError, "start write failed: %s (code=%d)",
+             grpc_call_error_detail_of(err), err);
+  }
+
+  return Qnil;
+}
+
+/* Queue a status for writing.
+   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.
+   GRPC_INVOKE_ACCEPTED must have been received by the application
+   prior to calling this.
+   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) {
+  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));
+  if (err != GRPC_CALL_OK) {
+    rb_raise(rb_eCallError, "start write status: %s (code=%d)",
+             grpc_call_error_detail_of(err), err);
+  }
+
+  return Qnil;
+}
+
+/* No more messages to send.
+   REQUIRES: No other writes are pending on the call. */
+static VALUE grpc_rb_call_writes_done(VALUE self, VALUE tag) {
+  grpc_call *call = NULL;
+  grpc_call_error err;
+  Data_Get_Struct(self, grpc_call, call);
+  err = grpc_call_writes_done(call, ROBJECT(tag));
+  if (err != GRPC_CALL_OK) {
+    rb_raise(rb_eCallError, "writes done: %s (code=%d)",
+             grpc_call_error_detail_of(err), err);
+  }
+
+  return Qnil;
+}
+
+/* call-seq:
+     call.accept(completion_queue, flags=nil)
+
+   Accept an incoming RPC, binding a completion queue to it.
+   To be called after adding metadata to the call, but before sending
+   messages.
+   flags is a bit-field combination of the write flags defined above.
+   REQUIRES: Can be called at most once per call.
+             Can only be called on the server.
+   Produces no events. */
+static VALUE grpc_rb_call_accept(int argc, VALUE *argv, VALUE self) {
+  VALUE cqueue = Qnil;
+  VALUE finished_tag = Qnil;
+  VALUE flags = Qnil;
+  grpc_call *call = NULL;
+  grpc_completion_queue *cq = NULL;
+  grpc_call_error err;
+
+  /* "21" == 2 mandatory args, 1 (flags) is optional */
+  rb_scan_args(argc, argv, "21", &cqueue, &finished_tag, &flags);
+  if (NIL_P(flags)) {
+    flags = UINT2NUM(0);  /* Default to no flags */
+  }
+  cq = grpc_rb_get_wrapped_completion_queue(cqueue);
+  Data_Get_Struct(self, grpc_call, call);
+  err = grpc_call_accept(call, cq, ROBJECT(finished_tag), NUM2UINT(flags));
+  if (err != GRPC_CALL_OK) {
+    rb_raise(rb_eCallError, "accept failed: %s (code=%d)",
+             grpc_call_error_detail_of(err), err);
+  }
+
+  /* Add the completion queue as an instance attribute, prevents it from being
+   * GCed until this call object is GCed */
+  rb_ivar_set(self, id_cq, cqueue);
+
+  return Qnil;
+}
+
+/* rb_cCall is the ruby class that proxies grpc_call. */
+VALUE rb_cCall = Qnil;
+
+/* rb_eCallError is the ruby class of the exception thrown during call
+   operations; */
+VALUE rb_eCallError = Qnil;
+
+void Init_google_rpc_error_codes() {
+  /* Constants representing the error codes of grpc_call_error in grpc.h */
+  VALUE rb_RpcErrors = rb_define_module_under(rb_mGoogleRPC, "RpcErrors");
+  rb_define_const(rb_RpcErrors, "OK", UINT2NUM(GRPC_CALL_OK));
+  rb_define_const(rb_RpcErrors, "ERROR", UINT2NUM(GRPC_CALL_ERROR));
+  rb_define_const(rb_RpcErrors, "NOT_ON_SERVER",
+                  UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER));
+  rb_define_const(rb_RpcErrors, "NOT_ON_CLIENT",
+                  UINT2NUM(GRPC_CALL_ERROR_NOT_ON_CLIENT));
+  rb_define_const(rb_RpcErrors, "ALREADY_INVOKED",
+                  UINT2NUM(GRPC_CALL_ERROR_ALREADY_INVOKED));
+  rb_define_const(rb_RpcErrors, "NOT_INVOKED",
+                  UINT2NUM(GRPC_CALL_ERROR_NOT_INVOKED));
+  rb_define_const(rb_RpcErrors, "ALREADY_FINISHED",
+                  UINT2NUM(GRPC_CALL_ERROR_ALREADY_FINISHED));
+  rb_define_const(rb_RpcErrors, "TOO_MANY_OPERATIONS",
+                  UINT2NUM(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS));
+  rb_define_const(rb_RpcErrors, "INVALID_FLAGS",
+                  UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS));
+
+  /* Add the detail strings to a Hash */
+  rb_error_code_details = rb_hash_new();
+  rb_hash_aset(rb_error_code_details,
+               UINT2NUM(GRPC_CALL_OK), rb_str_new2("ok"));
+  rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR),
+               rb_str_new2("unknown error"));
+  rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_ON_SERVER),
+               rb_str_new2("not available on a server"));
+  rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_ON_CLIENT),
+               rb_str_new2("not available on a client"));
+  rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_ALREADY_INVOKED),
+               rb_str_new2("call is already invoked"));
+  rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_NOT_INVOKED),
+               rb_str_new2("call is not yet invoked"));
+  rb_hash_aset(rb_error_code_details,
+               UINT2NUM(GRPC_CALL_ERROR_ALREADY_FINISHED),
+               rb_str_new2("call is already finished"));
+  rb_hash_aset(rb_error_code_details,
+               UINT2NUM(GRPC_CALL_ERROR_TOO_MANY_OPERATIONS),
+               rb_str_new2("outstanding read or write present"));
+  rb_hash_aset(rb_error_code_details, UINT2NUM(GRPC_CALL_ERROR_INVALID_FLAGS),
+               rb_str_new2("a bad flag was given"));
+  rb_define_const(rb_RpcErrors, "ErrorMessages", rb_error_code_details);
+  rb_obj_freeze(rb_error_code_details);
+}
+
+void Init_google_rpc_call() {
+  /* CallError inherits from Exception to signal that it is non-recoverable */
+  rb_eCallError = rb_define_class_under(rb_mGoogleRPC, "CallError",
+                                        rb_eException);
+  rb_cCall = rb_define_class_under(rb_mGoogleRPC, "Call", rb_cObject);
+
+  /* Prevent allocation or inialization of the Call class */
+  rb_define_alloc_func(rb_cCall, grpc_rb_cannot_alloc);
+  rb_define_method(rb_cCall, "initialize", grpc_rb_cannot_init, 0);
+  rb_define_method(rb_cCall, "initialize_copy", grpc_rb_cannot_init_copy, 1);
+
+  /* Add ruby analogues of the Call methods. */
+  rb_define_method(rb_cCall, "accept", grpc_rb_call_accept, -1);
+  rb_define_method(rb_cCall, "add_metadata", grpc_rb_call_add_metadata,
+                   -1);
+  rb_define_method(rb_cCall, "cancel", grpc_rb_call_cancel, 0);
+  rb_define_method(rb_cCall, "start_invoke", grpc_rb_call_start_invoke, -1);
+  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);
+  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);
+  rb_define_method(rb_cCall, "metadata", grpc_rb_call_get_metadata, 0);
+  rb_define_method(rb_cCall, "metadata=", grpc_rb_call_set_metadata, 1);
+
+  /* Ids used to support call attributes */
+  id_metadata = rb_intern("metadata");
+  id_status = rb_intern("status");
+
+  /* Ids used by the c wrapping internals. */
+  id_cq = rb_intern("__cq");
+  id_flags = rb_intern("__flags");
+  id_input_md = rb_intern("__input_md");
+
+  /* The hash for reference counting calls, to ensure they can't be destroyed
+   * more than once */
+  hash_all_calls = rb_hash_new();
+  rb_define_const(rb_cCall, "INTERNAL_ALL_CALLs", hash_all_calls);
+
+  Init_google_rpc_error_codes();
+}
+
+/* Gets the call from the ruby object */
+grpc_call* grpc_rb_get_wrapped_call(VALUE v) {
+  grpc_call *c = NULL;
+  Data_Get_Struct(v, grpc_call, c);
+  return c;
+}
+
+/* Obtains the wrapped object for a given call */
+VALUE grpc_rb_wrap_call(grpc_call* c) {
+  VALUE obj = Qnil;
+  if (c == NULL) {
+    return Qnil;
+  }
+  obj = rb_hash_aref(hash_all_calls, OFFT2NUM((VALUE)c));
+  if (obj == Qnil) {  /* Not in the hash add it */
+    rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)c), UINT2NUM(1));
+  } else {
+    rb_hash_aset(hash_all_calls, OFFT2NUM((VALUE)c),
+                 UINT2NUM(NUM2UINT(obj) + 1));
+  }
+  return Data_Wrap_Struct(rb_cCall, GC_NOT_MARKED, grpc_rb_call_destroy,
+                          c);
+}
diff --git a/src/ruby/ext/grpc/rb_call.h b/src/ruby/ext/grpc/rb_call.h
new file mode 100644
index 0000000..422e7e7
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_call.h
@@ -0,0 +1,59 @@
+/*
+ *
+ * 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_CALL_H_
+#define GRPC_RB_CALL_H_
+
+#include <grpc/grpc.h>
+#include <ruby.h>
+
+/* Gets the wrapped call from a VALUE. */
+grpc_call* grpc_rb_get_wrapped_call(VALUE v);
+
+/* Gets the VALUE corresponding to given grpc_call. */
+VALUE grpc_rb_wrap_call(grpc_call* c);
+
+/* Provides the details of an call error */
+const char* grpc_call_error_detail_of(grpc_call_error err);
+
+/* rb_cCall is the Call class whose instances proxy grpc_call. */
+extern VALUE rb_cCall;
+
+/* rb_cCallError is the ruby class of the exception thrown during call
+   operations. */
+extern VALUE rb_eCallError;
+
+/* Initializes the Call class. */
+void Init_google_rpc_call();
+
+#endif  /* GRPC_RB_CALL_H_ */
diff --git a/src/ruby/ext/grpc/rb_channel.c b/src/ruby/ext/grpc/rb_channel.c
new file mode 100644
index 0000000..f4c09a3
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_channel.c
@@ -0,0 +1,235 @@
+/*
+ *
+ * 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_channel.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include "rb_grpc.h"
+#include "rb_call.h"
+#include "rb_channel_args.h"
+#include "rb_completion_queue.h"
+#include "rb_server.h"
+
+/* id_channel is the name of the hidden ivar that preserves a reference to the
+ * channel on a call, so that calls are not GCed before their channel.  */
+static ID id_channel;
+
+/* id_target is the name of the hidden ivar that preserves a reference to the
+ * target string used to create the call, preserved so that is does not get
+ * GCed before the channel */
+static ID id_target;
+
+/* Used during the conversion of a hash to channel args during channel setup */
+static VALUE rb_cChannelArgs;
+
+/* grpc_rb_channel wraps a grpc_channel.  It provides a peer ruby object,
+ * 'mark' to minimize copying when a channel is created from ruby. */
+typedef struct grpc_rb_channel {
+  /* Holder of ruby objects involved in constructing the channel */
+  VALUE mark;
+  /* The actual channel */
+  grpc_channel *wrapped;
+} grpc_rb_channel;
+
+/* Destroys Channel instances. */
+static void grpc_rb_channel_free(void *p) {
+  grpc_rb_channel *ch = NULL;
+  if (p == NULL) {
+    return;
+  };
+  ch = (grpc_rb_channel *)p;
+
+  /* Deletes the wrapped object if the mark object is Qnil, which indicates
+   * that no other object is the actual owner. */
+  if (ch->wrapped != NULL && ch->mark == Qnil) {
+    grpc_channel_destroy(ch->wrapped);
+    rb_warning("channel gc: destroyed the c channel");
+  } else {
+    rb_warning("channel gc: did not destroy the c channel");
+  }
+
+  xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_channel_mark(void *p) {
+  grpc_rb_channel *channel = NULL;
+  if (p == NULL) {
+    return;
+  }
+  channel = (grpc_rb_channel *)p;
+  if (channel->mark != Qnil) {
+    rb_gc_mark(channel->mark);
+  }
+}
+
+/* Allocates grpc_rb_channel instances. */
+static VALUE grpc_rb_channel_alloc(VALUE cls) {
+  grpc_rb_channel *wrapper = ALLOC(grpc_rb_channel);
+  wrapper->wrapped = NULL;
+  wrapper->mark = Qnil;
+  return Data_Wrap_Struct(cls, grpc_rb_channel_mark, grpc_rb_channel_free,
+                          wrapper);
+}
+
+/* Initializes channel instances */
+static VALUE grpc_rb_channel_init(VALUE self, VALUE target,
+                                  VALUE channel_args) {
+  grpc_rb_channel *wrapper = NULL;
+  grpc_channel *ch = NULL;
+  char *target_chars = StringValueCStr(target);
+  grpc_channel_args args;
+  MEMZERO(&args, grpc_channel_args, 1);
+
+  Data_Get_Struct(self, grpc_rb_channel, wrapper);
+  grpc_rb_hash_convert_to_channel_args(channel_args, &args);
+  ch = grpc_channel_create(target_chars, &args);
+  if (args.args != NULL) {
+    xfree(args.args);   /* Allocated by grpc_rb_hash_convert_to_channel_args */
+  }
+  if (ch == NULL) {
+    rb_raise(rb_eRuntimeError, "could not create an rpc channel to target:%s",
+             target_chars);
+  }
+  rb_ivar_set(self, id_target, target);
+  wrapper->wrapped = ch;
+  return self;
+}
+
+/* Clones Channel instances.
+
+   Gives Channel a consistent implementation of Ruby's object copy/dup
+   protocol. */
+static VALUE grpc_rb_channel_init_copy(VALUE copy, VALUE orig) {
+  grpc_rb_channel *orig_ch = NULL;
+  grpc_rb_channel *copy_ch = NULL;
+
+  if (copy == orig) {
+    return copy;
+  }
+
+  /* Raise an error if orig is not a channel object or a subclass. */
+  if (TYPE(orig) != T_DATA ||
+      RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_channel_free) {
+    rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cChannel));
+  }
+
+  Data_Get_Struct(orig, grpc_rb_channel, orig_ch);
+  Data_Get_Struct(copy, grpc_rb_channel, copy_ch);
+
+  /* use ruby's MEMCPY to make a byte-for-byte copy of the channel wrapper
+   * object. */
+  MEMCPY(copy_ch, orig_ch, grpc_rb_channel, 1);
+  return copy;
+}
+
+/* Create a call given a grpc_channel, in order to call method. The request
+   is not sent until grpc_call_invoke is called. */
+static VALUE grpc_rb_channel_create_call(VALUE self, VALUE method, VALUE host,
+                                         VALUE deadline) {
+  VALUE res = Qnil;
+  grpc_rb_channel *wrapper = NULL;
+  grpc_channel *ch = NULL;
+  grpc_call *call = NULL;
+  char *method_chars = StringValueCStr(method);
+  char *host_chars = StringValueCStr(host);
+
+  Data_Get_Struct(self, grpc_rb_channel, wrapper);
+  ch = wrapper->wrapped;
+  if (ch == NULL) {
+    rb_raise(rb_eRuntimeError, "closed!");
+  }
+
+  call = grpc_channel_create_call(ch, method_chars, host_chars,
+                                  grpc_rb_time_timeval(deadline,
+                                                       /* absolute time */ 0));
+  if (call == NULL) {
+    rb_raise(rb_eRuntimeError, "cannot create call with method %s",
+             method_chars);
+  }
+  res = grpc_rb_wrap_call(call);
+
+  /* Make this channel an instance attribute of the call so that is is not GCed
+   * before the call. */
+  rb_ivar_set(res, id_channel, self);
+  return res;
+}
+
+/* Closes the channel, calling it's destroy method */
+static VALUE grpc_rb_channel_destroy(VALUE self) {
+  grpc_rb_channel *wrapper = NULL;
+  grpc_channel *ch = NULL;
+
+  Data_Get_Struct(self, grpc_rb_channel, wrapper);
+  ch = wrapper->wrapped;
+  if (ch != NULL) {
+    grpc_channel_destroy(ch);
+    wrapper->wrapped = NULL;
+    wrapper->mark = Qnil;
+  }
+
+  return Qnil;
+}
+
+/* rb_cChannel is the ruby class that proxies grpc_channel. */
+VALUE rb_cChannel = Qnil;
+
+void Init_google_rpc_channel() {
+  rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject);
+  rb_cChannel = rb_define_class_under(rb_mGoogleRPC, "Channel", rb_cObject);
+
+  /* Allocates an object managed by the ruby runtime */
+  rb_define_alloc_func(rb_cChannel, grpc_rb_channel_alloc);
+
+  /* Provides a ruby constructor and support for dup/clone. */
+  rb_define_method(rb_cChannel, "initialize", grpc_rb_channel_init, 2);
+  rb_define_method(rb_cChannel, "initialize_copy", grpc_rb_channel_init_copy,
+                   1);
+
+  /* Add ruby analogues of the Channel methods. */
+  rb_define_method(rb_cChannel, "create_call", grpc_rb_channel_create_call, 3);
+  rb_define_method(rb_cChannel, "destroy", grpc_rb_channel_destroy, 0);
+  rb_define_alias(rb_cChannel, "close", "destroy");
+
+  id_channel = rb_intern("__channel");
+  id_target = rb_intern("__target");
+}
+
+/* Gets the wrapped channel from the ruby wrapper */
+grpc_channel* grpc_rb_get_wrapped_channel(VALUE v) {
+  grpc_rb_channel *wrapper = NULL;
+  Data_Get_Struct(v, grpc_rb_channel, wrapper);
+  return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_channel.h b/src/ruby/ext/grpc/rb_channel.h
new file mode 100644
index 0000000..b0a3634
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_channel.h
@@ -0,0 +1,49 @@
+/*
+ *
+ * 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_CHANNEL_H_
+#define GRPC_RB_CHANNEL_H_
+
+#include <ruby.h>
+#include <grpc/grpc.h>
+
+/* rb_cChannel is the Channel class whose instances proxy grpc_channel. */
+extern VALUE rb_cChannel;
+
+/* Initializes the Channel class. */
+void Init_google_rpc_channel();
+
+/* Gets the wrapped channel from the ruby wrapper */
+grpc_channel* grpc_rb_get_wrapped_channel(VALUE v);
+
+#endif  /* GRPC_RB_CHANNEL_H_ */
diff --git a/src/ruby/ext/grpc/rb_channel_args.c b/src/ruby/ext/grpc/rb_channel_args.c
new file mode 100644
index 0000000..eebced0
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_channel_args.c
@@ -0,0 +1,157 @@
+/*
+ *
+ * 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_channel_args.h"
+
+#include <ruby.h>
+#include <grpc/grpc.h>
+
+#include "rb_grpc.h"
+
+/* A callback the processes the hash key values in channel_args hash */
+static int grpc_rb_channel_create_in_process_add_args_hash_cb(VALUE key,
+                                                              VALUE val,
+                                                              VALUE args_obj) {
+  const char* the_key;
+  grpc_channel_args* args;
+
+  switch (TYPE(key)) {
+
+    case T_STRING:
+      the_key = StringValuePtr(key);
+      break;
+
+    case T_SYMBOL:
+      the_key = rb_id2name(SYM2ID(key));
+      break;
+
+    default:
+      rb_raise(rb_eTypeError, "bad chan arg: got <%s>, want <String|Symbol>",
+               rb_obj_classname(key));
+      return ST_STOP;
+  }
+
+  Data_Get_Struct(args_obj, grpc_channel_args, args);
+  if (args->num_args <= 0) {
+    rb_raise(rb_eRuntimeError, "hash_cb bug: num_args is %lu for key:%s",
+             args->num_args, StringValueCStr(key));
+    return ST_STOP;
+  }
+
+  args->args[args->num_args - 1].key = (char *)the_key;
+  switch (TYPE(val)) {
+
+    case T_SYMBOL:
+      args->args[args->num_args - 1].type = GRPC_ARG_STRING;
+      args->args[args->num_args - 1].value.string =
+          (char *)rb_id2name(SYM2ID(val));
+      --args->num_args;
+      return ST_CONTINUE;
+
+    case T_STRING:
+      args->args[args->num_args - 1].type = GRPC_ARG_STRING;
+      args->args[args->num_args - 1].value.string = StringValueCStr(val);
+      --args->num_args;
+      return ST_CONTINUE;
+
+    case T_FIXNUM:
+      args->args[args->num_args - 1].type = GRPC_ARG_INTEGER;
+      args->args[args->num_args - 1].value.integer = NUM2INT(val);
+      --args->num_args;
+      return ST_CONTINUE;
+
+    default:
+      rb_raise(rb_eTypeError, "%s: bad value: got <%s>, want <String|Fixnum>",
+               StringValueCStr(key), rb_obj_classname(val));
+      return ST_STOP;
+  }
+  rb_raise(rb_eRuntimeError, "impl bug: hash_cb reached to far while on key:%s",
+           StringValueCStr(key));
+  return ST_STOP;
+}
+
+/* channel_convert_params allows the call to
+   grpc_rb_hash_convert_to_channel_args to be made within an rb_protect
+   exception-handler.  This allows any allocated memory to be freed before
+   propagating any exception that occurs */
+typedef struct channel_convert_params {
+  VALUE src_hash;
+  grpc_channel_args* dst;
+} channel_convert_params;
+
+
+static VALUE grpc_rb_hash_convert_to_channel_args0(VALUE as_value) {
+  ID id_size = rb_intern("size");
+  VALUE rb_cChannelArgs = rb_define_class("TmpChannelArgs", rb_cObject);
+  channel_convert_params* params = (channel_convert_params *)as_value;
+  size_t num_args = 0;
+
+  if (!NIL_P(params->src_hash) && TYPE(params->src_hash) != T_HASH) {
+    rb_raise(rb_eTypeError, "bad channel args: got:<%s> want: a hash or nil",
+             rb_obj_classname(params->src_hash));
+    return Qnil;
+  }
+
+  if (TYPE(params->src_hash) == T_HASH) {
+    num_args = NUM2INT(rb_funcall(params->src_hash, id_size, 0));
+    params->dst->num_args = num_args;
+    params->dst->args = ALLOC_N(grpc_arg, num_args);
+    MEMZERO(params->dst->args, grpc_arg, num_args);
+    rb_hash_foreach(params->src_hash,
+                    grpc_rb_channel_create_in_process_add_args_hash_cb,
+                    Data_Wrap_Struct(rb_cChannelArgs, GC_NOT_MARKED,
+                                     GC_DONT_FREE, params->dst));
+    /* reset num_args as grpc_rb_channel_create_in_process_add_args_hash_cb
+     * decrements it during has processing */
+    params->dst->num_args = num_args;
+  }
+  return Qnil;
+}
+
+void grpc_rb_hash_convert_to_channel_args(VALUE src_hash,
+                                          grpc_channel_args* dst) {
+  channel_convert_params params;
+  int status = 0;
+
+  /* Make a protected call to grpc_rb_hash_convert_channel_args */
+  params.src_hash = src_hash;
+  params.dst = dst;
+  rb_protect(grpc_rb_hash_convert_to_channel_args0, (VALUE) &params, &status);
+  if (status != 0) {
+    if (dst->args != NULL) {
+      /* Free any allocated memory before propagating the error */
+      xfree(dst->args);
+    }
+    rb_jump_tag(status);
+  }
+}
diff --git a/src/ruby/ext/grpc/rb_channel_args.h b/src/ruby/ext/grpc/rb_channel_args.h
new file mode 100644
index 0000000..bbff017
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_channel_args.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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_CHANNEL_ARGS_H_
+#define GRPC_RB_CHANNEL_ARGS_H_
+
+#include <ruby.h>
+#include <grpc/grpc.h>
+
+/* Converts a hash object containing channel args to a channel args instance.
+ *
+ * This func ALLOCs args->args.  The caller is responsible for freeing it.  If
+ * a ruby error is raised during processing of the hash values, the func takes
+ * care to deallocate any memory allocated so far, and propagate the error.
+ *
+ * @param src_hash A ruby hash
+ * @param dst the grpc_channel_args that the hash entries will be added to.
+ */
+void grpc_rb_hash_convert_to_channel_args(VALUE src_hash,
+                                          grpc_channel_args* dst);
+
+
+#endif  /* GRPC_RB_CHANNEL_ARGS_H_ */
diff --git a/src/ruby/ext/grpc/rb_completion_queue.c b/src/ruby/ext/grpc/rb_completion_queue.c
new file mode 100644
index 0000000..62d045e
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_completion_queue.c
@@ -0,0 +1,194 @@
+/*
+ *
+ * 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_completion_queue.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/time.h>
+#include "rb_grpc.h"
+#include "rb_event.h"
+
+/* Used to allow grpc_completion_queue_next call to release the GIL */
+typedef struct next_call_stack {
+  grpc_completion_queue *cq;
+  grpc_event *event;
+  gpr_timespec timeout;
+  void* tag;
+} next_call_stack;
+
+/* Calls grpc_completion_queue_next without holding the ruby GIL */
+static void *grpc_rb_completion_queue_next_no_gil(
+    next_call_stack *next_call) {
+  next_call->event = grpc_completion_queue_next(next_call->cq,
+                                                next_call->timeout);
+  return NULL;
+}
+
+/* Calls grpc_completion_queue_pluck without holding the ruby GIL */
+static void *grpc_rb_completion_queue_pluck_no_gil(
+    next_call_stack *next_call) {
+  next_call->event = grpc_completion_queue_pluck(next_call->cq,
+                                                 next_call->tag,
+                                                 next_call->timeout);
+  return NULL;
+}
+
+
+/* Shuts down and drains the completion queue if necessary.
+ *
+ * This is done when the ruby completion queue object is about to be GCed.
+ */
+static void grpc_rb_completion_queue_shutdown_drain(
+    grpc_completion_queue* cq) {
+  next_call_stack next_call;
+  grpc_completion_type type;
+  int drained = 0;
+  MEMZERO(&next_call, next_call_stack, 1);
+
+  grpc_completion_queue_shutdown(cq);
+  next_call.cq = cq;
+  next_call.event = NULL;
+  /* TODO(temiola): the timeout should be a module level constant that defaults
+   * to gpr_inf_future.
+   *
+   * - at the moment this does not work, it stalls.  Using a small timeout like
+   *   this one works, and leads to fast test run times; a longer timeout was
+   *   causing unnecessary delays in the test runs.
+   *
+   * - investigate further, this is probably another example of C-level cleanup
+   * not working consistently in all cases.
+   */
+  next_call.timeout = gpr_time_add(gpr_now(), gpr_time_from_micros(5e3));
+  do {
+    rb_thread_call_without_gvl(grpc_rb_completion_queue_next_no_gil,
+                               (void *)&next_call, NULL, NULL);
+    if (next_call.event == NULL) {
+      break;
+    }
+    type = next_call.event->type;
+    if (type != GRPC_QUEUE_SHUTDOWN) {
+      ++drained;
+      rb_warning("completion queue shutdown: %d undrained events", drained);
+    }
+    grpc_event_finish(next_call.event);
+    next_call.event = NULL;
+  } while (type != GRPC_QUEUE_SHUTDOWN);
+}
+
+/* Helper function to free a completion queue. */
+static void grpc_rb_completion_queue_destroy(void *p) {
+  grpc_completion_queue *cq = NULL;
+  if (p == NULL) {
+    return;
+  }
+  cq = (grpc_completion_queue *)p;
+  grpc_rb_completion_queue_shutdown_drain(cq);
+  grpc_completion_queue_destroy(cq);
+}
+
+/* Allocates a completion queue. */
+static VALUE grpc_rb_completion_queue_alloc(VALUE cls) {
+  grpc_completion_queue* cq = grpc_completion_queue_create();
+  if (cq == NULL) {
+    rb_raise(rb_eArgError,
+             "could not create a completion queue: not sure why");
+  }
+  return Data_Wrap_Struct(cls, GC_NOT_MARKED,
+                          grpc_rb_completion_queue_destroy, cq);
+}
+
+/* Blocks until the next event is available, and returns the event. */
+static VALUE grpc_rb_completion_queue_next(VALUE self, VALUE timeout) {
+  next_call_stack next_call;
+  MEMZERO(&next_call, next_call_stack, 1);
+  Data_Get_Struct(self, grpc_completion_queue, next_call.cq);
+  next_call.timeout = grpc_rb_time_timeval(timeout, /* absolute time*/ 0);
+  next_call.event = NULL;
+  rb_thread_call_without_gvl(grpc_rb_completion_queue_next_no_gil,
+                             (void *)&next_call, NULL, NULL);
+  if (next_call.event == NULL) {
+    return Qnil;
+  }
+  return Data_Wrap_Struct(rb_cEvent, GC_NOT_MARKED, grpc_rb_event_finish,
+                          next_call.event);
+}
+
+/* Blocks until the next event for given tag is available, and returns the
+ * event. */
+static VALUE grpc_rb_completion_queue_pluck(VALUE self, VALUE tag,
+                                            VALUE timeout) {
+  next_call_stack next_call;
+  MEMZERO(&next_call, next_call_stack, 1);
+  Data_Get_Struct(self, grpc_completion_queue, next_call.cq);
+  next_call.timeout = grpc_rb_time_timeval(timeout, /* absolute time*/ 0);
+  next_call.tag = ROBJECT(tag);
+  next_call.event = NULL;
+  rb_thread_call_without_gvl(grpc_rb_completion_queue_pluck_no_gil,
+                             (void *)&next_call, NULL, NULL);
+  if (next_call.event == NULL) {
+    return Qnil;
+  }
+  return Data_Wrap_Struct(rb_cEvent, GC_NOT_MARKED, grpc_rb_event_finish,
+                          next_call.event);
+}
+
+/* rb_cCompletionQueue is the ruby class that proxies grpc_completion_queue. */
+VALUE rb_cCompletionQueue = Qnil;
+
+void Init_google_rpc_completion_queue() {
+  rb_cCompletionQueue = rb_define_class_under(rb_mGoogleRPC,
+                                              "CompletionQueue",
+                                              rb_cObject);
+
+  /* constructor: uses an alloc func without an initializer. Using a simple
+     alloc func works here as the grpc header does not specify any args for
+     this func, so no separate initialization step is necessary. */
+  rb_define_alloc_func(rb_cCompletionQueue, grpc_rb_completion_queue_alloc);
+
+  /* Add the next method that waits for the next event. */
+  rb_define_method(rb_cCompletionQueue, "next",
+                   grpc_rb_completion_queue_next, 1);
+
+  /* Add the pluck method that waits for the next event of given tag */
+  rb_define_method(rb_cCompletionQueue, "pluck",
+                   grpc_rb_completion_queue_pluck, 2);
+}
+
+/* Gets the wrapped completion queue from the ruby wrapper */
+grpc_completion_queue* grpc_rb_get_wrapped_completion_queue(VALUE v) {
+  grpc_completion_queue *cq = NULL;
+  Data_Get_Struct(v, grpc_completion_queue, cq);
+  return cq;
+}
diff --git a/src/ruby/ext/grpc/rb_completion_queue.h b/src/ruby/ext/grpc/rb_completion_queue.h
new file mode 100644
index 0000000..1ec2718
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_completion_queue.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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_COMPLETION_QUEUE_H_
+#define GRPC_RB_COMPLETION_QUEUE_H_
+
+#include <grpc/grpc.h>
+#include <ruby.h>
+
+/* Gets the wrapped completion queue from the ruby wrapper */
+grpc_completion_queue *grpc_rb_get_wrapped_completion_queue(VALUE v);
+
+/* rb_cCompletionQueue is the CompletionQueue class whose instances proxy
+   grpc_completion_queue. */
+extern VALUE rb_cCompletionQueue;
+
+/* Initializes the CompletionQueue class. */
+void Init_google_rpc_completion_queue();
+
+#endif  /* GRPC_RB_COMPLETION_QUEUE_H_ */
diff --git a/src/ruby/ext/grpc/rb_event.c b/src/ruby/ext/grpc/rb_event.c
new file mode 100644
index 0000000..6f542f9
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_event.c
@@ -0,0 +1,284 @@
+/*
+ *
+ * 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_event.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include "rb_grpc.h"
+#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;
+
+/* Helper function to free an event. */
+void grpc_rb_event_finish(void *p) {
+  grpc_event_finish(p);
+}
+
+static VALUE grpc_rb_event_result(VALUE self);
+
+/* Obtains the type of an event. */
+static VALUE grpc_rb_event_type(VALUE self) {
+  grpc_event *event = NULL;
+  Data_Get_Struct(self, grpc_event, event);
+  switch (event->type) {
+    case GRPC_QUEUE_SHUTDOWN:
+      return rb_const_get(rb_mCompletionType, rb_intern("QUEUE_SHUTDOWN"));
+
+    case GRPC_READ:
+      return rb_const_get(rb_mCompletionType, rb_intern("READ"));
+
+    case GRPC_INVOKE_ACCEPTED:
+      grpc_rb_event_result(self);  /* validates the result */
+      return rb_const_get(rb_mCompletionType, rb_intern("INVOKE_ACCEPTED"));
+
+    case GRPC_WRITE_ACCEPTED:
+      grpc_rb_event_result(self);  /* validates the result */
+      return rb_const_get(rb_mCompletionType, rb_intern("WRITE_ACCEPTED"));
+
+    case GRPC_FINISH_ACCEPTED:
+      grpc_rb_event_result(self);  /* validates the result */
+      return rb_const_get(rb_mCompletionType, rb_intern("FINISH_ACCEPTED"));
+
+    case GRPC_CLIENT_METADATA_READ:
+      return rb_const_get(rb_mCompletionType,
+                          rb_intern("CLIENT_METADATA_READ"));
+
+    case GRPC_FINISHED:
+      return rb_const_get(rb_mCompletionType, rb_intern("FINISHED"));
+
+    case GRPC_SERVER_RPC_NEW:
+      return rb_const_get(rb_mCompletionType, rb_intern("SERVER_RPC_NEW"));
+
+    default:
+      rb_raise(rb_eRuntimeError,
+               "unrecognized event code for an rpc event:%d", event->type);
+  }
+  return Qnil; /* should not be reached */
+}
+
+/* Obtains the tag associated with an event. */
+static VALUE grpc_rb_event_tag(VALUE self) {
+  grpc_event *event = NULL;
+  Data_Get_Struct(self, grpc_event, event);
+  if (event->tag == NULL) {
+    return Qnil;
+  }
+  return (VALUE)event->tag;
+}
+
+/* Obtains the call associated with an event. */
+static VALUE grpc_rb_event_call(VALUE self) {
+  grpc_event *ev = NULL;
+  Data_Get_Struct(self, grpc_event, ev);
+  if (ev->call != NULL) {
+    return grpc_rb_wrap_call(ev->call);
+  }
+  return Qnil;
+}
+
+/* Obtains the metadata associated with an event. */
+static VALUE grpc_rb_event_metadata(VALUE self) {
+  grpc_event *event = NULL;
+  grpc_metadata *metadata = NULL;
+  VALUE key = Qnil;
+  VALUE new_ary = Qnil;
+  VALUE result = Qnil;
+  VALUE value = Qnil;
+  size_t count = 0;
+  size_t i = 0;
+
+  /* Figure out which metadata to read. */
+  Data_Get_Struct(self, grpc_event, event);
+  switch (event->type) {
+
+    case GRPC_CLIENT_METADATA_READ:
+      count = event->data.client_metadata_read.count;
+      metadata = event->data.client_metadata_read.elements;
+      break;
+
+    case GRPC_SERVER_RPC_NEW:
+      count = event->data.server_rpc_new.metadata_count;
+      metadata = event->data.server_rpc_new.metadata_elements;
+      break;
+
+    default:
+      rb_raise(rb_eRuntimeError,
+               "bug: bad event type reading server metadata. got %d; want %d",
+               event->type, GRPC_SERVER_RPC_NEW);
+      return Qnil;
+  }
+
+  result = rb_hash_new();
+  for (i = 0; i < count; i++) {
+    key = rb_str_new2(metadata[i].key);
+    value = rb_hash_aref(result, key);
+    if (value == Qnil) {
+      value = rb_str_new(
+          metadata[i].value,
+          metadata[i].value_length);
+      rb_hash_aset(result, key, value);
+    } else if (TYPE(value) == T_ARRAY) {
+      /* Add the string to the returned array */
+      rb_ary_push(value, rb_str_new(
+          metadata[i].value,
+          metadata[i].value_length));
+    } else {
+      /* Add the current value with this key and the new one to an array */
+      new_ary = rb_ary_new();
+      rb_ary_push(new_ary, value);
+      rb_ary_push(new_ary, rb_str_new(
+          metadata[i].value,
+          metadata[i].value_length));
+      rb_hash_aset(result, key, new_ary);
+    }
+  }
+  return result;
+}
+
+/* Obtains the data associated with an event. */
+static VALUE grpc_rb_event_result(VALUE self) {
+  grpc_event *event = NULL;
+  Data_Get_Struct(self, grpc_event, event);
+
+  switch (event->type) {
+
+    case GRPC_QUEUE_SHUTDOWN:
+      return Qnil;
+
+    case GRPC_READ:
+      return grpc_rb_byte_buffer_create_with_mark(self, event->data.read);
+
+    case GRPC_FINISH_ACCEPTED:
+      if (event->data.finish_accepted == GRPC_OP_OK) {
+        return Qnil;
+      }
+      rb_raise(rb_eEventError, "finish failed, not sure why (code=%d)",
+               event->data.finish_accepted);
+      break;
+
+    case GRPC_INVOKE_ACCEPTED:
+      if (event->data.invoke_accepted == GRPC_OP_OK) {
+        return Qnil;
+      }
+      rb_raise(rb_eEventError, "invoke failed, not sure why (code=%d)",
+               event->data.invoke_accepted);
+      break;
+
+    case GRPC_WRITE_ACCEPTED:
+      if (event->data.write_accepted == GRPC_OP_OK) {
+        return Qnil;
+      }
+      rb_raise(rb_eEventError, "write failed, not sure why (code=%d)",
+               event->data.invoke_accepted);
+      break;
+
+    case GRPC_CLIENT_METADATA_READ:
+      return grpc_rb_event_metadata(self);
+
+    case GRPC_FINISHED:
+      return grpc_rb_status_create_with_mark(self, &event->data.finished);
+      break;
+
+    case GRPC_SERVER_RPC_NEW:
+      return rb_struct_new(
+          rb_sNewServerRpc,
+          rb_str_new2(event->data.server_rpc_new.method),
+          rb_str_new2(event->data.server_rpc_new.host),
+          Data_Wrap_Struct(
+              rb_cTimeVal, GC_NOT_MARKED, GC_DONT_FREE,
+              (void *)&event->data.server_rpc_new.deadline),
+          grpc_rb_event_metadata(self),
+          NULL);
+
+    default:
+      rb_raise(rb_eRuntimeError,
+               "unrecognized event code for an rpc event:%d", event->type);
+  }
+
+  return Qfalse;
+}
+
+/* rb_sNewServerRpc is the struct that holds new server rpc details. */
+VALUE rb_sNewServerRpc = Qnil;
+
+/* rb_cEvent is the Event class whose instances proxy grpc_event */
+VALUE rb_cEvent = Qnil;
+
+/* rb_eEventError is the ruby class of the exception thrown on failures during
+   rpc event processing. */
+VALUE rb_eEventError = Qnil;
+
+void Init_google_rpc_event() {
+  rb_eEventError = rb_define_class_under(rb_mGoogleRPC, "EventError",
+                                         rb_eStandardError);
+  rb_cEvent = rb_define_class_under(rb_mGoogleRPC, "Event", rb_cObject);
+  rb_sNewServerRpc = rb_struct_define("NewServerRpc", "method", "host",
+                                      "deadline", "metadata", NULL);
+
+  /* Prevent allocation or inialization from ruby. */
+  rb_define_alloc_func(rb_cEvent, grpc_rb_cannot_alloc);
+  rb_define_method(rb_cEvent, "initialize", grpc_rb_cannot_init, 0);
+  rb_define_method(rb_cEvent, "initialize_copy", grpc_rb_cannot_init_copy, 1);
+
+  /* Accessors for the data available in an event. */
+  rb_define_method(rb_cEvent, "call", grpc_rb_event_call, 0);
+  rb_define_method(rb_cEvent, "result", grpc_rb_event_result, 0);
+  rb_define_method(rb_cEvent, "tag", grpc_rb_event_tag, 0);
+  rb_define_method(rb_cEvent, "type", grpc_rb_event_type, 0);
+
+  /* Constants representing the completion types */
+  rb_mCompletionType = rb_define_module_under(rb_mGoogleRPC, "CompletionType");
+  rb_define_const(rb_mCompletionType, "QUEUE_SHUTDOWN",
+                  INT2NUM(GRPC_QUEUE_SHUTDOWN));
+  rb_define_const(rb_mCompletionType, "READ", INT2NUM(GRPC_READ));
+  rb_define_const(rb_mCompletionType, "INVOKE_ACCEPTED",
+                  INT2NUM(GRPC_INVOKE_ACCEPTED));
+  rb_define_const(rb_mCompletionType, "WRITE_ACCEPTED",
+                  INT2NUM(GRPC_WRITE_ACCEPTED));
+  rb_define_const(rb_mCompletionType, "FINISH_ACCEPTED",
+                  INT2NUM(GRPC_FINISH_ACCEPTED));
+  rb_define_const(rb_mCompletionType, "CLIENT_METADATA_READ",
+                  INT2NUM(GRPC_CLIENT_METADATA_READ));
+  rb_define_const(rb_mCompletionType, "FINISHED",
+                  INT2NUM(GRPC_FINISHED));
+  rb_define_const(rb_mCompletionType, "SERVER_RPC_NEW",
+                  INT2NUM(GRPC_SERVER_RPC_NEW));
+  rb_define_const(rb_mCompletionType, "RESERVED",
+                  INT2NUM(GRPC_COMPLETION_DO_NOT_USE));
+}
diff --git a/src/ruby/ext/grpc/rb_event.h b/src/ruby/ext/grpc/rb_event.h
new file mode 100644
index 0000000..c398b6c
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_event.h
@@ -0,0 +1,55 @@
+/*
+ *
+ * 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_EVENT_H_
+#define GRPC_RB_EVENT_H_
+
+#include <ruby.h>
+
+/* rb_sNewServerRpc is the struct that holds new server rpc details. */
+extern VALUE rb_sNewServerRpc;
+
+/* rb_cEvent is the Event class whose instances proxy grpc_event. */
+extern VALUE rb_cEvent;
+
+/* rb_cEventError is the ruby class that acts the exception thrown during rpc
+   event processing. */
+extern VALUE rb_eEventError;
+
+/* Helper function to free an event. */
+void grpc_rb_event_finish(void *p);
+
+/* Initializes the Event and EventError classes. */
+void Init_google_rpc_event();
+
+#endif  /* GRPC_RB_EVENT_H_ */
diff --git a/src/ruby/ext/grpc/rb_grpc.c b/src/ruby/ext/grpc/rb_grpc.c
new file mode 100644
index 0000000..5cc45cf
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_grpc.c
@@ -0,0 +1,230 @@
+/*
+ *
+ * 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_grpc.h"
+
+#include <math.h>
+#include <ruby.h>
+#include <sys/time.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/time.h>
+#include "rb_byte_buffer.h"
+#include "rb_call.h"
+#include "rb_channel.h"
+#include "rb_completion_queue.h"
+#include "rb_event.h"
+#include "rb_metadata.h"
+#include "rb_server.h"
+#include "rb_status.h"
+
+/* Define common vars and funcs declared in rb.h */
+const RUBY_DATA_FUNC GC_NOT_MARKED = NULL;
+const RUBY_DATA_FUNC GC_DONT_FREE = NULL;
+
+VALUE rb_cTimeVal = Qnil;
+
+/* Alloc func that blocks allocation of a given object by raising an
+ * exception. */
+VALUE grpc_rb_cannot_alloc(VALUE cls) {
+  rb_raise(rb_eTypeError,
+           "allocation of %s only allowed from the gRPC native layer",
+           rb_class2name(cls));
+  return Qnil;
+}
+
+/* Init func that fails by raising an exception. */
+VALUE grpc_rb_cannot_init(VALUE self) {
+  rb_raise(rb_eTypeError,
+           "initialization of %s only allowed from the gRPC native layer",
+           rb_obj_classname(self));
+  return Qnil;
+}
+
+/* Init/Clone func that fails by raising an exception. */
+VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self) {
+  rb_raise(rb_eTypeError,
+           "initialization of %s only allowed from the gRPC native layer",
+           rb_obj_classname(copy));
+  return Qnil;
+}
+
+/* id_tv_{,u}sec are accessor methods on Ruby Time instances. */
+static ID id_tv_sec;
+static ID id_tv_nsec;
+
+/**
+ * grpc_rb_time_timeval creates a time_eval from a ruby time object.
+ *
+ * This func is copied from ruby source, MRI/source/time.c, which is published
+ * under the same license as the ruby.h, on which the entire extensions is
+ * based.
+ */
+gpr_timespec grpc_rb_time_timeval(VALUE time, int interval) {
+  gpr_timespec t;
+  gpr_timespec *time_const;
+  const char *tstr = interval ? "time interval" : "time";
+  const char *want = " want <secs from epoch>|<Time>|<GRPC::TimeConst.*>";
+
+  switch (TYPE(time)) {
+
+    case T_DATA:
+      if (CLASS_OF(time) == rb_cTimeVal) {
+        Data_Get_Struct(time, gpr_timespec, time_const);
+        t = *time_const;
+      } else if (CLASS_OF(time) == rb_cTime) {
+        t.tv_sec =  NUM2INT(rb_funcall(time, id_tv_sec, 0));
+        t.tv_nsec = NUM2INT(rb_funcall(time, id_tv_nsec, 0));
+      } else {
+        rb_raise(rb_eTypeError,
+                 "bad input: (%s)->c_timeval, got <%s>,%s",
+                 tstr, rb_obj_classname(time), want);
+      }
+      break;
+
+    case T_FIXNUM:
+      t.tv_sec = FIX2LONG(time);
+      if (interval && t.tv_sec < 0)
+        rb_raise(rb_eArgError, "%s must be positive", tstr);
+      t.tv_nsec = 0;
+      break;
+
+    case T_FLOAT:
+      if (interval && RFLOAT(time)->float_value < 0.0)
+        rb_raise(rb_eArgError, "%s must be positive", tstr);
+      else {
+        double f, d;
+
+        d = modf(RFLOAT(time)->float_value, &f);
+        if (d < 0) {
+          d += 1;
+          f -= 1;
+        }
+        t.tv_sec = (time_t)f;
+        if (f != t.tv_sec) {
+          rb_raise(rb_eRangeError, "%f out of Time range",
+                   RFLOAT(time)->float_value);
+        }
+        t.tv_nsec = (time_t)(d*1e9+0.5);
+      }
+      break;
+
+    case T_BIGNUM:
+      t.tv_sec = NUM2LONG(time);
+      if (interval && t.tv_sec < 0)
+        rb_raise(rb_eArgError, "%s must be positive", tstr);
+      t.tv_nsec = 0;
+      break;
+
+    default:
+      rb_raise(rb_eTypeError,
+               "bad input: (%s)->c_timeval, got <%s>,%s",
+               tstr, rb_obj_classname(time), want);
+      break;
+  }
+  return t;
+}
+
+/* id_at is the constructor method of the ruby standard Time class. */
+static ID id_at;
+
+/* id_inspect is the inspect method found on various ruby objects. */
+static ID id_inspect;
+
+/* id_to_s is the to_s method found on various ruby objects. */
+static ID id_to_s;
+
+/* Converts `a wrapped time constant to a standard time. */
+VALUE grpc_rb_time_val_to_time(VALUE self) {
+  gpr_timespec *time_const = NULL;
+  Data_Get_Struct(self, gpr_timespec, time_const);
+  return rb_funcall(rb_cTime, id_at, 2, INT2NUM(time_const->tv_sec),
+                    INT2NUM(time_const->tv_nsec));
+}
+
+/* Invokes inspect on the ctime version of the time val. */
+VALUE grpc_rb_time_val_inspect(VALUE self) {
+  return rb_funcall(grpc_rb_time_val_to_time(self), id_inspect, 0);
+}
+
+/* Invokes to_s on the ctime version of the time val. */
+VALUE grpc_rb_time_val_to_s(VALUE self) {
+  return rb_funcall(grpc_rb_time_val_to_time(self), id_to_s, 0);
+}
+
+/* Adds a module with constants that map to gpr's static timeval structs. */
+void Init_google_time_consts() {
+  VALUE rb_mTimeConsts = rb_define_module_under(rb_mGoogleRPC, "TimeConsts");
+  rb_cTimeVal = rb_define_class_under(rb_mGoogleRPC, "TimeSpec", rb_cObject);
+  rb_define_const(rb_mTimeConsts, "ZERO",
+                  Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED,
+                                   GC_DONT_FREE, (void *)&gpr_time_0));
+  rb_define_const(rb_mTimeConsts, "INFINITE_FUTURE",
+                  Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED,
+                                   GC_DONT_FREE, (void *)&gpr_inf_future));
+  rb_define_const(rb_mTimeConsts, "INFINITE_PAST",
+                  Data_Wrap_Struct(rb_cTimeVal, GC_NOT_MARKED,
+                                   GC_DONT_FREE, (void *)&gpr_inf_past));
+  rb_define_method(rb_cTimeVal, "to_time", grpc_rb_time_val_to_time, 0);
+  rb_define_method(rb_cTimeVal, "inspect", grpc_rb_time_val_inspect, 0);
+  rb_define_method(rb_cTimeVal, "to_s", grpc_rb_time_val_to_s, 0);
+  id_at = rb_intern("at");
+  id_inspect = rb_intern("inspect");
+  id_to_s = rb_intern("to_s");
+  id_tv_sec = rb_intern("tv_sec");
+  id_tv_nsec = rb_intern("tv_nsec");
+}
+
+void grpc_rb_shutdown(void *vm) {
+  grpc_shutdown();
+}
+
+/* Initialize the Google RPC module. */
+VALUE rb_mGoogle = Qnil;
+VALUE rb_mGoogleRPC = Qnil;
+void Init_grpc() {
+  grpc_init();
+  ruby_vm_at_exit(grpc_rb_shutdown);
+  rb_mGoogle = rb_define_module("Google");
+  rb_mGoogleRPC = rb_define_module_under(rb_mGoogle, "RPC");
+
+  Init_google_rpc_byte_buffer();
+  Init_google_rpc_event();
+  Init_google_rpc_channel();
+  Init_google_rpc_completion_queue();
+  Init_google_rpc_call();
+  Init_google_rpc_metadata();
+  Init_google_rpc_server();
+  Init_google_rpc_status();
+  Init_google_time_consts();
+}
diff --git a/src/ruby/ext/grpc/rb_grpc.h b/src/ruby/ext/grpc/rb_grpc.h
new file mode 100644
index 0000000..fd43c37
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_grpc.h
@@ -0,0 +1,71 @@
+/*
+ *
+ * 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_H_
+#define GRPC_RB_H_
+
+#include <sys/time.h>
+#include <ruby.h>
+#include <grpc/support/time.h>
+
+/* rb_mGoogle is the top-level Google module. */
+extern VALUE rb_mGoogle;
+
+/* rb_mGoogleRPC is the module containing all the ruby wrapper GRPC classes. */
+extern VALUE rb_mGoogleRPC;
+
+/* Class used to wrap timeval structs. */
+extern VALUE rb_cTimeVal;
+
+/* GC_NOT_MARKED is used in calls to Data_Wrap_Struct to indicate that the
+   wrapped struct does not need to participate in ruby gc. */
+extern const RUBY_DATA_FUNC GC_NOT_MARKED;
+
+/* GC_DONT_FREED is used in calls to Data_Wrap_Struct to indicate that the
+   wrapped struct should not be freed the wrapped ruby object is released by
+   the garbage collector. */
+extern const RUBY_DATA_FUNC GC_DONT_FREE;
+
+/* A ruby object alloc func that fails by raising an exception. */
+VALUE grpc_rb_cannot_alloc(VALUE cls);
+
+/* A ruby object init func that fails by raising an exception. */
+VALUE grpc_rb_cannot_init(VALUE self);
+
+/* A ruby object clone init func that fails by raising an exception. */
+VALUE grpc_rb_cannot_init_copy(VALUE copy, VALUE self);
+
+/* grpc_rb_time_timeval creates a gpr_timespec from a ruby time object. */
+gpr_timespec grpc_rb_time_timeval(VALUE time, int interval);
+
+#endif  /* GRPC_RB_H_ */
diff --git a/src/ruby/ext/grpc/rb_metadata.c b/src/ruby/ext/grpc/rb_metadata.c
new file mode 100644
index 0000000..13d515a
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_metadata.c
@@ -0,0 +1,215 @@
+/*
+ *
+ * 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_metadata.h"
+
+#include <ruby.h>
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include "rb_grpc.h"
+
+/* grpc_rb_metadata wraps a grpc_metadata.  It provides a peer ruby object,
+ * 'mark' to minimize copying when a metadata is created from ruby. */
+typedef struct grpc_rb_metadata {
+  /* Holder of ruby objects involved in constructing the metadata */
+  VALUE mark;
+  /* The actual metadata */
+  grpc_metadata *wrapped;
+} grpc_rb_metadata;
+
+
+/* Destroys Metadata instances. */
+static void grpc_rb_metadata_free(void *p) {
+  if (p == NULL) {
+    return;
+  };
+
+  /* Because metadata is only created during a call to grpc_call_add_metadata,
+   * and the call takes ownership of the metadata, this does not free the
+   * wrapped struct, only the wrapper */
+  xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_metadata_mark(void *p) {
+  grpc_rb_metadata *md = NULL;
+  if (p == NULL) {
+    return;
+  }
+
+  md = (grpc_rb_metadata *)p;
+  /* If it's not already cleaned up, mark the mark object */
+  if (md->mark != Qnil && BUILTIN_TYPE(md->mark) != T_NONE) {
+    rb_gc_mark(md->mark);
+  }
+}
+
+/* Allocates Metadata instances.
+
+   Provides safe default values for the Metadata fields. */
+static VALUE grpc_rb_metadata_alloc(VALUE cls) {
+  grpc_rb_metadata *wrapper = ALLOC(grpc_rb_metadata);
+  wrapper->wrapped = NULL;
+  wrapper->mark = Qnil;
+  return Data_Wrap_Struct(cls, grpc_rb_metadata_mark, grpc_rb_metadata_free,
+                          wrapper);
+}
+
+/* id_key and id_value are the names of the hidden ivars that preserve the
+ * original byte_buffer source string */
+static ID id_key;
+static ID id_value;
+
+/* Initializes Metadata instances. */
+static VALUE grpc_rb_metadata_init(VALUE self, VALUE key, VALUE value) {
+  grpc_rb_metadata *wrapper = NULL;
+  grpc_metadata *md = ALLOC(grpc_metadata);
+
+  /* Use direct pointers to the strings wrapped by the ruby object to avoid
+   * copying */
+  Data_Get_Struct(self, grpc_rb_metadata, wrapper);
+  wrapper->wrapped = md;
+  if (TYPE(key) == T_SYMBOL) {
+    md->key = (char *)rb_id2name(SYM2ID(key));
+  } else {  /* StringValueCStr does all other type exclusions for us */
+    md->key = StringValueCStr(key);
+  }
+  md->value = RSTRING_PTR(value);
+  md->value_length = RSTRING_LEN(value);
+
+  /* Save references to the original values on the mark object so that the
+   * pointers used there are valid for the lifetime of the object. */
+  wrapper->mark = rb_class_new_instance(0, NULL, rb_cObject);
+  rb_ivar_set(wrapper->mark, id_key, key);
+  rb_ivar_set(wrapper->mark, id_value, value);
+
+  return self;
+}
+
+/* Clones Metadata instances.
+
+   Gives Metadata a consistent implementation of Ruby's object copy/dup
+   protocol. */
+static VALUE grpc_rb_metadata_init_copy(VALUE copy, VALUE orig) {
+  grpc_rb_metadata *orig_md = NULL;
+  grpc_rb_metadata *copy_md = NULL;
+
+  if (copy == orig) {
+    return copy;
+  }
+
+  /* Raise an error if orig is not a metadata object or a subclass. */
+  if (TYPE(orig) != T_DATA ||
+      RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_metadata_free) {
+    rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cMetadata));
+  }
+
+  Data_Get_Struct(orig, grpc_rb_metadata, orig_md);
+  Data_Get_Struct(copy, grpc_rb_metadata, copy_md);
+
+  /* use ruby's MEMCPY to make a byte-for-byte copy of the metadata wrapper
+   * object. */
+  MEMCPY(copy_md, orig_md, grpc_rb_metadata, 1);
+  return copy;
+}
+
+/* Gets the key from a metadata instance. */
+static VALUE grpc_rb_metadata_key(VALUE self) {
+  VALUE key = Qnil;
+  grpc_rb_metadata *wrapper = NULL;
+  grpc_metadata *md = NULL;
+
+  Data_Get_Struct(self, grpc_rb_metadata, wrapper);
+  if (wrapper->mark != Qnil) {
+    key = rb_ivar_get(wrapper->mark, id_key);
+    if (key != Qnil) {
+      return key;
+    }
+  }
+
+  md = wrapper->wrapped;
+  if (md == NULL || md->key == NULL) {
+    return Qnil;
+  }
+  return rb_str_new2(md->key);
+}
+
+/* Gets the value from a metadata instance. */
+static VALUE grpc_rb_metadata_value(VALUE self) {
+  VALUE val = Qnil;
+  grpc_rb_metadata *wrapper = NULL;
+  grpc_metadata *md = NULL;
+
+  Data_Get_Struct(self, grpc_rb_metadata, wrapper);
+  if (wrapper->mark != Qnil) {
+    val = rb_ivar_get(wrapper->mark, id_value);
+    if (val != Qnil) {
+      return val;
+    }
+  }
+
+  md = wrapper->wrapped;
+  if (md == NULL || md->value == NULL) {
+    return Qnil;
+  }
+  return rb_str_new2(md->value);
+}
+
+/* rb_cMetadata is the Metadata class whose instances proxy grpc_metadata. */
+VALUE rb_cMetadata = Qnil;
+void Init_google_rpc_metadata() {
+  rb_cMetadata = rb_define_class_under(rb_mGoogleRPC, "Metadata", rb_cObject);
+
+  /* Allocates an object managed by the ruby runtime */
+  rb_define_alloc_func(rb_cMetadata, grpc_rb_metadata_alloc);
+
+  /* Provides a ruby constructor and support for dup/clone. */
+  rb_define_method(rb_cMetadata, "initialize", grpc_rb_metadata_init, 2);
+  rb_define_method(rb_cMetadata, "initialize_copy", grpc_rb_metadata_init_copy,
+                   1);
+
+  /* Provides accessors for the code and details. */
+  rb_define_method(rb_cMetadata, "key", grpc_rb_metadata_key, 0);
+  rb_define_method(rb_cMetadata, "value", grpc_rb_metadata_value, 0);
+
+  id_key = rb_intern("__key");
+  id_value = rb_intern("__value");
+}
+
+/* Gets the wrapped metadata from the ruby wrapper */
+grpc_metadata* grpc_rb_get_wrapped_metadata(VALUE v) {
+  grpc_rb_metadata *wrapper = NULL;
+  Data_Get_Struct(v, grpc_rb_metadata, wrapper);
+  return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_metadata.h b/src/ruby/ext/grpc/rb_metadata.h
new file mode 100644
index 0000000..6b70591
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_metadata.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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_METADATA_H_
+#define GRPC_RB_METADATA_H_
+
+#include <grpc/grpc.h>
+#include <ruby.h>
+
+/* rb_cMetadata is the Metadata class whose instances proxy grpc_metadata. */
+extern VALUE rb_cMetadata;
+
+/* grpc_rb_metadata_create_with_mark creates a grpc_rb_metadata with a ruby mark
+ * object that will be kept alive while the metadata is alive. */
+extern VALUE grpc_rb_metadata_create_with_mark(VALUE mark, grpc_metadata *md);
+
+/* Gets the wrapped metadata from the ruby wrapper */
+grpc_metadata* grpc_rb_get_wrapped_metadata(VALUE v);
+
+/* Initializes the Metadata class. */
+void Init_google_rpc_metadata();
+
+#endif  /* GRPC_RB_METADATA_H_ */
diff --git a/src/ruby/ext/grpc/rb_server.c b/src/ruby/ext/grpc/rb_server.c
new file mode 100644
index 0000000..f4230bd
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_server.c
@@ -0,0 +1,226 @@
+/*
+ *
+ * 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_server.h"
+
+#include <ruby.h>
+
+#include <grpc/grpc.h>
+#include "rb_call.h"
+#include "rb_channel_args.h"
+#include "rb_completion_queue.h"
+#include "rb_grpc.h"
+
+/* rb_cServer is the ruby class that proxies grpc_server. */
+VALUE rb_cServer = Qnil;
+
+/* grpc_rb_server wraps a grpc_server.  It provides a peer ruby object,
+ * 'mark' to minimize copying when a server is created from ruby. */
+typedef struct grpc_rb_server {
+  /* Holder of ruby objects involved in constructing the server */
+  VALUE mark;
+  /* The actual server */
+  grpc_server *wrapped;
+} grpc_rb_server;
+
+/* Destroys server instances. */
+static void grpc_rb_server_free(void *p) {
+  grpc_rb_server *svr = NULL;
+  if (p == NULL) {
+    return;
+  };
+  svr = (grpc_rb_server *)p;
+
+  /* Deletes the wrapped object if the mark object is Qnil, which indicates
+   * that no other object is the actual owner. */
+  if (svr->wrapped != NULL && svr->mark == Qnil) {
+    grpc_server_shutdown(svr->wrapped);
+    grpc_server_destroy(svr->wrapped);
+  }
+
+  xfree(p);
+}
+
+/* Protects the mark object from GC */
+static void grpc_rb_server_mark(void *p) {
+  grpc_rb_server *server = NULL;
+  if (p == NULL) {
+    return;
+  }
+  server = (grpc_rb_server *)p;
+  if (server->mark != Qnil) {
+    rb_gc_mark(server->mark);
+  }
+}
+
+/* Allocates grpc_rb_server instances. */
+static VALUE grpc_rb_server_alloc(VALUE cls) {
+  grpc_rb_server *wrapper = ALLOC(grpc_rb_server);
+  wrapper->wrapped = NULL;
+  wrapper->mark = Qnil;
+  return Data_Wrap_Struct(cls, grpc_rb_server_mark, grpc_rb_server_free,
+                          wrapper);
+}
+
+/* Initializes Server instances. */
+static VALUE grpc_rb_server_init(VALUE self, VALUE cqueue, VALUE channel_args) {
+  grpc_completion_queue *cq = grpc_rb_get_wrapped_completion_queue(cqueue);
+  grpc_rb_server *wrapper = NULL;
+  grpc_server *srv = NULL;
+  grpc_channel_args args;
+  MEMZERO(&args, grpc_channel_args, 1);
+
+  Data_Get_Struct(self, grpc_rb_server, wrapper);
+  grpc_rb_hash_convert_to_channel_args(channel_args, &args);
+  srv = grpc_server_create(cq, &args);
+  if (args.args != NULL) {
+    xfree(args.args);  /* Allocated by grpc_rb_hash_convert_to_channel_args */
+  }
+  if (srv == NULL) {
+    rb_raise(rb_eRuntimeError, "could not create a gRPC server, not sure why");
+  }
+  wrapper->wrapped = srv;
+
+  /* Add the cq as the server's mark object. This ensures the ruby cq can't be
+   * GCed before the server */
+  wrapper->mark = cqueue;
+  return self;
+}
+
+/* Clones Server instances.
+
+   Gives Server a consistent implementation of Ruby's object copy/dup
+   protocol. */
+static VALUE grpc_rb_server_init_copy(VALUE copy, VALUE orig) {
+  grpc_rb_server *orig_srv = NULL;
+  grpc_rb_server *copy_srv = NULL;
+
+  if (copy == orig) {
+    return copy;
+  }
+
+  /* Raise an error if orig is not a server object or a subclass. */
+  if (TYPE(orig) != T_DATA ||
+      RDATA(orig)->dfree != (RUBY_DATA_FUNC)grpc_rb_server_free) {
+    rb_raise(rb_eTypeError, "not a %s", rb_obj_classname(rb_cServer));
+  }
+
+  Data_Get_Struct(orig, grpc_rb_server, orig_srv);
+  Data_Get_Struct(copy, grpc_rb_server, copy_srv);
+
+  /* use ruby's MEMCPY to make a byte-for-byte copy of the server wrapper
+   * object. */
+  MEMCPY(copy_srv, orig_srv, grpc_rb_server, 1);
+  return copy;
+}
+
+static VALUE grpc_rb_server_request_call(VALUE self, VALUE tag_new) {
+  grpc_call_error err;
+  grpc_rb_server *s = NULL;
+  Data_Get_Struct(self, grpc_rb_server, s);
+  if (s->wrapped == NULL) {
+    rb_raise(rb_eRuntimeError, "closed!");
+  } else {
+    err = grpc_server_request_call(s->wrapped, ROBJECT(tag_new));
+    if (err != GRPC_CALL_OK) {
+      rb_raise(rb_eCallError, "server request failed: %s (code=%d)",
+               grpc_call_error_detail_of(err), err);
+    }
+  }
+  return Qnil;
+}
+
+static VALUE grpc_rb_server_start(VALUE self) {
+  grpc_rb_server *s = NULL;
+  Data_Get_Struct(self, grpc_rb_server, s);
+  if (s->wrapped == NULL) {
+    rb_raise(rb_eRuntimeError, "closed!");
+  } else {
+    grpc_server_start(s->wrapped);
+  }
+  return Qnil;
+}
+
+static VALUE grpc_rb_server_destroy(VALUE self) {
+  grpc_rb_server *s = NULL;
+  Data_Get_Struct(self, grpc_rb_server, s);
+  if (s->wrapped != NULL) {
+    grpc_server_shutdown(s->wrapped);
+    grpc_server_destroy(s->wrapped);
+    s->wrapped = NULL;
+    s->mark = Qnil;
+  }
+  return Qnil;
+}
+
+static VALUE grpc_rb_server_add_http2_port(VALUE self, VALUE port) {
+  grpc_rb_server *s = NULL;
+  int added_ok = 0;
+  Data_Get_Struct(self, grpc_rb_server, s);
+  if (s->wrapped == NULL) {
+    rb_raise(rb_eRuntimeError, "closed!");
+  } else {
+    added_ok = grpc_server_add_http2_port(s->wrapped, StringValueCStr(port));
+    if (added_ok == 0) {
+      rb_raise(rb_eRuntimeError, "could not add port %s to server, not sure why",
+               StringValueCStr(port));
+    }
+  }
+  return Qnil;
+}
+
+void Init_google_rpc_server() {
+  rb_cServer = rb_define_class_under(rb_mGoogleRPC, "Server", rb_cObject);
+
+  /* Allocates an object managed by the ruby runtime */
+  rb_define_alloc_func(rb_cServer, grpc_rb_server_alloc);
+
+  /* Provides a ruby constructor and support for dup/clone. */
+  rb_define_method(rb_cServer, "initialize", grpc_rb_server_init, 2);
+  rb_define_method(rb_cServer, "initialize_copy", grpc_rb_server_init_copy, 1);
+
+  /* Add the server methods. */
+  rb_define_method(rb_cServer, "request_call", grpc_rb_server_request_call, 1);
+  rb_define_method(rb_cServer, "start", grpc_rb_server_start, 0);
+  rb_define_method(rb_cServer, "destroy", grpc_rb_server_destroy, 0);
+  rb_define_alias(rb_cServer, "close", "destroy");
+  rb_define_method(rb_cServer, "add_http2_port", grpc_rb_server_add_http2_port,
+                   1);
+}
+
+/* Gets the wrapped server from the ruby wrapper */
+grpc_server* grpc_rb_get_wrapped_server(VALUE v) {
+  grpc_rb_server *wrapper = NULL;
+  Data_Get_Struct(v, grpc_rb_server, wrapper);
+  return wrapper->wrapped;
+}
diff --git a/src/ruby/ext/grpc/rb_server.h b/src/ruby/ext/grpc/rb_server.h
new file mode 100644
index 0000000..4619203
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_server.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * 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_BYTE_BUFFER_H_
+#define GRPC_RB_SERVER_H_
+
+#include <ruby.h>
+#include <grpc/grpc.h>
+
+/* rb_cServer is the Server class whose instances proxy
+   grpc_byte_buffer. */
+extern VALUE rb_cServer;
+
+/* Initializes the Server class. */
+void Init_google_rpc_server();
+
+/* Gets the wrapped server from the ruby wrapper */
+grpc_server* grpc_rb_get_wrapped_server(VALUE v);
+
+#endif  /* GRPC_RB_SERVER_H_ */
diff --git a/src/ruby/ext/grpc/rb_status.c b/src/ruby/ext/grpc/rb_status.c
new file mode 100644
index 0000000..747c47c
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_status.c
@@ -0,0 +1,243 @@
+/*
+ *
+ * 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_mGoogleRPC, "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_mGoogleRPC, "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
new file mode 100644
index 0000000..ceb6f9f
--- /dev/null
+++ b/src/ruby/ext/grpc/rb_status.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * 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_ */