Plumb CallCredentials through Ruby code, replacing metadata_updater functionality
diff --git a/src/ruby/spec/call_credentials_spec.rb b/src/ruby/spec/call_credentials_spec.rb
new file mode 100644
index 0000000..32a0ad4
--- /dev/null
+++ b/src/ruby/spec/call_credentials_spec.rb
@@ -0,0 +1,57 @@
+# Copyright 2015, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+require 'grpc'
+
+describe GRPC::Core::CallCredentials do
+  CallCredentials = GRPC::Core::CallCredentials
+
+  let(:auth_proc) { proc { { 'plugin_key' => 'plugin_value' } } }
+
+  describe '#new' do
+    it 'can successfully create a CallCredentials from a proc' do
+      expect { CallCredentials.new(auth_proc) }.not_to raise_error
+    end
+  end
+
+  describe '#compose' do
+    it 'can compose with another CallCredentials' do
+      creds1 = CallCredentials.new(auth_proc)
+      creds2 = CallCredentials.new(auth_proc)
+      expect { creds1.compose creds2 }.not_to raise_error
+    end
+
+    it 'can compose with multiple CallCredentials' do
+      creds1 = CallCredentials.new(auth_proc)
+      creds2 = CallCredentials.new(auth_proc)
+      creds3 = CallCredentials.new(auth_proc)
+      expect { creds1.compose(creds2, creds3) }.not_to raise_error
+    end
+  end
+end
diff --git a/src/ruby/spec/call_spec.rb b/src/ruby/spec/call_spec.rb
index dd3c45f..6629570 100644
--- a/src/ruby/spec/call_spec.rb
+++ b/src/ruby/spec/call_spec.rb
@@ -144,6 +144,15 @@
     end
   end
 
+  describe '#set_credentials!' do
+    it 'can set a valid CallCredentials object' do
+      call = make_test_call
+      auth_proc = proc { { 'plugin_key' => 'plugin_value' } }
+      creds = GRPC::Core::CallCredentials.new auth_proc
+      expect { call.set_credentials! creds }.not_to raise_error
+    end
+  end
+
   def make_test_call
     @ch.create_call(client_queue, nil, nil, 'dummy_method', nil, deadline)
   end
diff --git a/src/ruby/spec/channel_credentials_spec.rb b/src/ruby/spec/channel_credentials_spec.rb
index b2bdf70..ecab5dc 100644
--- a/src/ruby/spec/channel_credentials_spec.rb
+++ b/src/ruby/spec/channel_credentials_spec.rb
@@ -31,6 +31,7 @@
 
 describe GRPC::Core::ChannelCredentials do
   ChannelCredentials = GRPC::Core::ChannelCredentials
+  CallCredentials = GRPC::Core::CallCredentials
 
   def load_test_certs
     test_root = File.join(File.dirname(__FILE__), 'testdata')
@@ -60,4 +61,32 @@
       expect(&blk).to raise_error
     end
   end
+
+  describe '#compose' do
+    it 'can compose with a CallCredentials' do
+      certs = load_test_certs
+      channel_creds = ChannelCredentials.new(*certs)
+      auth_proc = proc { { 'plugin_key' => 'plugin_value' } }
+      call_creds = CallCredentials.new auth_proc
+      expect { channel_creds.compose call_creds }.not_to raise_error
+    end
+
+    it 'can compose with multiple CallCredentials' do
+      certs = load_test_certs
+      channel_creds = ChannelCredentials.new(*certs)
+      auth_proc = proc { { 'plugin_key' => 'plugin_value' } }
+      call_creds1 = CallCredentials.new auth_proc
+      call_creds2 = CallCredentials.new auth_proc
+      expect do
+        channel_creds.compose(call_creds1, call_creds2)
+      end.not_to raise_error
+    end
+
+    it 'cannot compose with ChannelCredentials' do
+      certs = load_test_certs
+      channel_creds1 = ChannelCredentials.new(*certs)
+      channel_creds2 = ChannelCredentials.new(*certs)
+      expect { channel_creds1.compose channel_creds2 }.to raise_error(TypeError)
+    end
+  end
 end
diff --git a/src/ruby/spec/client_server_spec.rb b/src/ruby/spec/client_server_spec.rb
index 734f176..7cce207 100644
--- a/src/ruby/spec/client_server_spec.rb
+++ b/src/ruby/spec/client_server_spec.rb
@@ -413,6 +413,8 @@
 end
 
 describe 'the secure http client/server' do
+  include_context 'setup: tags'
+
   def load_test_certs
     test_root = File.join(File.dirname(__FILE__), 'testdata')
     files = ['ca.pem', 'server1.key', 'server1.pem']
@@ -443,4 +445,31 @@
 
   it_behaves_like 'GRPC metadata delivery works OK' do
   end
