protobuf 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +28 -0
  4. data/README.md +216 -0
  5. data/Rakefile +1 -0
  6. data/bin/rpc_server +117 -0
  7. data/bin/rprotoc +46 -0
  8. data/examples/addressbook.pb.rb +55 -0
  9. data/examples/addressbook.proto +24 -0
  10. data/examples/reading_a_message.rb +32 -0
  11. data/examples/writing_a_message.rb +46 -0
  12. data/lib/protobuf.rb +6 -0
  13. data/lib/protobuf/common/exceptions.rb +11 -0
  14. data/lib/protobuf/common/logger.rb +64 -0
  15. data/lib/protobuf/common/util.rb +59 -0
  16. data/lib/protobuf/common/wire_type.rb +10 -0
  17. data/lib/protobuf/compiler/compiler.rb +52 -0
  18. data/lib/protobuf/compiler/nodes.rb +323 -0
  19. data/lib/protobuf/compiler/proto.y +216 -0
  20. data/lib/protobuf/compiler/proto2.ebnf +79 -0
  21. data/lib/protobuf/compiler/proto_parser.rb +1425 -0
  22. data/lib/protobuf/compiler/template/rpc_bin.erb +4 -0
  23. data/lib/protobuf/compiler/template/rpc_client.erb +18 -0
  24. data/lib/protobuf/compiler/template/rpc_service.erb +25 -0
  25. data/lib/protobuf/compiler/template/rpc_service_implementation.erb +42 -0
  26. data/lib/protobuf/compiler/visitors.rb +302 -0
  27. data/lib/protobuf/descriptor/descriptor.proto +286 -0
  28. data/lib/protobuf/descriptor/descriptor.rb +55 -0
  29. data/lib/protobuf/descriptor/descriptor_builder.rb +143 -0
  30. data/lib/protobuf/descriptor/descriptor_proto.rb +138 -0
  31. data/lib/protobuf/descriptor/enum_descriptor.rb +33 -0
  32. data/lib/protobuf/descriptor/field_descriptor.rb +49 -0
  33. data/lib/protobuf/descriptor/file_descriptor.rb +37 -0
  34. data/lib/protobuf/message/decoder.rb +83 -0
  35. data/lib/protobuf/message/encoder.rb +46 -0
  36. data/lib/protobuf/message/enum.rb +62 -0
  37. data/lib/protobuf/message/extend.rb +8 -0
  38. data/lib/protobuf/message/field.rb +701 -0
  39. data/lib/protobuf/message/message.rb +402 -0
  40. data/lib/protobuf/message/protoable.rb +38 -0
  41. data/lib/protobuf/rpc/buffer.rb +74 -0
  42. data/lib/protobuf/rpc/client.rb +268 -0
  43. data/lib/protobuf/rpc/client_connection.rb +225 -0
  44. data/lib/protobuf/rpc/error.rb +34 -0
  45. data/lib/protobuf/rpc/error/client_error.rb +31 -0
  46. data/lib/protobuf/rpc/error/server_error.rb +43 -0
  47. data/lib/protobuf/rpc/rpc.pb.rb +107 -0
  48. data/lib/protobuf/rpc/server.rb +183 -0
  49. data/lib/protobuf/rpc/service.rb +244 -0
  50. data/lib/protobuf/rpc/stat.rb +70 -0
  51. data/lib/protobuf/version.rb +3 -0
  52. data/proto/rpc.proto +73 -0
  53. data/protobuf.gemspec +25 -0
  54. data/script/mk_parser +2 -0
  55. data/spec/functional/embedded_service_spec.rb +7 -0
  56. data/spec/proto/test.pb.rb +31 -0
  57. data/spec/proto/test.proto +31 -0
  58. data/spec/proto/test_service.rb +30 -0
  59. data/spec/proto/test_service_impl.rb +17 -0
  60. data/spec/spec_helper.rb +26 -0
  61. data/spec/unit/client_spec.rb +128 -0
  62. data/spec/unit/common/logger_spec.rb +121 -0
  63. data/spec/unit/enum_spec.rb +13 -0
  64. data/spec/unit/message_spec.rb +67 -0
  65. data/spec/unit/server_spec.rb +27 -0
  66. data/spec/unit/service_spec.rb +75 -0
  67. data/test/check_unbuild.rb +30 -0
  68. data/test/data/data.bin +3 -0
  69. data/test/data/data_source.py +14 -0
  70. data/test/data/types.bin +0 -0
  71. data/test/data/types_source.py +22 -0
  72. data/test/data/unk.png +0 -0
  73. data/test/proto/addressbook.pb.rb +66 -0
  74. data/test/proto/addressbook.proto +33 -0
  75. data/test/proto/addressbook_base.pb.rb +58 -0
  76. data/test/proto/addressbook_base.proto +26 -0
  77. data/test/proto/addressbook_ext.pb.rb +20 -0
  78. data/test/proto/addressbook_ext.proto +6 -0
  79. data/test/proto/collision.pb.rb +17 -0
  80. data/test/proto/collision.proto +5 -0
  81. data/test/proto/ext_collision.pb.rb +24 -0
  82. data/test/proto/ext_collision.proto +8 -0
  83. data/test/proto/ext_range.pb.rb +22 -0
  84. data/test/proto/ext_range.proto +7 -0
  85. data/test/proto/float_default.proto +10 -0
  86. data/test/proto/lowercase.pb.rb +30 -0
  87. data/test/proto/lowercase.proto +9 -0
  88. data/test/proto/merge.pb.rb +39 -0
  89. data/test/proto/merge.proto +15 -0
  90. data/test/proto/nested.pb.rb +30 -0
  91. data/test/proto/nested.proto +9 -0
  92. data/test/proto/optional_field.pb.rb +35 -0
  93. data/test/proto/optional_field.proto +12 -0
  94. data/test/proto/packed.pb.rb +22 -0
  95. data/test/proto/packed.proto +6 -0
  96. data/test/proto/rpc.proto +6 -0
  97. data/test/proto/types.pb.rb +84 -0
  98. data/test/proto/types.proto +37 -0
  99. data/test/test_addressbook.rb +56 -0
  100. data/test/test_compiler.rb +325 -0
  101. data/test/test_descriptor.rb +122 -0
  102. data/test/test_enum_value.rb +41 -0
  103. data/test/test_extension.rb +36 -0
  104. data/test/test_lowercase.rb +11 -0
  105. data/test/test_message.rb +128 -0
  106. data/test/test_optional_field.rb +103 -0
  107. data/test/test_packed_field.rb +40 -0
  108. data/test/test_parse.rb +15 -0
  109. data/test/test_repeated_types.rb +132 -0
  110. data/test/test_serialize.rb +61 -0
  111. data/test/test_standard_message.rb +96 -0
  112. data/test/test_types.rb +226 -0
  113. metadata +261 -0
