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 CHANGED
@@ -1,21 +1,23 @@
1
1
  # protobuf
2
2
 
3
- Protobuf is an implementation of [Google's protocol buffers][google-pb] in ruby. It's a gem for managing 3 things:
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
- 1. Compiling `.proto` definitions to ruby
6
- 2. Provide a Socket-RPC mechanism for calling services
7
- 3. Provide RPC interop between ruby and other protobuf-rpc aware implementations for different languages (e.g. [protobuf-socket-rpc][])
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. Compile `.proto` definitions to ruby
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 mycompany;
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 -o ./lib
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
- |- mycompany
35
+ |- foo
34
36
  |- defs.pb.rb
35
37
  ```
36
38
 
37
- And `defs.pb.rb` should look like this:
39
+ The generated file `defs.pb.rb` should look something like this:
38
40
 
39
41
  ```ruby
40
- module Mycompany
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
- You can then use that class just like normal:
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/mycompany/user.pb'
57
+ require 'lib/foo/user.pb'
52
58
 
53
59
  # dot notation reading/writing fields
54
- user = Mycompany::User.new
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 = Mycompany::User.new :first_name => "Lloyd", :last_name => "Christmas"
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 relies on RPC and that's why you're here.
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/mycompany/user_service.rb
91
- module Mycompany
92
- class UserService < Protobuf::Rpc::Service
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
- Recognize that the extra messages would actually have gone into the `defs.pb.rb` file while the service stub would receive it's own file at `user_service.rb`.
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 compiled service stub, you'll want to require it from `lib` and implement the methods. You'll notice when you compile the stub there is a large comment at the top of the file. You can use this code comment to start your real implementation. Go ahead and copy it to your services directory (probably `app/services` if we're in rails).
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/mycompany/user_service'
109
- module Mycompany
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 -> Mycompany::UserRequest
113
- # response -> Mycompany::UserResponse
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 Mycompany::User when appending to the `users` field
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. No other methods will be allowed in the class (even helpers or private methods). An implicit `request` and `response` object are provided for you, pre-instantiated, and in the case of the request, already are populated with the data that was sent by the client.
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
- If you need to create your own response object (a valid case), be sure to assign it back to the instance by using `self.response = your_response_obj`. The object you assign **MUST** be of the defined return type, in this case `Mycompany::UserList`. Any other type will result in an error.
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
- Triggering an error from the service is simple:
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 -e production -l ./log/protobuf.log config/environment.rb
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 EventMachine 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.
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 ajax-ish, mostly because of the nature of EventMachine, but I also think it works quite well.
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/mycompany/user.pb'
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 = Mycompany::UserRequest.new(:email => 'jeff@gmail.com')
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
- Mycompany::UserService.client.find(req) do |c|
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 `:async => true` or `:timeout => 600`). See the `lib/protobuf/rpc/client.rb` and `lib/protobuf/rpc/service.rb` files for more documentation. It should be noted that the default behavior of `UserService.client` is to return a blocking client. The nature of using Client calls within a framework like Rails demands a blocking call if the response of a web request is dependent on data returned from the service.
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. The [old gem][] did not provide a very robust RPC implementation and it most certainly did not work with the Java stack.
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 a large amount of the code in this library was taken from 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've decided to diverge from their codeset. I take no credit whatsoever for the (de)serialization and `rprotoc` code generation original work, though I have modified it slightly to be more compliant with my understanding of the pb spec. I want to say thanks to the original devs for the good work they did to get me most of the way there. The code was initially diverged at their 0.4.0 version.
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][], [rand9.com][]
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
@@ -32,7 +32,7 @@ module Protobuf
32
32
  end
33
33
 
34
34
  def self.name_by_value(value)
35
- @names[value]
35
+ value.nil? ? nil : @names[value]
36
36
  end
37
37
 
38
38
  def self.valid_tag?(tag)
@@ -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
 
@@ -184,6 +184,7 @@ module Protobuf
184
184
  field = self
185
185
  @message_class.class_eval do
186
186
  define_method(field.getter_method_name) do
187
+ field.warn_if_deprecated
187
188
  @values[field.name] ||= ::Protobuf::Field::FieldArray.new(field)
188
189
  end
189
190
  end
@@ -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)
@@ -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
- if candidate.is_a?(Hash)
57
- candidate = definition.response_type.new(candidate)
58
- elsif candidate.respond_to?(:to_hash)
59
- candidate = definition.response_type.new(candidate.to_hash)
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
@@ -69,7 +69,7 @@ module Protobuf
69
69
 
70
70
  def to_s
