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,121 @@
1
+ require 'protobuf/common/logger'
2
+ require 'stringio'
3
+
4
+ describe Protobuf::Logger do
5
+
6
+ subject { Protobuf::Logger }
7
+
8
+ before(:each) do
9
+ Protobuf::Logger.reset_device!
10
+ Protobuf::Logger.file = '/dev/null'
11
+ Protobuf::Logger.level = ::Logger::INFO
12
+ end
13
+
14
+ describe '.instance' do
15
+
16
+ it 'doesn\'t create a logger if the file was not set' do
17
+ subject.file = nil
18
+ subject.instance.should be_nil
19
+ end
20
+
21
+ it 'doesn\'t create a logger if the level was not set' do
22
+ subject.level = nil
23
+ subject.instance.should be_nil
24
+ end
25
+
26
+ it 'gets a new instance of the logger when file and level are set' do
27
+ subject.file.should_not be_nil
28
+ subject.level.should_not be_nil
29
+ subject.instance.should_not be_nil
30
+ end
31
+
32
+ it 'keeps the same object from multiple calls to instance' do
33
+ subject.instance === subject.instance
34
+ end
35
+
36
+ end
37
+
38
+ describe '.configure' do
39
+ before(:each) { subject.reset_device! }
40
+ it 'sets the file and level in one call' do
41
+ subject.file.should_not be
42
+ subject.level.should_not be
43
+ subject.instance.should_not be
44
+ subject.configure :file => 'myfile.log', :level => ::Logger::WARN
45
+ subject.file.should == 'myfile.log'
46
+ subject.level.should == ::Logger::WARN
47
+ subject.instance.level.should == ::Logger::WARN
48
+ end
49
+
50
+ end
51
+
52
+ describe '.reset_device!' do
53
+
54
+ it 'resets the logger instance, file, and level' do
55
+ subject.instance.should be
56
+ subject.file.should be
57
+ subject.level.should be
58
+ subject.reset_device!
59
+ subject.instance.should_not be
60
+ subject.file.should_not be
61
+ subject.level.should_not be
62
+ end
63
+
64
+ end
65
+
66
+ context 'when logging' do
67
+
68
+ it 'doesn\'t raise errors when log instance is nil' do
69
+ subject.reset_device!
70
+ subject.instance.should be_nil
71
+ expect {
72
+ subject.debug 'No errors here'
73
+ subject.info 'No errors here'
74
+ subject.warn 'No errors here'
75
+ subject.error 'No errors here'
76
+ subject.fatal 'No errors here'
77
+ subject.add 'No errors here'
78
+ subject.log 'No errors here'
79
+ }.should_not raise_error
80
+ end
81
+
82
+ it 'logs correctly when instance is valid' do
83
+ subject.instance.should_not be_nil
84
+ subject.instance.should_receive(:info).with('Should log great')
85
+ subject.info 'Should log great'
86
+ end
87
+
88
+ end
89
+
90
+ describe Protobuf::Logger::LogMethods do
91
+
92
+ context 'when included in another class' do
93
+
94
+ before(:all) do
95
+ class MyTestClass
96
+ include Protobuf::Logger::LogMethods
97
+ end
98
+ end
99
+
100
+ subject { MyTestClass.new }
101
+
102
+ it 'responds to all logger methods' do
103
+ subject.should respond_to :log_debug
104
+ subject.should respond_to :log_info
105
+ subject.should respond_to :log_warn
106
+ subject.should respond_to :log_error
107
+ subject.should respond_to :log_fatal
108
+ subject.should respond_to :log_add
109
+ subject.should respond_to :log_log
110
+ end
111
+
112
+ it 'passes all embedded log calls to Logger instance' do
113
+ Protobuf::Logger.instance.should_receive(:debug).with('log this')
114
+ subject.log_debug('log this')
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+
121
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'spec/proto/test.pb'
3
+
4
+ describe Protobuf::Enum do
5
+ context 'when coercing from enum' do
6
+ subject { Spec::Proto::StatusType::PENDING }
7
+ it { should == 0 }
8
+ end
9
+ context 'when coercing from integer' do
10
+ subject { 0 }
11
+ it { should == Spec::Proto::StatusType::PENDING }
12
+ end
13
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+ require 'spec/proto/test.pb'
3
+
4
+ describe Protobuf::Message do
5
+
6
+ context 'when converting to json' do
7
+
8
+ it "should be jsonable" do
9
+ msg = Spec::Proto::ResourceFindRequest.new
10
+ msg.should respond_to(:to_json)
11
+ msg.name = 'Jeff'
12
+ msg.active = false
13
+ msg.to_json.should == '{"name":"Jeff","active":false}'
14
+ end
15
+
16
+ it 'should generate nested messages into nested json objects' do
17
+ date_created = Time.now.to_i
18
+
19
+ nested = Spec::Proto::Nested.new
20
+ nested.name = 'NESTED'
21
+ nested.resource = Spec::Proto::Resource.new.tap do |r|
22
+ r.name = 'RESOURCE SINGLE'
23
+ r.date_created = date_created
24
+ r.status = Spec::Proto::StatusType::PENDING
25
+ end
26
+ 2.times do |i|
27
+ nested.multiple_resources << Spec::Proto::Resource.new.tap do |r|
28
+ r.name = 'RESOURCE MULTIPLE %d' % i
29
+ r.date_created = date_created
30
+ r.status = Spec::Proto::StatusType::PENDING
31
+ end
32
+ end
33
+ nested.status = Spec::Proto::StatusType::ENABLED
34
+
35
+ nested.to_json.should == %Q{{"name":"NESTED","resource":{"name":"RESOURCE SINGLE","date_created":#{date_created},"status":0,"repeated_enum":[]},"multiple_resources":[{"name":"RESOURCE MULTIPLE 0","date_created":#{date_created},"status":0,"repeated_enum":[]},{"name":"RESOURCE MULTIPLE 1","date_created":#{date_created},"status":0,"repeated_enum":[]}],"status":1}}
36
+ end
37
+
38
+
39
+ end
40
+
41
+ context 'when converting to a hash' do
42
+
43
+ context 'when message has repeated enum field' do
44
+
45
+ it 'provides an array of integers' do
46
+ resource = Spec::Proto::Resource.new :repeated_enum => [
47
+ Spec::Proto::StatusType::PENDING,
48
+ Spec::Proto::StatusType::ENABLED,
49
+ Spec::Proto::StatusType::ENABLED,
50
+ Spec::Proto::StatusType::DISABLED,
51
+ Spec::Proto::StatusType::DELETED
52
+ ]
53
+
54
+ resource.to_hash[:repeated_enum].should == [
55
+ Spec::Proto::StatusType::PENDING.value,
56
+ Spec::Proto::StatusType::ENABLED.value,
57
+ Spec::Proto::StatusType::ENABLED.value,
58
+ Spec::Proto::StatusType::DISABLED.value,
59
+ Spec::Proto::StatusType::DELETED.value
60
+ ]
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'protobuf/rpc/server'
3
+ require 'spec/proto/test_service_impl'
4
+
5
+ describe Protobuf::Rpc::Server do
6
+ context 'when sending response objects' do
7
+ it 'should be able to send a hash object as a response' do
8
+ server = Protobuf::Rpc::Server.new
9
+
10
+ # Setup the right mocks
11
+ server.instance_variable_set(:@klass, Spec::Proto::TestService)
12
+ response_wrapper = mock('response')
13
+ response_wrapper.stub(:response_proto=)
14
+ server.instance_variable_set(:@response, response_wrapper)
15
+ Spec::Proto::TestService.stub_chain(:rpcs, :[], :[], :response_type).and_return(Spec::Proto::ResourceFindRequest)
16
+
17
+ # Setup expectations
18
+ hash_response = {:name => 'Test Name', :active => false}
19
+ expected = Spec::Proto::ResourceFindRequest.new(hash_response)
20
+ Spec::Proto::ResourceFindRequest.should_receive(:new).with(hash_response).and_return(expected)
21
+ server.should_not_receive(:handle_error)
22
+
23
+ # Call the method
24
+ server.send(:parse_response_from_service, hash_response)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+ require 'spec/proto/test_service_impl'
3
+
4
+ describe Protobuf::Rpc::Service do
5
+
6
+ context 'when configuring' do
7
+ before :each do
8
+ reset_service_location Spec::Proto::TestService
9
+ end
10
+
11
+ it 'should have a default location configured' do
12
+ Spec::Proto::TestService.host.should == Protobuf::Rpc::Service::DEFAULT_LOCATION[:host]
13
+ Spec::Proto::TestService.port.should == Protobuf::Rpc::Service::DEFAULT_LOCATION[:port]
14
+ end
15
+
16
+ it "should be able to pre-configure a service location for clients" do
17
+ Spec::Proto::TestService.located_at 'google.com:12345'
18
+ client = Spec::Proto::TestService.client
19
+ client.options[:host].should == 'google.com'
20
+ client.options[:port].should == 12345
21
+ end
22
+
23
+ it 'should be able to configure and read the host' do
24
+ Spec::Proto::TestService.configure :host => 'somehost.com'
25
+ Spec::Proto::TestService.host.should == 'somehost.com'
26
+ end
27
+
28
+ it 'should be able to configure and read the port' do
29
+ Spec::Proto::TestService.configure :port => 12345
30
+ Spec::Proto::TestService.port.should == 12345
31
+ end
32
+
33
+ it 'should skip configuring location if the location passed does not match host:port syntax' do
34
+ invalid_locations = [nil, 'myhost:', ':9939', 'badhost123']
35
+ invalid_locations.each do |location|
36
+ Spec::Proto::TestService.located_at location
37
+ Spec::Proto::TestService.host.should == Protobuf::Rpc::Service::DEFAULT_LOCATION[:host]
38
+ Spec::Proto::TestService.port.should == Protobuf::Rpc::Service::DEFAULT_LOCATION[:port]
39
+ end
40
+ end
41
+ end
42
+
43
+ context 'when server calls the service method' do
44
+
45
+ before(:all) do
46
+ class ::NewTestService < Protobuf::Rpc::Service
47
+ rpc :bad_method, Spec::Proto::ResourceFindRequest, Spec::Proto::Resource
48
+ rpc :bad_var, Spec::Proto::ResourceFindRequest, Spec::Proto::Resource
49
+ def bad_method
50
+ hash = {}
51
+ hash[:one].explode
52
+ end
53
+ def bad_var
54
+ invalidvar
55
+ end
56
+ end
57
+ end
58
+
59
+ it 'raises an undefined method name error when calling a method on a non-existant object' do
60
+ expect {
61
+ req = mock('RequestWrapper', :request_proto => Spec::Proto::ResourceFindRequest.new.to_s)
62
+ ::NewTestService.new.bad_method(req)
63
+ }.to raise_error(NoMethodError)
64
+ end
65
+
66
+ it 'raises a name error when accessing a non-existant object' do
67
+ expect {
68
+ req = mock('RequestWrapper', :request_proto => Spec::Proto::ResourceFindRequest.new.to_s)
69
+ ::NewTestService.new.bad_var(req)
70
+ }.to raise_error(NameError)
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.push "#{File.dirname(__FILE__)}/../lib"
4
+
5
+ require 'test/unit'
6
+ require 'protobuf/descriptor/descriptor_builder'
7
+ require 'protobuf/descriptor/descriptor_proto'
8
+
9
+ class DescriptorTest < Test::Unit::TestCase
10
+ def test_unbuild
11
+ tutorial_proto = Google::Protobuf::FileDescriptorProto.new
12
+ tutorial_proto.parse_from_file 'person.bin'
13
+ Protobuf::Descriptor::DescriptorBuilder.build tutorial_proto
14
+
15
+ assert_nothing_raised {Tutorial::Person}
16
+ assert_nothing_raised {Tutorial::Person.new}
17
+ assert_equal(['age', 'email', 'id', 'name', 'phone'],
18
+ Tutorial::Person.fields.map{|tag, field| field.name}.sort)
19
+
20
+ assert_nothing_raised {Tutorial::Person::PhoneNumber}
21
+ assert_nothing_raised {Tutorial::Person::PhoneNumber.new}
22
+ assert_equal(['number', 'type'],
23
+ Tutorial::Person::PhoneNumber.fields.map{|tag, field| field.name}.sort)
24
+
25
+ assert_nothing_raised {Tutorial::Person::PhoneType}
26
+ assert_equal(0, Tutorial::Person::PhoneType::MOBILE)
27
+ assert_equal(1, Tutorial::Person::PhoneType::HOME)
28
+ assert_equal(2, Tutorial::Person::PhoneType::WORK)
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+
2
+ John Doe� jdoe@example.com"
3
+ 555-4321
@@ -0,0 +1,14 @@
1
+ import addressbook_pb2
2
+ import sys
3
+
4
+ person = addressbook_pb2.Person()
5
+ person.id = 1234
6
+ person.name = "John Doe"
7
+ person.email = "jdoe@example.com"
8
+ phone = person.phone.add()
9
+ phone.number = "555-4321"
10
+ phone.type = addressbook_pb2.Person.HOME
11
+
12
+ f = open('data.bin', 'wb')
13
+ f.write(person.SerializeToString())
14
+ f.close()
Binary file
@@ -0,0 +1,22 @@
1
+ import types_pb2
2
+ import sys
3
+
4
+ types = types_pb2.TestTypes()
5
+ types.type1 = 0.01
6
+ types.type2 = 0.1
7
+ types.type3 = 1
8
+ types.type4 = 10
9
+ types.type5 = 100
10
+ types.type6 = 1000
11
+ types.type7 = -1
12
+ types.type8 = -10
13
+ types.type9 = 10000
14
+ types.type10 = 100000
15
+ types.type11 = False
16
+ types.type12 = 'hello all types'
17
+ # TODO test type13
18
+ #types.type13 =
19
+
20
+ f = open('types.bin', 'wb')
21
+ f.write(types.SerializeToString())
22
+ f.close()
data/test/data/unk.png ADDED
Binary file
@@ -0,0 +1,66 @@
1
+ ### Generated by rprotoc. DO NOT EDIT!
2
+ ### <proto file: test/proto/addressbook.proto>
3
+ # package tutorial;
4
+ #
5
+ # message Person {
6
+ # required string name = 1;
7
+ # required int32 id = 2;
8
+ # optional string email = 3;
9
+ #
10
+ # enum PhoneType {
11
+ # MOBILE = 0;
12
+ # HOME = 1;
13
+ # WORK = 2;
14
+ # }
15
+ #
16
+ # message PhoneNumber {
17
+ # required string number = 1;
18
+ # optional PhoneType type = 2 [default = HOME];
19
+ # }
20
+ #
21
+ # repeated PhoneNumber phone = 4;
22
+ # optional uint32 age = 5 [default = 20];
23
+ #
24
+ # extensions 100 to 200;
25
+ # }
26
+ #
27
+ # /*
28
+ # extend Person {
29
+ # optional int32 age = 100;
30
+ # }
31
+ # */
32
+ #
33
+ # message AddressBook {
34
+ # repeated Person person = 1;
35
+ # }
36
+
37
+ require 'protobuf/message/message'
38
+ require 'protobuf/message/enum'
39
+ require 'protobuf/message/extend'
40
+
41
+ module Tutorial
42
+ class Person < ::Protobuf::Message
43
+ defined_in __FILE__
44
+ required :string, :name, 1
45
+ required :int32, :id, 2
46
+ optional :string, :email, 3
47
+ class PhoneType < ::Protobuf::Enum
48
+ defined_in __FILE__
49
+ define :MOBILE, 0
50
+ define :HOME, 1
51
+ define :WORK, 2
52
+ end
53
+ class PhoneNumber < ::Protobuf::Message
54
+ defined_in __FILE__
55
+ required :string, :number, 1
56
+ optional :PhoneType, :type, 2, :default => :HOME
57
+ end
58
+ repeated :PhoneNumber, :phone, 4
59
+ optional :uint32, :age, 5, :default => 20
60
+ extensions 100..200
61
+ end
62
+ class AddressBook < ::Protobuf::Message
63
+ defined_in __FILE__
64
+ repeated :Person, :person, 1
65
+ end
66
+ end