Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2bbab74
added support for interfaceSchemas
nemoNoboru May 30, 2016
56a057c
fixed a little the readme
nemoNoboru May 30, 2016
5e52852
added capability client, (not finished) and tweaked the interfaces
nemoNoboru May 30, 2016
28a7f74
fixed typos on readme
nemoNoboru May 30, 2016
84079b0
FAILING TESTS 'symbol lookup error' on CapabilityClient
nemoNoboru May 31, 2016
db30c08
oops
nemoNoboru May 31, 2016
5274c4d
having some trouble. fixing problems, created anothers'
nemoNoboru Jun 1, 2016
a4d8b1d
Merge branch 'master' of https://github.com/nemoNoboru/capnp-ruby
nemoNoboru Jun 1, 2016
9628e07
fixed things. deleted unnecesary clases
nemoNoboru Jun 2, 2016
e72f272
Capclient almost done
nemoNoboru Jun 3, 2016
380a564
having some trouble pipeling things...
nemoNoboru Jun 3, 2016
1b7c905
quickfixes
nemoNoboru Jun 3, 2016
2042eb2
passing tests. TODO: an easy to use interface
nemoNoboru Jun 6, 2016
2d76100
nice interface to client. there is some work to do but it works
nemoNoboru Jun 6, 2016
e439b9f
fixed todos
nemoNoboru Jun 6, 2016
9ef98a7
fixed some memory leaks, realease/adquire global interpreter lock on …
nemoNoboru Jun 7, 2016
318cc42
fixed things
nemoNoboru Jun 8, 2016
3d3d65a
improved client interface
nemoNoboru Jun 9, 2016
5cb1101
added more documentation
nemoNoboru Jun 10, 2016
8e987ef
quickfix
nemoNoboru Jun 10, 2016
850f7be
working on capability::server.
nemoNoboru Jun 18, 2016
9b61a9d
wrapped CallContext on ruby
nemoNoboru Jun 20, 2016
ce1974c
TODO: ezrpc-server
nemoNoboru Jun 21, 2016
0f0cf21
initial capabilityServer.
nemoNoboru Jun 23, 2016
b672706
now the capabilityServer releases and adquires lock.
nemoNoboru Jun 27, 2016
b9e7888
WIP capabilityServer
nemoNoboru Jun 28, 2016
09a7baf
Day of coding samples/tests.
nemoNoboru Jun 29, 2016
8917f1d
quickfix setting params not working properly
nemoNoboru Jun 30, 2016
4483db4
rewrited example code, fixed important stuff, almost fixed pipelines
nemoNoboru Jun 30, 2016
7a2c3ae
breaking changes on capClient. docs and tests will be rewritted soon
nemoNoboru Jul 1, 2016
b07c810
breaking changes #2
nemoNoboru Jul 1, 2016
0735432
RPC server released, fixed documentation, fixed examples, fixed minor…
nemoNoboru Jul 4, 2016
914a19c
fixed exceptions at close.
nemoNoboru Jul 8, 2016
24a53d2
Optimized Capserver, breaking changes. will write doc soon.
nemoNoboru Jul 18, 2016
aca0bb2
writted new doc
nemoNoboru Jul 19, 2016
d95882e
quickfixes
nemoNoboru Jul 19, 2016
addb19e
gemified
nemoNoboru Jul 25, 2016
6ddff47
quickfix on readme
nemoNoboru Jul 30, 2016
d2417df
improved the readme, deleted the useless makefile
nemoNoboru Aug 2, 2016
621565e
added known bugs
nemoNoboru Aug 2, 2016
417f17a
Deleted old test files, made minor changes, corrected some comments i…
nemoNoboru Aug 9, 2016
dc5e64d
preparing the binding to be published on rubygems
nemoNoboru Aug 12, 2016
ae1ba58
quickfix, added the --pre flag
nemoNoboru Aug 12, 2016
f363e65
added the require sentences
nemoNoboru Aug 12, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ Gemfile.lock
tmp
.env
pkg
*.o
*.so
*.gem
Makefile
147 changes: 142 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,148 @@ This here is a [Ruby][ruby] wrapper for the official C++ implementation of [Cap'
First [install libcapnp][libcapnp-install], then install the gem:

```bash
gem install capn_proto --pre
gem install capn_proto-rpc --pre
```

The native extension for this gem requires a C++ compiler with C++11 features, so use the same C++ compiler and flags that you used to compile libcapnp (e.g. `CXX` and `CXXFLAGS`). As an OSX user, having followed the [instructions for installing libcapnp on OSX][libcapnp-install], the correct incantation is as follows:
or add this to your Gemfile


```bash
CXX=$HOME/clang-3.2/bin/clang++ gem install capn_proto --pre
gem capn_proto-rpc
```

The native extension for this gem requires a C++ compiler with C++11 features.
I've hardcoded compiler flags directly on the makefile in order to make the install easier.

# RPC Client Example
note: the schema file, client example and the server example can be found in lib/tests as a minitest.

The following examples uses this schema:

```CapnProto
# file hidraCordatus.capnp

struct Task {
dataint @0 :Int32;
madeBy @1 :Text;
}

interface Employer {
getWorker @0 () -> ( worker :Worker );
}

interface Worker {
put23 @0 (taskToProcess :Task) -> (taskProcessed :Task);
}

```
Load all the schemas and methods that will be used then create an EzRpcClient and from it get our client.
```ruby
require 'capn_proto'

module Hydra extend CapnProto::SchemaLoader
load_schema('./tests/hidraCordatus.capnp')
end

employer_schema = Hydra::Employer.schema
get_worker_method = Hydra::Employer.method! 'getWorker'
put23method = Hydra::Worker.method! 'put23'

ezclient = CapnProto::EzRpcClient.new("127.0.0.1:1337",employer_schema)
client = ezclient.client

```
Create a request of the method "getWorker" who is in the variable get_worker_method above.
Then, send it storing the pipelined request.
```ruby
request = client.request(get_worker_method)
pipelinedRequest = request.send
```
get the returned "worker" set the method that we want to request on it and then set
the parameters to be requested, in this case we set dataint to 0.

```ruby
pipelinedRequest.get('worker').method = put23method
pipelinedRequest.taskToProcess.dataint(0)
```
now we wait for the results (note that this is the only line that blocks). while we are waiting
the Global interpreter lock is released so we can run ruby code on other threads.
Also note that we use ezclient as a waitscope.
```ruby
results = pipelinedRequest.send.wait(ezclient)
puts results.taskProcessed.dataint
puts results.taskProcessed.madeBy
```

# RPC server Example
```ruby
require 'capn_proto'

module Hydra extend CapnProto::SchemaLoader
load_schema('./tests/hidraCordatus.capnp')
end

class WorkerServer < CapnProto::CapabilityServer
def initialize(i)
@madeBy = "made by worker ##{i}"
super(Hydra::Worker.schema)
end

def put23(context)
n = context.getParams.taskToProcess.dataint
context.getResults.taskProcessed.dataint = n + 23
context.getResults.taskProcessed.madeBy = @madeBy
end
end

class EmployerServer < CapnProto::CapabilityServer
def initialize(wp)
@worker_pool = wp
@currentWorker = 0
super(Hydra::Employer.schema)
end

def get_a_Worker
@currentWorker += 1
@worker_pool[@currentWorker % @worker_pool.size]
end

def getWorker(context)
context.getResults.worker = get_a_Worker
end
end

```
note that the name of the methods is exactly the same as the name of the function that is defined on the schema and recieves only one argument. This argument is a callContext, you can use the method **getParams** to get the parameters passed to the called method or
use **getResults** to set the results of the request.
regarding to the example, EmployerServer will serve WorkerServers to the clients.

# Example
```ruby
workers = []
10.times do |i|
workers << WorkerServer.new(i)
end


e = CapnProto::EzRpcServer.new(EmployerServer.new(workers), "*:1337")
puts "serving EmployerServer on 1337..."
e.run

```
create ten workers, then a EzRpcServer wich binds to port 1337. Then run it.
```
the results of running the server/client pair is :

23
"made by worker #1"
23
"made by worker #2"
23
"made by worker #3"
23
...
```
# Structs Example

```ruby
require 'capn_proto'
Expand Down Expand Up @@ -99,13 +231,18 @@ What's implemented:
- Message writing
- To byte string
- To file descriptor
- RPC
- loading InterfaceSchema and their methods
- RPC client
- RPC server

What's to come:
- More reading/writing mechanisms:
- Packing/unpacking
- Extensive test coverage
- Proper support for [JRuby][jruby]
- Support for RPC
- There is a known bug where the servers don't exits when pressing control-c. It only exits after
pressing control-c and then a request is made from a client.

[logo]: https://raw.github.com/cstrahan/capnp-ruby/master/media/captain_proto_small.png "Cap'n Proto"
[ruby]: http://www.ruby-lang.org/ "Ruby"
Expand Down
18 changes: 10 additions & 8 deletions capn_proto.gemspec
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
require File.expand_path('../lib/capn_proto/version', __FILE__)

Gem::Specification.new do |s|
s.name = 'capn_proto'
s.name = 'capn_proto-rpc'
s.license = 'MIT'

s.version = CapnProto::VERSION
s.required_ruby_version = '>= 2.0'

s.summary = "Cap'n Proto (libcapnp) bindings for Ruby."

Expand All @@ -14,19 +15,20 @@ Gem::Specification.new do |s|
"From the Cap'n Proto documentation: " \
"\"Cap'n Proto is an insanely fast data interchange format and " \
"capability-based RPC system. Think JSON, except binary. " \
"Or think Protocol Buffers, except faster.\""
"Or think Protocol Buffers, except faster.\" " \
"This is a extended version of the original gem Capnproto which adds RPC support visit the homepage to view usage"

s.homepage = 'https://github.com/cstrahan/capnp-ruby'
s.homepage = 'https://github.com/nemoNoboru/capnp-ruby'

s.authors = ['Charles Strahan']
s.email = ['charles.c.strahan@gmail.com']
s.authors = ['Charles Strahan','Felipe Vieira']
s.email = ['charles.c.strahan@gmail.com','felipetavres@gmail.com']

s.add_development_dependency 'rspec', '2.14.1'
s.add_development_dependency 'rake'
s.add_development_dependency 'rake' , '~> 0'
s.add_development_dependency 'rake-compiler', '0.7.6'

s.add_development_dependency 'awesome_print'
s.add_development_dependency 'interactive_editor'
s.add_development_dependency 'awesome_print', '~> 0'
s.add_development_dependency 'interactive_editor', '~> 0'

s.extensions = ['ext/capn_proto/extconf.rb']
s.require_paths = ['lib']
Expand Down
52 changes: 52 additions & 0 deletions ext/capn_proto/EzRpc_client.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include "ruby_capn_proto.h"
#include "EzRpc_client.h"
#include "capability_client.h"
#include "dynamic_capability_client.h"
#include "class_builder.h"
#include "exception.h"
#include "util.h"

namespace ruby_capn_proto {
using WrappedType = capnp::EzRpcClient;
VALUE EzRpcCapabilityClient::Class;

void EzRpcCapabilityClient::Init() {
ClassBuilder("EzRpcClient", rb_cObject).
defineAlloc(&alloc).
defineMethod("client", &make_dynamic).
defineMethod("initialize" , &create).
store(&Class);
}

void EzRpcCapabilityClient::free(WrappedType* p) {
ruby_xfree(p);
}

VALUE EzRpcCapabilityClient::alloc(VALUE klass) {
return Data_Wrap_Struct(klass, NULL, free, ruby_xmalloc(sizeof(WrappedType)));
}

WrappedType* EzRpcCapabilityClient::unwrap(VALUE self) {
WrappedType* p;
Data_Get_Struct(self, WrappedType, p);
return p;
}

VALUE EzRpcCapabilityClient::create(VALUE self, VALUE dir, VALUE interschema) {

WrappedType* rb_self = unwrap(self);
new (rb_self) capnp::EzRpcClient(Util::toString(dir));

//store the InterfaceSchema
rb_iv_set(self,"schema",interschema);

return self;
}

VALUE EzRpcCapabilityClient::make_dynamic(VALUE self){
VALUE rb_schema = rb_iv_get(self,"schema");
VALUE rb_cap = CapabilityClient::create(unwrap(self)->getMain());
return DynamicCapabilityClient::create(rb_cap,rb_schema);
}

}
21 changes: 21 additions & 0 deletions ext/capn_proto/EzRpc_client.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef EZRPC_CAPABILITY_CLIENT_H
#define EXRPC_CAPABILITY_CLIENT_H

#include "ruby_capn_proto.h"

namespace ruby_capn_proto {
class EzRpcCapabilityClient {
public:
using WrappedType = capnp::EzRpcClient;
static void Init();
static VALUE alloc(VALUE klass);
static VALUE create(VALUE self, VALUE dir, VALUE schema);
static void free(WrappedType* p);
static WrappedType* unwrap(VALUE self);
static VALUE make_dynamic(VALUE self);
static VALUE Class;
};
}


#endif /* EZRPC_CAPABILITY_CLIENT_H */
79 changes: 79 additions & 0 deletions ext/capn_proto/EzRpc_server.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include "ruby_capn_proto.h"
#include "EzRpc_server.h"
#include "ruby_capability_server.h"
#include "interface_schema.h"
#include "exception.h"
#include "class_builder.h"
#include <ruby/thread.h>
#include "util.h"

namespace ruby_capn_proto {
using WrappedType = capnp::EzRpcServer;
VALUE EzRpcCapabilityServer::Class;

void EzRpcCapabilityServer::Init() {
ClassBuilder("EzRpcServer", rb_cObject).
defineAlloc(&alloc).
defineMethod("run" , &process).
defineMethod("initialize" , &create).
store(&Class);
}

void EzRpcCapabilityServer::free(WrappedType* p) {
ruby_xfree(p);
}

VALUE EzRpcCapabilityServer::alloc(VALUE klass) {
return Data_Wrap_Struct(klass, NULL, free, ruby_xmalloc(sizeof(WrappedType)));
}

WrappedType* EzRpcCapabilityServer::unwrap(VALUE self) {
WrappedType* p;
Data_Get_Struct(self, WrappedType, p);
return p;
}

VALUE EzRpcCapabilityServer::create(VALUE self, VALUE rb_capServer, VALUE dir) {

VALUE interschema = rb_iv_get(rb_capServer,"@schema");
auto schema = InterfaceSchema::unwrap(interschema);

WrappedType* rb_self = unwrap(self);
new (rb_self) capnp::EzRpcServer( kj::heap<capnp::RubyCapabilityServer>(*schema, rb_capServer) , Util::toString(dir) );

return self;
}

VALUE EzRpcCapabilityServer::process(VALUE self){
try{
loopCall l;
auto server = unwrap(self);
auto to_fulfill = kj::heap<kj::PromiseFulfillerPair<void>>(kj::newPromiseAndFulfiller<void>());
l.waitscope = &server->getWaitScope();
l.promisepair = to_fulfill.get();
rb_thread_call_without_gvl(loopServer, &l, stopLoopServer , l.promisepair);
}catch ( kj::Exception t ){
Exception::raise(t);
}
return Qtrue;
}

void * EzRpcCapabilityServer::loopServer(void * p){
try {
auto* loopcall = (loopCall*) p;
loopcall->promisepair->promise.wait(*(loopcall->waitscope));
}catch( kj::Exception t ){
//adquire the lock to raise a ruby exception
rb_thread_call_with_gvl(&Exception::raise,&t);
}
}

void EzRpcCapabilityServer::stopLoopServer(void *p){
try {
auto* promisefulfiller = (kj::PromiseFulfillerPair<void>*) p;
promisefulfiller->fulfiller->fulfill();
}catch( kj::Exception t ){
Exception::raise(t);
}
}
}
Loading