+
+  it 'modifies metadata with CallCredentials' do
+    auth_proc = proc { { 'k1' => 'updated-v1' } }
+    call_creds = GRPC::Core::CallCredentials.new(auth_proc)
+    md = { 'k2' => 'v2' }
+    expected_md = { 'k1' => 'updated-v1', 'k2' => 'v2' }
+    recvd_rpc = nil
+    rcv_thread = Thread.new do
+      recvd_rpc = @server.request_call(@server_queue, @server_tag, deadline)
+    end
+
+    call = new_client_call
+    call.set_credentials! call_creds
+    client_ops = {
+      CallOps::SEND_INITIAL_METADATA => md
+    }
+    batch_result = call.run_batch(@client_queue, @client_tag, deadline,
+                                  client_ops)
+    expect(batch_result.send_metadata).to be true
+
+    # confirm the server can receive the client metadata
+    rcv_thread.join
+    expect(recvd_rpc).to_not eq nil
+    recvd_md = recvd_rpc.metadata
+    replace_symbols = Hash[expected_md.each_pair.collect { |x, y| [x.to_s, y] }]
+    expect(recvd_md).to eq(recvd_md.merge(replace_symbols))
+  end
 end
diff --git a/src/ruby/spec/generic/client_stub_spec.rb b/src/ruby/spec/generic/client_stub_spec.rb
index da5bc6c..4055023 100644
--- a/src/ruby/spec/generic/client_stub_spec.rb
+++ b/src/ruby/spec/generic/client_stub_spec.rb
@@ -145,34 +145,6 @@
         th.join
       end
 
-      it 'should update the sent metadata with a provided metadata updater' do
-        server_port = create_test_server
-        host = "localhost:#{server_port}"
-        th = run_request_response(@sent_msg, @resp, @pass,
-                                  k1: 'updated-v1', k2: 'v2')
-        update_md = proc do |md|
-          md[:k1] = 'updated-v1'
-          md
-        end
-        stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md)
-        expect(get_response(stub)).to eq(@resp)
-        th.join
-      end
-
-      it 'should downcase the keys provided by the metadata updater' do
-        server_port = create_test_server
-        host = "localhost:#{server_port}"
-        th = run_request_response(@sent_msg, @resp, @pass,
-                                  k1: 'downcased-key-v1', k2: 'v2')
-        update_md = proc do |md|
-          md[:K1] = 'downcased-key-v1'
-          md
-        end
-        stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md)
-        expect(get_response(stub)).to eq(@resp)
-        th.join
-      end
-
       it 'should send a request when configured using an override channel' do
         server_port = create_test_server
         alt_host = "localhost:#{server_port}"
@@ -241,20 +213,6 @@
         th.join
       end
 
-      it 'should update the sent metadata with a provided metadata updater' do
-        server_port = create_test_server
-        host = "localhost:#{server_port}"
-        th = run_client_streamer(@sent_msgs, @resp, @pass,
-                                 k1: 'updated-v1', k2: 'v2')
-        update_md = proc do |md|
-          md[:k1] = 'updated-v1'
-          md
-        end
-        stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md)
-        expect(get_response(stub)).to eq(@resp)
-        th.join
-      end
-
       it 'should raise an error if the status is not ok' do
         server_port = create_test_server
         host = "localhost:#{server_port}"
@@ -323,21 +281,6 @@
         expect { e.collect { |r| r } }.to raise_error(GRPC::BadStatus)
         th.join
       end
-
-      it 'should update the sent metadata with a provided metadata updater' do
-        server_port = create_test_server
-        host = "localhost:#{server_port}"
-        th = run_server_streamer(@sent_msg, @replys, @pass,
-                                 k1: 'updated-v1', k2: 'v2')
-        update_md = proc do |md|
-          md[:k1] = 'updated-v1'
-          md
-        end
-        stub = GRPC::ClientStub.new(host, @cq, update_metadata: update_md)
-        e = get_responses(stub)
-        expect(e.collect { |r| r }).to eq(@replys)
-        th.join
-      end
     end
 
     describe 'without a call operation' do
diff --git a/src/ruby/spec/generic/rpc_server_spec.rb b/src/ruby/spec/generic/rpc_server_spec.rb
index efe07f7..d95a021 100644
--- a/src/ruby/spec/generic/rpc_server_spec.rb
+++ b/src/ruby/spec/generic/rpc_server_spec.rb
@@ -422,25 +422,6 @@
         t.join
       end
 
-      it 'should receive updated metadata', server: true do
-        service = EchoService.new
-        @srv.handle(service)
-        t = Thread.new { @srv.run }
-        @srv.wait_till_running
-        req = EchoMsg.new
-        client_opts[:update_metadata] = proc do |md|
-          md[:k1] = 'updated-v1'
-          md
-        end
-        stub = EchoStub.new(@host, **client_opts)
-        expect(stub.an_rpc(req, k1: 'v1', k2: 'v2')).to be_a(EchoMsg)
-        wanted_md = [{ 'k1' => 'updated-v1', 'k2' => 'v2',
-                       'jwt_aud_uri' => "https://#{@host}/EchoService" }]
-        check_md(wanted_md, service.received_md)
-        @srv.stop
-        t.join
-      end
-
       it 'should handle multiple parallel requests', server: true do
         @srv.handle(EchoService)
         t = Thread.new { @srv.run }