71
71
  [
72
- server? ? "[SRV-#{self.class}]" : "[CLT-#{self.class}]",
72
+ server? ? "[SRV]" : "[CLT]",
73
73
  rpc,
74
74
  elapsed_time,
75
75
  sizes,
@@ -1,4 +1,4 @@
1
1
  module Protobuf
2
- VERSION = '2.0.0'
2
+ VERSION = '2.0.1'
3
3
  PROTOC_VERSION = '2.4.1'
4
4
  end
@@ -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
- %x{ rprotoc --proto_path=#{proto_path} --ruby_out=#{proto_path} #{File.join(proto_path, '**', '*.proto')} }
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
- ::Test::EnumTestType.define :ONE, 1
21
- ::Test::EnumTestType.define :TWO, 2
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
- ::Test::EnumTestMessage.optional(::Test::EnumTestType, :non_default_enum, 1)
28
- ::Test::EnumTestMessage.optional(::Test::EnumTestType, :default_enum, 2, :default => ::Test::EnumTestType::ONE)
29
- ::Test::EnumTestMessage.repeated(::Test::EnumTestType, :repeated_enums, 3)
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
- ::Test::StatusType.define :PENDING, 0
24
- ::Test::StatusType.define :ENABLED, 1
25
- ::Test::StatusType.define :DISABLED, 2
26
- ::Test::StatusType.define :DELETED, 3
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
- ::Test::ResourceFindRequest.required(::Protobuf::Field::StringField, :name, 1)
33
- ::Test::ResourceFindRequest.optional(::Protobuf::Field::BoolField, :active, 2)
34
-
35
- ::Test::Resource.required(::Protobuf::Field::StringField, :name, 1)
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
- ::Test::Nested.optional(::Protobuf::Field::StringField, :name, 1)
41
- ::Test::Nested.optional(::Test::Resource, :resource, 2)
42
- ::Test::Nested.repeated(::Test::Resource, :multiple_resources, 3)
43
- ::Test::Nested.optional(::Test::StatusType, :status, 4)
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.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-23 00:00:00.000000000Z
13
+ date: 2012-10-29 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
17
- requirement: &2152084280 !ruby/object:Gem::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: *2152084280
25
+ version_requirements: *2152132920
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: ffi
28
- requirement: &2152082200 !ruby/object:Gem::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: *2152082200
36
+ version_requirements: *2152129180
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: multi_json
39
- requirement: &2152079980 !ruby/object:Gem::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: *2152079980
47
+ version_requirements: *2152127660
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: thor
50
- requirement: &2152079060 !ruby/object:Gem::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: *2152079060
58
+ version_requirements: *2152126820
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: eventmachine
61
- requirement: &2152077380 !ruby/object:Gem::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: *2152077380
69
+ version_requirements: *2152125000
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: ffi-rzmq
72
- requirement: &2152076200 !ruby/object:Gem::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: *2152076200
80
+ version_requirements: *2152121980
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: perftools.rb
83
- requirement: &2152074580 !ruby/object:Gem::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: *2152074580
91
+ version_requirements: *2152112840
92
92
  - !ruby/object:Gem::Dependency
93
93
  name: pry
94
- requirement: &2152073400 !ruby/object:Gem::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: *2152073400
102
+ version_requirements: *2152111420
103
103
  - !ruby/object:Gem::Dependency
104
104
  name: pry-nav
105
- requirement: &2152067360 !ruby/object:Gem::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: *2152067360
113
+ version_requirements: *2152110580
114
114
  - !ruby/object:Gem::Dependency
115
115
  name: rake
116
- requirement: &2152066440 !ruby/object:Gem::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: *2152066440
124
+ version_requirements: *2152109440
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: rake-compiler
127
- requirement: &2152065680 !ruby/object:Gem::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: *2152065680
135
+ version_requirements: *2152108860
136
136
  - !ruby/object:Gem::Dependency
137
137
  name: rspec
138
- requirement: &2152064960 !ruby/object:Gem::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: *2152064960
146
+ version_requirements: *2152101880
147
147
  - !ruby/object:Gem::Dependency
148
148
  name: simplecov
149
- requirement: &2152063940 !ruby/object:Gem::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: *2152063940
157
+ version_requirements: *2152101000
158
158
  - !ruby/object:Gem::Dependency
159
159
  name: yard
160
- requirement: &2152062660 !ruby/object:Gem::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: *2152062660
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: 1241353488378730897
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: 1241353488378730897
456
+ hash: -2086976779931604579
456
457
  requirements: []
457
458
  rubyforge_project:
458
459
  rubygems_version: 1.8.15