@@ -0,0 +1,70 @@
1
+ require 'date'
2
+ require 'protobuf/common/logger'
3
+
4
+ module Protobuf
5
+ module Rpc
6
+ class Stat
7
+ attr_accessor :type, :start_time, :end_time, :request_size, :response_size, :client, :server, :service, :method
8
+
9
+ TYPES = [:SERVER, :CLIENT]
10
+
11
+ def initialize type=:SERVER, do_start=true
12
+ @type = type
13
+ start if do_start
14
+ end
15
+
16
+ def client= peer
17
+ @client = {:port => peer[0], :ip => peer[1]}
18
+ end
19
+
20
+ def client
21
+ @client ? '%s:%d' % [@client[:ip], @client[:port]] : nil
22
+ end
23
+
24
+ def server= peer
25
+ @server = {:port => peer[0], :ip => peer[1]}
26
+ end
27
+
28
+ def server
29
+ @server ? '%s:%d' % [@server[:ip], @server[:port]] : nil
30
+ end
31
+
32
+ def sizes
33
+ '%dB/%dB' % [@request_size || 0, @response_size || 0]
34
+ end
35
+
36
+ def start
37
+ @start_time ||= Time.now
38
+ end
39
+
40
+ def end
41
+ start if !@start_time
42
+ @end_time ||= Time.now
43
+ end
44
+
45
+ def rpc
46
+ service && method ? '%s#%s' % [service, method] : nil
47
+ end
48
+
49
+ def elapsed_time
50
+ (start_time && end_time ? '%ss' % (end_time - start_time).round(4) : nil)
51
+ end
52
+
53
+ def log_stats
54
+ Protobuf::Logger.info to_s
55
+ end
56
+
57
+ def to_s
58
+ [
59
+ @type == :SERVER ? '[SRV]' : '[CLT]',
60
+ rpc,
61
+ elapsed_time,
62
+ sizes,
63
+ @type == :SERVER ? server : client
64
+ ].delete_if{|v| v.nil? }.join(' - ')
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+
@@ -0,0 +1,3 @@
1
+ module Protobuf
2
+ VERSION = '1.0.0'
3
+ end
data/proto/rpc.proto ADDED
@@ -0,0 +1,73 @@
1
+ // Copyright (c) 2009 Shardul Deo
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in
11
+ // all copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ // THE SOFTWARE.
20
+
21
+ // Author: Shardul Deo
22
+ //
23
+ // Protobufs needed for socket rpcs.
24
+
25
+ package protobuf.socketrpc;
26
+
27
+ message Request {
28
+
29
+ // RPC service full name
30
+ required string service_name = 1;
31
+
32
+ // RPC method name
33
+ required string method_name = 2;
34
+
35
+ // RPC request proto
36
+ required bytes request_proto = 3;
37
+ }
38
+
39
+ message Response {
40
+
41
+ // RPC response proto
42
+ optional bytes response_proto = 1;
43
+
44
+ // Error, if any
45
+ optional string error = 2;
46
+
47
+ // Was callback invoked
48
+ optional bool callback = 3 [default = false];
49
+
50
+ // Error Reason
51
+ optional ErrorReason error_reason = 4;
52
+ }
53
+
54
+ // Possible error reasons
55
+ // The server-side errors are returned in the response from the server.
56
+ // The client-side errors are returned by the client-side code when it doesn't
57
+ // have a response from the server.
58
+ enum ErrorReason {
59
+
60
+ // Server-side errors
61
+ BAD_REQUEST_DATA = 0; // Server received bad request data
62
+ BAD_REQUEST_PROTO = 1; // Server received bad request proto
63
+ SERVICE_NOT_FOUND = 2; // Service not found on server
64
+ METHOD_NOT_FOUND = 3; // Method not found on server
65
+ RPC_ERROR = 4; // Rpc threw exception on server
66
+ RPC_FAILED = 5; // Rpc failed on server
67
+
68
+ // Client-side errors (these are returned by the client-side code)
69
+ INVALID_REQUEST_PROTO = 6; // Rpc was called with invalid request proto
70
+ BAD_RESPONSE_PROTO = 7; // Server returned a bad response proto
71
+ UNKNOWN_HOST = 8; // Could not find supplied host
72
+ IO_ERROR = 9; // I/O error while communicating with server
73
+ }
data/protobuf.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("./lib", File.dirname(__FILE__))
3
+ require "protobuf/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'protobuf'
7
+ s.version = Protobuf::VERSION
8
+ s.date = %q{2011-11-06}
9
+
10
+ s.authors = ['BJ Neilsen']
11
+ s.email = ["bj.neilsen@gmail.com"]
12
+ s.homepage = %q{https://github.com/localshred/protobuf}
13
+ s.summary = 'Ruby implementation for Protocol Buffers. Works with other protobuf rpc implementations (e.g. Java, Python, C++).'
14
+ s.description = s.summary + "\n\nThis gem has diverged from https://github.com/macks/ruby-protobuf. All credit for serialization and rprotoc work most certainly goes to the original authors. All RPC implementation code (client/server/service) was written and is maintained by this author. Attempts to reconcile the original codebase with the current RPC implementation went unsuccessful."
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_dependency 'eventmachine', '~> 0.12.10'
22
+
23
+ s.add_development_dependency 'rake', '~> 0.8.7'
24
+ s.add_development_dependency 'rspec', '~> 2.7.0'
25
+ end
data/script/mk_parser ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ racc -E lib/protobuf/compiler/proto.y -o lib/protobuf/compiler/proto_parser.rb
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'An Embedded Service Call Hierarchy' do
4
+
5
+
6
+
7
+ end
@@ -0,0 +1,31 @@
1
+ ### Generated by rprotoc. DO NOT EDIT!
2
+ require 'protobuf/message/message'
3
+ require 'protobuf/message/enum'
4
+ require 'protobuf/message/extend'
5
+
6
+ module Spec
7
+ module Proto
8
+ class StatusType < ::Protobuf::Enum
9
+ define :PENDING, 0
10
+ define :ENABLED, 1
11
+ define :DISABLED, 2
12
+ define :DELETED, 3
13
+ end
14
+ class ResourceFindRequest < ::Protobuf::Message
15
+ optional :string, :name, 1
16
+ optional :bool, :active, 2
17
+ end
18
+ class Resource < ::Protobuf::Message
19
+ optional :string, :name, 1
20
+ optional :int64, :date_created, 2
21
+ optional :StatusType, :status, 3
22
+ repeated :StatusType, :repeated_enum, 4
23
+ end
24
+ class Nested < ::Protobuf::Message
25
+ optional :string, :name, 1
26
+ optional :Resource, :resource, 2
27
+ repeated :Resource, :multiple_resources, 3
28
+ optional :StatusType, :status, 4
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ package spec.proto;
2
+
3
+ enum StatusType {
4
+ PENDING = 0;
5
+ ENABLED = 1;
6
+ DISABLED = 2;
7
+ DELETED = 3;
8
+ }
9
+
10
+ message ResourceFindRequest {
11
+ optional string name = 1;
12
+ optional bool active = 2;
13
+ }
14
+
15
+ message Resource {
16
+ optional string name = 1;
17
+ optional int64 date_created = 2;
18
+ optional StatusType status = 3;
19
+ repeated StatusType repeated_enum = 4;
20
+ }
21
+
22
+ message Nested {
23
+ optional string name = 1;
24
+ optional Resource resource = 2;
25
+ repeated Resource multiple_resources = 3;
26
+ optional StatusType status = 4;
27
+ }
28
+
29
+ service TestService {
30
+ rpc Find (ResourceFindRequest) returns (Resource);
31
+ }
@@ -0,0 +1,30 @@
1
+ require 'protobuf/rpc/service'
2
+ require 'spec/proto/test.pb'
3
+
4
+ ## !! DO NOT EDIT THIS FILE !!
5
+ ##
6
+ ## To implement this service as defined by the protobuf, simply
7
+ ## reopen Spec::Proto::TestService and implement each service method:
8
+ ##
9
+ ## module Spec
10
+ ## module Proto
11
+ ## class TestService
12
+ ##
13
+ ## # request -> Spec::Proto::ResourceFindRequest
14
+ ## # response -> Spec::Proto::Resource
15
+ ## def find
16
+ ## # TODO: implement find
17
+ ## end
18
+ ##
19
+ ## end
20
+ ## end
21
+ ## end
22
+ ##
23
+
24
+ module Spec
25
+ module Proto
26
+ class TestService < Protobuf::Rpc::Service
27
+ rpc :find, ResourceFindRequest, Resource
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec/proto/test_service'
2
+
3
+ module Spec
4
+ module Proto
5
+ class TestService
6
+ located_at "localhost:9191"
7
+
8
+ # request -> Spec::Proto::ResourceFindRequest
9
+ # response -> Spec::Proto::Resource
10
+ def find
11
+ response.name = request.name
12
+ response.status = request.active ? 1 : 0
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup :default, :development, :test
4
+
5
+ RSpec.configure do |c|
6
+ c.mock_with :rspec
7
+ end
8
+
9
+ $:.push File.expand_path('..', File.dirname(__FILE__))
10
+ $:.push File.expand_path('../lib', File.dirname(__FILE__))
11
+ require 'protobuf'
12
+
13
+ require 'protobuf/rpc/client'
14
+ class ::Protobuf::Rpc::Client
15
+ def == other
16
+ options == other.options && \
17
+ error == other.error && \
18
+ do_block == other.do_block && \
19
+ @success_callback == other.instance_variable_get(:@success_callback) && \
20
+ @failure_callback == other.instance_variable_get(:@failure_callback)
21
+ end
22
+ end
23
+
24
+ def reset_service_location service
25
+ service.instance_variable_set :@locations, nil
26
+ end
@@ -0,0 +1,128 @@
1
+ require 'spec_helper'
2
+ require 'spec/proto/test_service_impl'
3
+
4
+ describe Protobuf::Rpc::Client do
5
+
6
+ context 'when creating a client from a service' do
7
+
8
+ it 'should be able to get a client through the Service#client helper method' do
9
+ Spec::Proto::TestService.client(:port => 9191).should == Protobuf::Rpc::Client.new(:service => Spec::Proto::TestService, :port => 9191)
10
+ end
11
+
12
+ it "should be able to override a service location's host and port" do
13
+ Spec::Proto::TestService.located_at 'somewheregreat.com:12345'
14
+ clean_client = Spec::Proto::TestService.client
15
+ clean_client.options[:host].should == 'somewheregreat.com'
16
+ clean_client.options[:port].should == 12345
17
+
18
+ updated_client = Spec::Proto::TestService.client(:host => 'amazing.com', :port => 54321)
19
+ updated_client.options[:host].should == 'amazing.com'
20
+ updated_client.options[:port].should == 54321
21
+ end
22
+
23
+ it 'should be able to define the syncronicity of the client request' do
24
+ client = Spec::Proto::TestService.client(:async => false)
25
+ client.options[:async].should be_false
26
+ client.do_block.should be_true
27
+
28
+ client = Spec::Proto::TestService.client(:async => true)
29
+ client.options[:async].should be_true
30
+ client.do_block.should be_false
31
+ end
32
+
33
+ it 'should be able to define which service to create itself for' do
34
+ client = Protobuf::Rpc::Client.new :service => Spec::Proto::TestService
35
+ client.options[:service].should == Spec::Proto::TestService
36
+ end
37
+
38
+ it 'should have a hard default for host and port on a service that has not been configured' do
39
+ reset_service_location Spec::Proto::TestService
40
+ client = Spec::Proto::TestService.client
41
+ client.options[:host].should == Protobuf::Rpc::Service::DEFAULT_LOCATION[:host]
42
+ client.options[:port].should == Protobuf::Rpc::Service::DEFAULT_LOCATION[:port]
43
+ end
44
+
45
+ end
46
+
47
+ context 'when calling methods on a service client' do
48
+
49
+ # NOTE: we are assuming the service methods are accurately
50
+ # defined inside spec/proto/test_service.rb,
51
+ # namely the :find method
52
+
53
+ it 'should respond to defined service methods' do
54
+ client = Spec::Proto::TestService.client
55
+ client.should_receive(:send_request).and_return(nil)
56
+ expect { client.find(nil) }.should_not raise_error
57
+ end
58
+
59
+ it 'raises a NameError when accessing a var that does not exist' do
60
+ pending
61
+ end
62
+
63
+ it 'should be able to set and get local variables within client response blocks' do
64
+ outer_value = 'OUTER'
65
+ inner_value = 'INNER'
66
+ client = Spec::Proto::TestService.client(:async => true)
67
+
68
+ EM.should_receive(:reactor_running?).and_return(true)
69
+ EM.stub!(:schedule) do
70
+ client.instance_variable_get(:@success_callback).call(inner_value)
71
+ end
72
+
73
+ client.find(nil) do |c|
74
+ c.on_success do |response|
75
+ outer_value.should == 'OUTER'
76
+ outer_value = response
77
+ end
78
+ end
79
+ outer_value.should == inner_value
80
+ end
81
+
82
+ end
83
+
84
+ context 'when receiving request objects' do
85
+
86
+ it 'should be able to create the correct request object if passed a hash' do
87
+ client = Spec::Proto::TestService.client
88
+ client.should_receive(:send_request)
89
+ client.find({:name => 'Test Name', :active => false})
90
+ client.options[:request].should be_a Spec::Proto::ResourceFindRequest
91
+ client.options[:request].name.should == 'Test Name'
92
+ client.options[:request].active.should == false
93
+ end
94
+
95
+ end
96
+
97
+ describe '#synchronize_or_return' do
98
+
99
+ context 'when a timeout error occurs' do
100
+ it 'returns a timeout error' do
101
+ client = Spec::Proto::TestService.client :timeout => 1
102
+ client.stub(:ensure_callback).and_return(proc {|err|
103
+ err.should be_a Protobuf::Rpc::ClientError
104
+ err.message.should == 'Client timeout of 1 seconds expired'
105
+ err.code.should == Protobuf::Socketrpc::ErrorReason::RPC_ERROR
106
+ })
107
+ Timeout.timeout(2) { client.synchronize_or_return }
108
+ end
109
+ end
110
+
111
+ context 'when any other error occurs' do
112
+ it 'returns the error' do
113
+ client = Spec::Proto::TestService.client
114
+ client.stub(:ensure_callback).and_return(proc {|err|
115
+ err.should be_a Protobuf::Rpc::ClientError
116
+ err.message.should == 'Client failed: This is another type of error'
117
+ err.code.should == Protobuf::Socketrpc::ErrorReason::RPC_ERROR
118
+ })
119
+ Timeout.timeout(2) {
120
+ Timeout.stub(:timeout).and_raise(RuntimeError.new('This is another type of error'))
121
+ client.synchronize_or_return
122
+ }
123
+ end
124
+ end
125
+
126
+ end
127
+
128
+ end