protobuf 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +75 -52
- data/UPGRADING.md +60 -0
- data/lib/protobuf/enum.rb +1 -1
- data/lib/protobuf/field.rb +7 -7
- data/lib/protobuf/field/base_field.rb +1 -0
- data/lib/protobuf/message.rb +12 -0
- data/lib/protobuf/rpc/server.rb +2 -1
- data/lib/protobuf/rpc/service_dispatcher.rb +8 -5
- data/lib/protobuf/rpc/stat.rb +1 -1
- data/lib/protobuf/version.rb +1 -1
- data/spec/lib/protobuf/enum_spec.rb +6 -0
- data/spec/lib/protobuf/message_spec.rb +68 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/support/test/enum.pb.rb +9 -7
- data/spec/support/test/resource.pb.rb +23 -16
- metadata +33 -32
data/README.md
CHANGED
@@ -1,21 +1,23 @@
|
|
1
1
|
# protobuf
|
2
2
|
|
3
|
-
|
3
|
+
***IMPORTANT: Those upgrading from version 1.4.2 to 2.0 should read the [UPGRADING.md](https://github.com/localshred/protobuf/blob/master/UPGRADING.md) notes***
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
Protobuf is an implementation of [Google's protocol buffers][google-pb] in ruby. We currently support version 2.4.0. It's a gem for managing 3 things:
|
6
|
+
|
7
|
+
1. Generating ruby classes from `.proto` files.
|
8
|
+
2. Provide an RPC mechanism for calling remote services.
|
9
|
+
3. Provide RPC interop between ruby and other protobuf-rpc aware implementations for different languages (e.g. [protobuf-socket-rpc][]).
|
8
10
|
|
9
11
|
So let's dive in and see how to work with all three.
|
10
12
|
|
11
|
-
## 1.
|
13
|
+
## 1. Generating ruby classes from `.proto` files
|
12
14
|
|
13
15
|
Protocol Buffers are great because they allow you to clearly define data storage or data transfer packets. Google officially supports Java, C++, and Python for compilation and usage. Let's make it ruby aware!
|
14
16
|
|
15
17
|
Let's say you have a `defs.proto` file that defines a User message.
|
16
18
|
|
17
19
|
```
|
18
|
-
package
|
20
|
+
package foo;
|
19
21
|
message User {
|
20
22
|
required string first_name = 1;
|
21
23
|
required string last_name = 1;
|
@@ -23,21 +25,23 @@ message User {
|
|
23
25
|
|
24
26
|
Now let's compile that definition to ruby:
|
25
27
|
|
26
|
-
$ rprotoc defs.proto
|
28
|
+
$ rprotoc defs.proto --ruby_out ./lib
|
27
29
|
```
|
28
30
|
|
29
|
-
The previous line will take whatever is defined in defs.proto and output ruby classes to the `./lib` directory, obeying the package directive. Assuming that's all defs.proto had defined, `./lib` should now look like this:
|
31
|
+
The previous line will take whatever is defined in `defs.proto` and output ruby classes to the `./lib` directory, obeying the package directive. Assuming that's all `defs.proto` had defined, `./lib` should now look like this:
|
30
32
|
|
31
33
|
```
|
32
34
|
- lib
|
33
|
-
|-
|
35
|
+
|- foo
|
34
36
|
|- defs.pb.rb
|
35
37
|
```
|
36
38
|
|
37
|
-
|
39
|
+
The generated file `defs.pb.rb` should look something like this:
|
38
40
|
|
39
41
|
```ruby
|
40
|
-
module
|
42
|
+
module Foo
|
43
|
+
class User < ::Protobuf::Message; end
|
44
|
+
|
41
45
|
class User
|
42
46
|
required :string, :first_name, 1
|
43
47
|
required :string, :last_name, 2
|
@@ -45,26 +49,41 @@ module Mycompany
|
|
45
49
|
end
|
46
50
|
```
|
47
51
|
|
48
|
-
|
52
|
+
_Note: The generator will pre-define all the classes empty and then re-open to apply the defined fields. This is an optomization to prevent recursive field errors._
|
53
|
+
|
54
|
+
The generated class is now just a plain old ruby object and you can use it however you wish.
|
49
55
|
|
50
56
|
```ruby
|
51
|
-
require 'lib/
|
57
|
+
require 'lib/foo/user.pb'
|
52
58
|
|
53
59
|
# dot notation reading/writing fields
|
54
|
-
user =
|
60
|
+
user = Foo::User.new
|
55
61
|
user.first_name = "Lloyd"
|
56
62
|
user.last_name = "Christmas"
|
57
63
|
user.first_name # => "Lloyd"
|
58
64
|
|
59
65
|
# or pass in the fields as a hash to the initializer
|
60
|
-
user =
|
66
|
+
user = Foo::User.new :first_name => "Lloyd", :last_name => "Christmas"
|
61
67
|
user.first_name # => Lloyd
|
62
68
|
user.last_name # => Christmas
|
63
69
|
```
|
64
70
|
|
71
|
+
### Message (de)serialization
|
72
|
+
|
73
|
+
Every message object comes ready for serialization or deserialization. Use `serialize_to_string` to write out the byte-string for the message. Use `parse_from_string` on a new message instance to inflate the byte-string back to a message in ruby.
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
user = Foo::User.new(:first_name => 'Bob')
|
77
|
+
bytes = user.serialize_to_string
|
78
|
+
puts bytes #=> binary representation of this message object
|
79
|
+
|
80
|
+
inflated_user = Foo::User.new.parse_from_string(bytes)
|
81
|
+
inflated_user == user #=> true
|
82
|
+
```
|
83
|
+
|
65
84
|
## 2. RPC
|
66
85
|
|
67
|
-
RPC is one of many technologies that tries to solve the problem of getting smaller pieces of data from one place to another. Many will argue for or against RPC and its usefulness, but I'm not going to do that here. Google's Protocol Buffers
|
86
|
+
RPC is one of many technologies that tries to solve the problem of getting smaller pieces of data from one place to another. Many will argue for or against RPC and its usefulness, but I'm not going to do that here. Google's Protocol Buffers provides support for Services with RPC and that's why you're here.
|
68
87
|
|
69
88
|
Any discussion about RPC leads to a discussion about clients and servers and the remote procedures themselves. For our purposes, we'll talk about a `Client` (process that is calling the server/service), a `Service` (the remote procedure), and a `Server` (the process that manages one or more services). We'll start with the Service first.
|
70
89
|
|
@@ -73,6 +92,7 @@ Any discussion about RPC leads to a discussion about clients and servers and the
|
|
73
92
|
Services are simply classes that have endpoint methods defined. Here's what one looks like in protobuf:
|
74
93
|
|
75
94
|
```
|
95
|
+
package foo;
|
76
96
|
message UserRequest {
|
77
97
|
optional string email = 1;
|
78
98
|
}
|
@@ -87,47 +107,53 @@ service UserService {
|
|
87
107
|
And the equivalent ruby stub for the service (generated with `rprotoc`):
|
88
108
|
|
89
109
|
```ruby
|
90
|
-
# lib/
|
91
|
-
module
|
92
|
-
|
110
|
+
# lib/foo/user.pb.rb
|
111
|
+
module Foo
|
112
|
+
# UserRequest and UserList Class definitions not shown (see above for generated output of classes).
|
113
|
+
|
114
|
+
class UserService < ::Protobuf::Rpc::Service
|
93
115
|
rpc :find, UserRequest, UserList
|
94
116
|
end
|
95
117
|
end
|
96
118
|
```
|
97
119
|
|
98
|
-
|
99
|
-
|
100
|
-
**Important Note: The *stubbed* class here is a *stub*. You should not alter it directly in any way as it will break your definition. Read on to learn how to use this stub.**
|
120
|
+
**Important Note: The UserService class here is a *stub*. You should not provide your implementation in this generated file as subsequent generations will wipe out your implmentation. Read on to learn how to use this stub.**
|
101
121
|
|
102
122
|
Did you read the note above? Go read it. I'll wait.
|
103
123
|
|
104
|
-
Ok, now that you have a
|
124
|
+
Ok, now that you have a generated service stub, you'll want to require it from `lib` and implement the methods. Create a service implementation file in your project. In rails I'd put this in `app/services/user_service.rb`.
|
105
125
|
|
106
126
|
```ruby
|
107
127
|
# app/services/user_service.rb
|
108
|
-
require 'lib/
|
109
|
-
|
128
|
+
require 'lib/foo/user.pb'
|
129
|
+
|
130
|
+
# Reopen the class and provide the implementation for each rpc method defined.
|
131
|
+
module Foo
|
110
132
|
class UserService
|
111
|
-
|
112
|
-
# request ->
|
113
|
-
# response ->
|
133
|
+
|
134
|
+
# request -> Foo::UserRequest
|
135
|
+
# response -> Foo::UserResponse
|
114
136
|
def find
|
115
137
|
# request.email will be the unpacked string that was sent by the client request
|
116
138
|
User.find_by_email(request.email).each do |user|
|
117
|
-
# must only use a proto instance of
|
139
|
+
# must only use a proto instance of Foo::User when appending to the `users` field
|
118
140
|
response.users << user.to_proto
|
119
141
|
end
|
120
142
|
end
|
121
|
-
|
143
|
+
|
122
144
|
end
|
123
145
|
end
|
124
146
|
```
|
125
147
|
|
126
|
-
Simply implement the instance method for the defined rpc.
|
148
|
+
Simply implement the instance method for the defined rpc. You can provide any other methods in this class as helpers, but only those defined in the proto file will be callable by remote clients. Every request made by a client will provide a non-empty request of the defined type. The server creates a new service instance based on the request, so you should not be constrained to just the endpoint method. This is similar to rails controllers where only methods defined by the routes file are hooked up to HTTP requests, but it's very common to implement private methods the aid in code quality and simpilicity.
|
127
149
|
|
128
|
-
|
150
|
+
Every instance has a `request` and `response` object used for fulfilling the call, again, similar to a rails controller action. You should never attempt to modify the `request` object. The `response` object however should be modified or replaced entirely. If you need to create your own response object (a valid case), simply use `respond_with(new_response)`. The returned object should conform to one of three properties:
|
129
151
|
|
130
|
-
|
152
|
+
1. Response should be of same type as defined by the rpc definition (in this case, `Foo::UserList`), or
|
153
|
+
2. Response should be a hash. This hash will be used to construct an instance of the defined type and should therefore conform to the appropriate fields for that type.
|
154
|
+
3. Response should respond to the `to_proto` method. The object returned by `to_proto` should be an instance of the defined response type.
|
155
|
+
|
156
|
+
If at any time the implementation encounters an error, the client can be instructed of the error using `rpc_failed`:
|
131
157
|
|
132
158
|
```ruby
|
133
159
|
#...
|
@@ -141,21 +167,19 @@ end
|
|
141
167
|
#...
|
142
168
|
```
|
143
169
|
|
144
|
-
This means that the client's `on_failure` callback will be invoked instead of the `on_success` callback. Read more below on client callbacks.
|
145
|
-
|
146
|
-
I find it very convenient to use a CRUD-style interface when defining certain data services, though this is certainly not always the case.
|
170
|
+
This means that the client's `on_failure` callback will be invoked instead of the `on_success` callback. Read more below on client callbacks. One drawback to the `rpc_failed` approach is that it does not short-circuit the rest of the method. This means that you must explicitly return from the method if you do not wish the remainder to be executed.
|
147
171
|
|
148
172
|
### Servers
|
149
173
|
|
150
174
|
A service is nothing without being hooked up to a socket. It's the nerdy kid waiting by the telephone for someone to call without knowing that the phone company disconnected their house. Sad and pathetic. So hook the phone lines!
|
151
175
|
|
152
176
|
```
|
153
|
-
$ rpc_server -o myserver.com -p 9939 -
|
177
|
+
$ rpc_server -o myserver.com -p 9939 -l ./log/protobuf.log ./config/environment.rb
|
154
178
|
```
|
155
179
|
|
156
|
-
The previous call will start an
|
180
|
+
The previous call will start an Socket server running on the given host and port which will load your application into memory. You certainly don't have to run rails or any other framework, just make sure you have some kind of file that will load your services all into memory. The server doesn't know where you put your code, so tell it.
|
157
181
|
|
158
|
-
Be aware that server needs to be able to translate the socket stream of bytes into an actual protobuf request object. If the definition for that request object aren't known to the server, you're going to have a long day getting this going. It's necessary to store all your definitions and their generated classes in a shared repository (read: gem) that both client and server have access to in their respective load paths.
|
182
|
+
Be aware that the server needs to be able to translate the socket stream of bytes into an actual protobuf request object. If the definition for that request object aren't known to the server, you're going to have a long day getting this going. It's necessary to store all your definitions and their generated classes in a shared repository (read: gem) so that both client and server have access to the ruby classes in their respective load paths.
|
159
183
|
|
160
184
|
Once the server starts, you should see it as a running process with `ps`. Sending a KILL, QUIT, or TERM signal to the pid will result in shutting the server down gracefully.
|
161
185
|
|
@@ -169,24 +193,24 @@ rpc_server shutdown
|
|
169
193
|
|
170
194
|
### Clients
|
171
195
|
|
172
|
-
A lot of work has gone into making the client calls simple and easy to use yet still powerful. Clients have a DSL that feels very
|
196
|
+
A lot of work has gone into making the client calls simple and easy to use yet still powerful. Clients have a DSL that feels very ajaxy.
|
173
197
|
|
174
198
|
```ruby
|
175
199
|
# require the defs from the shared gem/repo
|
176
|
-
require 'sharedgem/
|
177
|
-
require 'sharedgem/mycompany/user_service'
|
200
|
+
require 'sharedgem/foo/user.pb'
|
178
201
|
|
179
202
|
# Create a request object for the method we are invoking
|
180
|
-
req =
|
203
|
+
req = Foo::UserRequest.new(:email => 'jeff@gmail.com')
|
181
204
|
|
182
205
|
# Use the UserService class to generate a client, invoke the rpc method
|
183
|
-
# while passing the request object
|
184
|
-
|
206
|
+
# while passing the request object.
|
207
|
+
# We could also simply pass a hash to find.
|
208
|
+
Foo::UserService.client.find(req) do |c|
|
185
209
|
# This block will be executed (registering the callbacks)
|
186
210
|
# before the request actualy occurs.
|
187
211
|
# the `c` param in this block is the `.client` object
|
188
212
|
# that is generated from the call above
|
189
|
-
|
213
|
+
|
190
214
|
# Register a block for execution when the response
|
191
215
|
# is deemed successful from the service. Accepts
|
192
216
|
# the unpacked response as its only parameter
|
@@ -195,7 +219,7 @@ Mycompany::UserService.client.find(req) do |c|
|
|
195
219
|
puts u.inspect
|
196
220
|
end
|
197
221
|
end
|
198
|
-
|
222
|
+
|
199
223
|
# Register a block for execution when the response
|
200
224
|
# is deemed a failure. This can be either a client-side
|
201
225
|
# or server-side failure. The object passed the to the
|
@@ -207,22 +231,21 @@ Mycompany::UserService.client.find(req) do |c|
|
|
207
231
|
end
|
208
232
|
```
|
209
233
|
|
210
|
-
Many different options can be passed to the `.client` call above (such as `:
|
234
|
+
Many different options can be passed to the `.client` call above (such as `:timeout => 600`). See the `lib/protobuf/rpc/client.rb` and `lib/protobuf/rpc/service.rb` files for more documentation.
|
211
235
|
|
212
236
|
## 3. RPC Interop
|
213
237
|
|
214
|
-
The main reason I wrote this gem was to provide a ruby implementation to google's protobuf that worked on the RPC layer with a Java Service layer that was already running [protobuf-socket-rpc][], the supported socket rpc library for protobuf from Google.
|
238
|
+
The main reason I wrote this gem was to provide a ruby implementation to google's protobuf that worked on the RPC layer with a Java Service layer that was already running [protobuf-socket-rpc][], the supported socket rpc library for protobuf from Google. Other ruby protobuf implementations I've used did not provide this kind of support.
|
215
239
|
|
216
240
|
## Accreditation & Caveats
|
217
241
|
|
218
|
-
It must be noted
|
242
|
+
It must be noted that this gem was started originally as a fork of the [ruby-protobuf][old gem] gem. Its authors and I were unable to reach a communication point to be able to merge all of my RPC updates in with their master. Unfortunately I just simply couldn't use their RPC code and so I forked the code. Myself and others have significantly changed the internals of the gem, including the rpc implementation, the message/field implementation, and the compiler implementation. These changes were made to address glaring performance and quality issues in the code. The code was initially diverged at their 0.4.0 version.
|
219
243
|
|
220
244
|
It should also be noted that there are many more features I haven't really shown here, so please let me know if you have any questions on usage or support for various features. Happy protobufing.
|
221
245
|
|
222
|
-
-- BJ Neilsen, [@localshred][]
|
246
|
+
-- BJ Neilsen, [@localshred][]
|
223
247
|
|
224
248
|
[google-pb]: http://code.google.com/p/protobuf "Google Protocol Buffers"
|
225
249
|
[protobuf-socket-rpc]: http://code.google.com/p/protobuf-socket-rpc/ "Google's official Socket-RPC library for protobuf"
|
226
250
|
[old gem]: https://github.com/macks/ruby-protobuf "Macks ruby-protobuf on github"
|
227
251
|
[@localshred]: http://twitter.com/localshred "Follow on twitter @localshred"
|
228
|
-
[rand9.com]: http://rand9.com "Blog"
|
data/UPGRADING.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Upgrading from 1.X to 2.0
|
2
|
+
|
3
|
+
I'm sure I'm missing quite a few of the smaller changes that have been made, but here is a list of changes that should affect any external programs or applications using `protobuf`.
|
4
|
+
|
5
|
+
## `rprotoc` changes
|
6
|
+
|
7
|
+
* New option `--ruby_out` to specify the output directory to place generated ruby files. If not provided, ruby code will not be generated.
|
8
|
+
* Extends `libprotoc` to hook in directly to google's provided compiler mechanism.
|
9
|
+
* Removed all previous compiler code including the racc parser, node visitors, etc.
|
10
|
+
* See `protoc --help` for default options.
|
11
|
+
|
12
|
+
## `rprotoc` generated files changes
|
13
|
+
|
14
|
+
* Import `require`s now occur outside of any module or class declaration which solves ruby vm warnings previously seen.
|
15
|
+
* Empty inherited Message and Enum classes are pre-defined in the file, then reopened and their fields applied. This solves the issue of recursive field dependencies of two or more types in the same file.
|
16
|
+
* Generated DSL lines for message fields include the fully qualified name of the type (e.g. optional `::Protobuf::Field::StringField`, :name, 1)
|
17
|
+
* Support for any combination of `packed`, `deprecated`, and `default` as options to pass to a field definition.
|
18
|
+
* Services are now generated in the corresponding `.pb.rb` file instead of their own `*_service.rb` files as before.
|
19
|
+
|
20
|
+
## `rpc_server` changes
|
21
|
+
|
22
|
+
* Removed `--env` option. The running application or program is solely in charge of ensuring it's environment is properly loaded.
|
23
|
+
* Removed reading of `PB_CLIENT_TYPE`, `PB_SERVER_TYPE` environment variables. Should use mode switches or custom requires (see below) instead.
|
24
|
+
* Removed `--client_socket` in favor of using mode switches. This also means client calls made by the `rpc_server` will run as the same connector type as the given mode (socket, zmq, or evented).
|
25
|
+
* Removed `--pre-cache-definitions` switch in favor of always pre-caching for performance.
|
26
|
+
* Removed `--gc-pause-serialization` since using `--gc-pause-request` in conjunction was redundant.
|
27
|
+
* Removed `--client-type` in favor of mode switches.
|
28
|
+
* Removed `--server-type` in favor of mode switches.
|
29
|
+
* Added mode switch `--evented`.
|
30
|
+
* Added `--threads` to specify number of ZMQ Worker threads to use. Ignored if mode is not zmq.
|
31
|
+
* Added `--print-deprecation-warnings` switch to tell the server whether or not to print deprecation warnings on field usage. Enabled by default.
|
32
|
+
* See `rpc_server help start` for all options and usage. Note: the `start` task is the default and not necessary when running the `rpc_server`.
|
33
|
+
|
34
|
+
## Message changes
|
35
|
+
|
36
|
+
* `Message#get_field` usage should now specify either `Message#get_field_by_name` or `Message#get_field_by_tag`, depending on your lookup criteria.
|
37
|
+
* Support for STDERR output when accessing a message field which has been defined as `[deprecated=true]`. Deprecated warnings can be skipped by running your application or program with `PB_IGNORE_DEPRECATIONS=1`.
|
38
|
+
* Significant internal refactoring which provides huge boosts in speed and efficiency both in accessing/writing Message field values, as well as serialization and deserialization routines.
|
39
|
+
* Refactor `Message#to_hash` to delegate hash representations to the field values, simply collecting the display values and returning a hash of fields that are set. This also affects `to_json` output.
|
40
|
+
|
41
|
+
## Enum changes
|
42
|
+
|
43
|
+
* Add `Enum.fetch` class method to polymorphically retrieve an `EnumValue` object.
|
44
|
+
* Add `Enum.value_by_name` to retrieve the corresponding `EnumValue` to the given symbol name.
|
45
|
+
* Add `Enum.enum_by_value` to retrieve the corresponding `EnumValue` to the given integer value.
|
46
|
+
|
47
|
+
## RPC Service changes
|
48
|
+
|
49
|
+
* `async_responder` paradigm is no longer supported.
|
50
|
+
* `self.response=` paradigm should be converted to using `respond_with(object)`.
|
51
|
+
* Significant internal changes that should not bleed beyond the API but which make maintaining the code much easier.
|
52
|
+
|
53
|
+
## RPC Client changes
|
54
|
+
|
55
|
+
* In the absence of `PB_CLIENT_TYPE` environment var, you should be requiring the specific connector type specifically. For instance, if you wish to run in zmq mode for client requests, update your Gemfile: `gem 'protobuf', :require => 'protobuf/zmq'`.
|
56
|
+
* `:async` option on client calls is no longer recognized.
|
57
|
+
|
58
|
+
## Other changes
|
59
|
+
|
60
|
+
* Moved files out of `lib/protobuf/common` folder into `lib/protobuf`. Files affected are logger, wire_type, util. The only update would need to be the require path to these files since the modules were always `Protobuf::{TYPE}`.
|
data/lib/protobuf/enum.rb
CHANGED
data/lib/protobuf/field.rb
CHANGED
@@ -24,25 +24,25 @@ require 'protobuf/field/extension_fields'
|
|
24
24
|
module Protobuf
|
25
25
|
module Field
|
26
26
|
PREDEFINED_TYPES = [
|
27
|
-
::Protobuf::Field::DoubleField,
|
27
|
+
::Protobuf::Field::DoubleField,
|
28
28
|
::Protobuf::Field::FloatField,
|
29
|
-
::Protobuf::Field::Int32Field,
|
29
|
+
::Protobuf::Field::Int32Field,
|
30
30
|
::Protobuf::Field::Int64Field,
|
31
|
-
::Protobuf::Field::Uint32Field,
|
31
|
+
::Protobuf::Field::Uint32Field,
|
32
32
|
::Protobuf::Field::Uint64Field,
|
33
|
-
::Protobuf::Field::Sint32Field,
|
33
|
+
::Protobuf::Field::Sint32Field,
|
34
34
|
::Protobuf::Field::Sint64Field,
|
35
35
|
::Protobuf::Field::Fixed32Field,
|
36
36
|
::Protobuf::Field::Fixed64Field,
|
37
|
-
::Protobuf::Field::Sfixed32Field,
|
37
|
+
::Protobuf::Field::Sfixed32Field,
|
38
38
|
::Protobuf::Field::Sfixed64Field,
|
39
|
-
::Protobuf::Field::StringField,
|
39
|
+
::Protobuf::Field::StringField,
|
40
40
|
::Protobuf::Field::BytesField,
|
41
41
|
::Protobuf::Field::BoolField
|
42
42
|
].freeze
|
43
43
|
|
44
44
|
def self.build(message_class, rule, type, name, tag, options={})
|
45
|
-
field_class = type_message_or_enum(type)
|
45
|
+
field_class = type_message_or_enum(type)
|
46
46
|
field_class.new(message_class, rule, type, name, tag, options)
|
47
47
|
end
|
48
48
|
|
data/lib/protobuf/message.rb
CHANGED
@@ -6,6 +6,9 @@ require 'protobuf/message/encoder'
|
|
6
6
|
|
7
7
|
module Protobuf
|
8
8
|
class Message
|
9
|
+
|
10
|
+
class FieldNotDefinedError < StandardError; end
|
11
|
+
|
9
12
|
STRING_ENCODING = "ASCII-8BIT".freeze
|
10
13
|
|
11
14
|
def self.all_fields
|
@@ -80,16 +83,25 @@ module Protobuf
|
|
80
83
|
def self.get_field_by_name(name)
|
81
84
|
# Check if the name has been used before, if not then set it to the sym value
|
82
85
|
fields[field_name_to_tag[name]]
|
86
|
+
rescue TypeError => e
|
87
|
+
name = 'nil' if name.nil?
|
88
|
+
raise FieldNotDefinedError.new("Field '#{name}' is not defined on message '#{self.name}'")
|
83
89
|
end
|
84
90
|
|
85
91
|
# Find a field object by +tag+ number.
|
86
92
|
def self.get_field_by_tag(tag)
|
87
93
|
fields[tag]
|
94
|
+
rescue TypeError => e
|
95
|
+
tag = tag.nil? ? 'nil' : tag.to_s
|
96
|
+
raise FieldNotDefinedError.new("Tag '#{tag}' does not reference a message field for '#{self.name}'")
|
88
97
|
end
|
89
98
|
|
90
99
|
def self.get_ext_field_by_name(name)
|
91
100
|
# Check if the name has been used before, if not then set it to the sym value
|
92
101
|
extension_fields[extension_field_name_to_tag[name]]
|
102
|
+
rescue TypeError => e
|
103
|
+
name = 'nil' if name.nil?
|
104
|
+
raise FieldNotDefinedError.new("Field '#{name}' is not defined on message '#{self.name}'")
|
93
105
|
end
|
94
106
|
|
95
107
|
def self.get_ext_field_by_tag(tag)
|
data/lib/protobuf/rpc/server.rb
CHANGED
@@ -78,8 +78,9 @@ module Protobuf
|
|
78
78
|
def send_response
|
79
79
|
log_debug { sign_message("Sending response to client: #{@response.inspect}") }
|
80
80
|
send_data
|
81
|
-
@stats.stop && log_info { @stats.to_s }
|
82
81
|
ensure
|
82
|
+
@stats.stop
|
83
|
+
log_info { @stats.to_s }
|
83
84
|
enable_gc!
|
84
85
|
end
|
85
86
|
end
|
@@ -53,12 +53,15 @@ module Protobuf
|
|
53
53
|
def coerced_response
|
54
54
|
candidate = service.response
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
elsif candidate.respond_to?(:to_proto)
|
56
|
+
case
|
57
|
+
when candidate.is_a?(::Protobuf::Message) then
|
58
|
+
# no-op
|
59
|
+
when candidate.respond_to?(:to_proto) then
|
61
60
|
candidate = candidate.to_proto
|
61
|
+
when candidate.respond_to?(:to_proto_hash) then
|
62
|
+
candidate = definition.response_type.new(candidate.to_proto_hash)
|
63
|
+
when candidate.respond_to?(:to_hash) then
|
64
|
+
candidate = definition.response_type.new(candidate.to_hash)
|
62
65
|
end
|
63
66
|
|
64
67
|
candidate
|
data/lib/protobuf/rpc/stat.rb
CHANGED
data/lib/protobuf/version.rb
CHANGED
@@ -51,6 +51,12 @@ describe Protobuf::Enum do
|
|
51
51
|
it 'gets the name of the enum corresponding to the given value (tag)' do
|
52
52
|
Test::EnumTestType.name_by_value(tag).should eq name
|
53
53
|
end
|
54
|
+
|
55
|
+
context 'when given name is nil' do
|
56
|
+
it 'returns a nil' do
|
57
|
+
Test::EnumTestType.name_by_value(nil).should be_nil
|
58
|
+
end
|
59
|
+
end
|
54
60
|
end
|
55
61
|
|
56
62
|
describe '.valid_tag?' do
|
@@ -47,4 +47,72 @@ describe Protobuf::Message do
|
|
47
47
|
its(:to_json) { should eq '{"name":"Test Name","active":false}' }
|
48
48
|
end
|
49
49
|
|
50
|
+
describe '#get_field_by_name' do
|
51
|
+
subject do
|
52
|
+
::Test::Resource.new({ :name => 'Test Name', :date_created => Time.now.to_i })
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when name is a valid field' do
|
56
|
+
let(:valid_field) { subject.get_field_by_name(:name) }
|
57
|
+
specify { valid_field.should be_a ::Protobuf::Field::StringField }
|
58
|
+
specify { valid_field.name.should eq :name }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when name is not a valid field' do
|
62
|
+
specify do
|
63
|
+
expect {
|
64
|
+
subject.get_field_by_name(1)
|
65
|
+
}.to raise_error(::Protobuf::Message::FieldNotDefinedError, /.*1.*#{subject.class.name}/)
|
66
|
+
end
|
67
|
+
|
68
|
+
specify do
|
69
|
+
expect {
|
70
|
+
subject.get_field_by_name(:nothere)
|
71
|
+
}.to raise_error(::Protobuf::Message::FieldNotDefinedError, /.*nothere.*#{subject.class.name}/)
|
72
|
+
end
|
73
|
+
|
74
|
+
specify do
|
75
|
+
expect {
|
76
|
+
subject.get_field_by_name(nil)
|
77
|
+
}.to raise_error(::Protobuf::Message::FieldNotDefinedError, /.*nil.*#{subject.class.name}/)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#get_ext_field_by_name' do
|
83
|
+
pending 'Need to get a proto compiled with extensions first'
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#get_field_by_tag' do
|
87
|
+
subject do
|
88
|
+
::Test::Resource.new({ :name => 'Test Name', :date_created => Time.now.to_i })
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'when tag references a valid field' do
|
92
|
+
let(:valid_field) { subject.get_field_by_tag(1) }
|
93
|
+
specify { valid_field.should be_a ::Protobuf::Field::StringField }
|
94
|
+
specify { valid_field.name.should eq :name }
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'when tag does not reference a field' do
|
98
|
+
it 'returns nil' do
|
99
|
+
subject.get_field_by_tag(-1).should be_nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'when tag is not numeric' do
|
104
|
+
specify do
|
105
|
+
expect {
|
106
|
+
subject.get_field_by_tag("not a number")
|
107
|
+
}.to raise_error(::Protobuf::Message::FieldNotDefinedError, /.*not a number.*#{subject.class.name}/)
|
108
|
+
end
|
109
|
+
|
110
|
+
specify do
|
111
|
+
expect {
|
112
|
+
subject.get_field_by_tag(nil)
|
113
|
+
}.to raise_error(::Protobuf::Message::FieldNotDefinedError, /.*nil.*#{subject.class.name}/)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
50
118
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -26,7 +26,9 @@ end
|
|
26
26
|
unless ENV['NO_COMPILE_TEST_PROTOS']
|
27
27
|
$stdout.puts 'Compiling test protos (use NO_COMPILE_TEST_PROTOS=1 to skip)'
|
28
28
|
proto_path = File.expand_path("../support/", __FILE__)
|
29
|
-
%
|
29
|
+
cmd = %Q{ rprotoc --proto_path=#{proto_path} --ruby_out=#{proto_path} #{File.join(proto_path, '**', '*.proto')} }
|
30
|
+
puts cmd
|
31
|
+
%x{#{cmd}}
|
30
32
|
end
|
31
33
|
end
|
32
34
|
end
|
@@ -17,16 +17,18 @@ module Test
|
|
17
17
|
##
|
18
18
|
# Enum Values
|
19
19
|
#
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
class EnumTestType
|
21
|
+
define :ONE, 1
|
22
|
+
define :TWO, 2
|
23
|
+
end
|
23
24
|
|
24
25
|
##
|
25
26
|
# Message Fields
|
26
27
|
#
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
class EnumTestMessage
|
29
|
+
optional ::Test::EnumTestType, :non_default_enum, 1
|
30
|
+
optional ::Test::EnumTestType, :default_enum, 2, :default => ::Test::EnumTestType::ONE
|
31
|
+
repeated ::Test::EnumTestType, :repeated_enums, 3
|
32
|
+
end
|
31
33
|
|
32
34
|
end
|
@@ -15,33 +15,40 @@ module Test
|
|
15
15
|
#
|
16
16
|
class ResourceFindRequest < ::Protobuf::Message; end
|
17
17
|
class Resource < ::Protobuf::Message; end
|
18
|
+
class Extended < ::Protobuf::Message; end
|
18
19
|
class Nested < ::Protobuf::Message; end
|
19
20
|
|
20
21
|
##
|
21
22
|
# Enum Values
|
22
23
|
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
class StatusType
|
25
|
+
define :PENDING, 0
|
26
|
+
define :ENABLED, 1
|
27
|
+
define :DISABLED, 2
|
28
|
+
define :DELETED, 3
|
29
|
+
end
|
28
30
|
|
29
31
|
##
|
30
32
|
# Message Fields
|
31
33
|
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
::Test::Resource.optional(::Protobuf::Field::Int64Field, :date_created, 2)
|
37
|
-
::Test::Resource.optional(::Test::StatusType, :status, 3)
|
38
|
-
::Test::Resource.repeated(::Test::StatusType, :repeated_enum, 4)
|
34
|
+
class ResourceFindRequest
|
35
|
+
required ::Protobuf::Field::StringField, :name, 1
|
36
|
+
optional ::Protobuf::Field::BoolField, :active, 2
|
37
|
+
end
|
39
38
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
class Resource
|
40
|
+
required ::Protobuf::Field::StringField, :name, 1
|
41
|
+
optional ::Protobuf::Field::Int64Field, :date_created, 2
|
42
|
+
optional ::Test::StatusType, :status, 3
|
43
|
+
repeated ::Test::StatusType, :repeated_enum, 4
|
44
|
+
end
|
44
45
|
|
46
|
+
class Nested
|
47
|
+
optional ::Protobuf::Field::StringField, :name, 1
|
48
|
+
optional ::Test::Resource, :resource, 2
|
49
|
+
repeated ::Test::Resource, :multiple_resources, 3
|
50
|
+
optional ::Test::StatusType, :status, 4
|
51
|
+
end
|
45
52
|
|
46
53
|
##
|
47
54
|
# Services
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protobuf
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-10-
|
13
|
+
date: 2012-10-29 00:00:00.000000000Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
17
|
-
requirement: &
|
17
|
+
requirement: &2152132920 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *2152132920
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: ffi
|
28
|
-
requirement: &
|
28
|
+
requirement: &2152129180 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: '0'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *2152129180
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: multi_json
|
39
|
-
requirement: &
|
39
|
+
requirement: &2152127660 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,10 +44,10 @@ dependencies:
|
|
44
44
|
version: '0'
|
45
45
|
type: :runtime
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *2152127660
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: thor
|
50
|
-
requirement: &
|
50
|
+
requirement: &2152126820 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ! '>='
|
@@ -55,10 +55,10 @@ dependencies:
|
|
55
55
|
version: '0'
|
56
56
|
type: :runtime
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *2152126820
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: eventmachine
|
61
|
-
requirement: &
|
61
|
+
requirement: &2152125000 !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
64
64
|
- - ! '>='
|
@@ -66,10 +66,10 @@ dependencies:
|
|
66
66
|
version: '0'
|
67
67
|
type: :development
|
68
68
|
prerelease: false
|
69
|
-
version_requirements: *
|
69
|
+
version_requirements: *2152125000
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: ffi-rzmq
|
72
|
-
requirement: &
|
72
|
+
requirement: &2152121980 !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
75
|
- - ! '>='
|
@@ -77,10 +77,10 @@ dependencies:
|
|
77
77
|
version: '0'
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
|
-
version_requirements: *
|
80
|
+
version_requirements: *2152121980
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
82
|
name: perftools.rb
|
83
|
-
requirement: &
|
83
|
+
requirement: &2152112840 !ruby/object:Gem::Requirement
|
84
84
|
none: false
|
85
85
|
requirements:
|
86
86
|
- - ! '>='
|
@@ -88,10 +88,10 @@ dependencies:
|
|
88
88
|
version: '0'
|
89
89
|
type: :development
|
90
90
|
prerelease: false
|
91
|
-
version_requirements: *
|
91
|
+
version_requirements: *2152112840
|
92
92
|
- !ruby/object:Gem::Dependency
|
93
93
|
name: pry
|
94
|
-
requirement: &
|
94
|
+
requirement: &2152111420 !ruby/object:Gem::Requirement
|
95
95
|
none: false
|
96
96
|
requirements:
|
97
97
|
- - ! '>='
|
@@ -99,10 +99,10 @@ dependencies:
|
|
99
99
|
version: '0'
|
100
100
|
type: :development
|
101
101
|
prerelease: false
|
102
|
-
version_requirements: *
|
102
|
+
version_requirements: *2152111420
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
104
|
name: pry-nav
|
105
|
-
requirement: &
|
105
|
+
requirement: &2152110580 !ruby/object:Gem::Requirement
|
106
106
|
none: false
|
107
107
|
requirements:
|
108
108
|
- - ! '>='
|
@@ -110,10 +110,10 @@ dependencies:
|
|
110
110
|
version: '0'
|
111
111
|
type: :development
|
112
112
|
prerelease: false
|
113
|
-
version_requirements: *
|
113
|
+
version_requirements: *2152110580
|
114
114
|
- !ruby/object:Gem::Dependency
|
115
115
|
name: rake
|
116
|
-
requirement: &
|
116
|
+
requirement: &2152109440 !ruby/object:Gem::Requirement
|
117
117
|
none: false
|
118
118
|
requirements:
|
119
119
|
- - ! '>='
|
@@ -121,10 +121,10 @@ dependencies:
|
|
121
121
|
version: '0'
|
122
122
|
type: :development
|
123
123
|
prerelease: false
|
124
|
-
version_requirements: *
|
124
|
+
version_requirements: *2152109440
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: rake-compiler
|
127
|
-
requirement: &
|
127
|
+
requirement: &2152108860 !ruby/object:Gem::Requirement
|
128
128
|
none: false
|
129
129
|
requirements:
|
130
130
|
- - ! '>='
|
@@ -132,10 +132,10 @@ dependencies:
|
|
132
132
|
version: '0'
|
133
133
|
type: :development
|
134
134
|
prerelease: false
|
135
|
-
version_requirements: *
|
135
|
+
version_requirements: *2152108860
|
136
136
|
- !ruby/object:Gem::Dependency
|
137
137
|
name: rspec
|
138
|
-
requirement: &
|
138
|
+
requirement: &2152101880 !ruby/object:Gem::Requirement
|
139
139
|
none: false
|
140
140
|
requirements:
|
141
141
|
- - ! '>='
|
@@ -143,10 +143,10 @@ dependencies:
|
|
143
143
|
version: '0'
|
144
144
|
type: :development
|
145
145
|
prerelease: false
|
146
|
-
version_requirements: *
|
146
|
+
version_requirements: *2152101880
|
147
147
|
- !ruby/object:Gem::Dependency
|
148
148
|
name: simplecov
|
149
|
-
requirement: &
|
149
|
+
requirement: &2152101000 !ruby/object:Gem::Requirement
|
150
150
|
none: false
|
151
151
|
requirements:
|
152
152
|
- - ! '>='
|
@@ -154,10 +154,10 @@ dependencies:
|
|
154
154
|
version: '0'
|
155
155
|
type: :development
|
156
156
|
prerelease: false
|
157
|
-
version_requirements: *
|
157
|
+
version_requirements: *2152101000
|
158
158
|
- !ruby/object:Gem::Dependency
|
159
159
|
name: yard
|
160
|
-
requirement: &
|
160
|
+
requirement: &2152100220 !ruby/object:Gem::Requirement
|
161
161
|
none: false
|
162
162
|
requirements:
|
163
163
|
- - ! '>='
|
@@ -165,7 +165,7 @@ dependencies:
|
|
165
165
|
version: '0'
|
166
166
|
type: :development
|
167
167
|
prerelease: false
|
168
|
-
version_requirements: *
|
168
|
+
version_requirements: *2152100220
|
169
169
|
description: Google Protocol Buffers v2.4.1 Serialization and RPC implementation for
|
170
170
|
Ruby.
|
171
171
|
email:
|
@@ -183,6 +183,7 @@ files:
|
|
183
183
|
- Gemfile
|
184
184
|
- README.md
|
185
185
|
- Rakefile
|
186
|
+
- UPGRADING.md
|
186
187
|
- bin/rpc_server
|
187
188
|
- bin/rprotoc
|
188
189
|
- examples/addressbook.pb.rb
|
@@ -443,7 +444,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
443
444
|
version: '0'
|
444
445
|
segments:
|
445
446
|
- 0
|
446
|
-
hash:
|
447
|
+
hash: -2086976779931604579
|
447
448
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
448
449
|
none: false
|
449
450
|
requirements:
|
@@ -452,7 +453,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
452
453
|
version: '0'
|
453
454
|
segments:
|
454
455
|
- 0
|
455
|
-
hash:
|
456
|
+
hash: -2086976779931604579
|
456
457
|
requirements: []
|
457
458
|
rubyforge_project:
|
458
459
|
rubygems_version: 1.8.15
|