protobuf 2.2.5-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (256) hide show
  1. data/.gitignore +17 -0
  2. data/.travis.yml +9 -0
  3. data/.yardopts +5 -0
  4. data/Gemfile +3 -0
  5. data/README.md +316 -0
  6. data/Rakefile +29 -0
  7. data/UPGRADING.md +60 -0
  8. data/bin/rpc_server +5 -0
  9. data/bin/rprotoc +62 -0
  10. data/examples/addressbook.pb.rb +55 -0
  11. data/examples/addressbook.proto +24 -0
  12. data/examples/reading_a_message.rb +32 -0
  13. data/examples/writing_a_message.rb +46 -0
  14. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/code_generator.h +142 -0
  15. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/command_line_interface.h +318 -0
  16. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_enum.h +99 -0
  17. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_enum_field.h +103 -0
  18. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_extension.h +85 -0
  19. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_field.h +167 -0
  20. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_file.h +98 -0
  21. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_generator.h +72 -0
  22. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_helpers.h +159 -0
  23. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_message.h +170 -0
  24. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_message_field.h +102 -0
  25. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_primitive_field.h +103 -0
  26. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_service.h +118 -0
  27. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_string_field.h +104 -0
  28. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.h +2721 -0
  29. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/importer.h +303 -0
  30. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_enum.h +84 -0
  31. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_enum_field.h +121 -0
  32. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_extension.h +77 -0
  33. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_field.h +108 -0
  34. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_file.h +101 -0
  35. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_generator.h +72 -0
  36. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_helpers.h +213 -0
  37. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_message.h +109 -0
  38. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_message_field.h +134 -0
  39. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_primitive_field.h +121 -0
  40. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_service.h +113 -0
  41. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/java/java_string_field.h +120 -0
  42. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/mock_code_generator.h +113 -0
  43. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/package_info.h +64 -0
  44. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/parser.h +434 -0
  45. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/plugin.h +73 -0
  46. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/plugin.pb.h +790 -0
  47. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/python/python_generator.h +156 -0
  48. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/subprocess.h +108 -0
  49. data/ext/protobuf-2.4.1/src/google/protobuf/compiler/zip_writer.h +93 -0
  50. data/ext/protobuf-2.4.1/src/google/protobuf/descriptor.h +1367 -0
  51. data/ext/protobuf-2.4.1/src/google/protobuf/descriptor.pb.h +5223 -0
  52. data/ext/protobuf-2.4.1/src/google/protobuf/descriptor_database.h +366 -0
  53. data/ext/protobuf-2.4.1/src/google/protobuf/dynamic_message.h +136 -0
  54. data/ext/protobuf-2.4.1/src/google/protobuf/extension_set.h +904 -0
  55. data/ext/protobuf-2.4.1/src/google/protobuf/generated_message_reflection.h +424 -0
  56. data/ext/protobuf-2.4.1/src/google/protobuf/generated_message_util.h +82 -0
  57. data/ext/protobuf-2.4.1/src/google/protobuf/io/coded_stream.h +1102 -0
  58. data/ext/protobuf-2.4.1/src/google/protobuf/io/coded_stream_inl.h +64 -0
  59. data/ext/protobuf-2.4.1/src/google/protobuf/io/gzip_stream.h +207 -0
  60. data/ext/protobuf-2.4.1/src/google/protobuf/io/package_info.h +54 -0
  61. data/ext/protobuf-2.4.1/src/google/protobuf/io/printer.h +136 -0
  62. data/ext/protobuf-2.4.1/src/google/protobuf/io/tokenizer.h +313 -0
  63. data/ext/protobuf-2.4.1/src/google/protobuf/io/zero_copy_stream.h +238 -0
  64. data/ext/protobuf-2.4.1/src/google/protobuf/io/zero_copy_stream_impl.h +357 -0
  65. data/ext/protobuf-2.4.1/src/google/protobuf/io/zero_copy_stream_impl_lite.h +340 -0
  66. data/ext/protobuf-2.4.1/src/google/protobuf/message.h +692 -0
  67. data/ext/protobuf-2.4.1/src/google/protobuf/message_lite.h +239 -0
  68. data/ext/protobuf-2.4.1/src/google/protobuf/package_info.h +64 -0
  69. data/ext/protobuf-2.4.1/src/google/protobuf/reflection_ops.h +80 -0
  70. data/ext/protobuf-2.4.1/src/google/protobuf/repeated_field.h +1295 -0
  71. data/ext/protobuf-2.4.1/src/google/protobuf/service.h +291 -0
  72. data/ext/protobuf-2.4.1/src/google/protobuf/stubs/common.h +1211 -0
  73. data/ext/protobuf-2.4.1/src/google/protobuf/stubs/hash.h +220 -0
  74. data/ext/protobuf-2.4.1/src/google/protobuf/stubs/map-util.h +119 -0
  75. data/ext/protobuf-2.4.1/src/google/protobuf/stubs/once.h +123 -0
  76. data/ext/protobuf-2.4.1/src/google/protobuf/stubs/stl_util-inl.h +121 -0
  77. data/ext/protobuf-2.4.1/src/google/protobuf/stubs/strutil.h +457 -0
  78. data/ext/protobuf-2.4.1/src/google/protobuf/stubs/substitute.h +170 -0
  79. data/ext/protobuf-2.4.1/src/google/protobuf/test_util.h +174 -0
  80. data/ext/protobuf-2.4.1/src/google/protobuf/test_util_lite.h +101 -0
  81. data/ext/protobuf-2.4.1/src/google/protobuf/testing/file.h +83 -0
  82. data/ext/protobuf-2.4.1/src/google/protobuf/testing/googletest.h +98 -0
  83. data/ext/protobuf-2.4.1/src/google/protobuf/text_format.h +285 -0
  84. data/ext/protobuf-2.4.1/src/google/protobuf/unittest.pb.h +11915 -0
  85. data/ext/protobuf-2.4.1/src/google/protobuf/unittest_custom_options.pb.h +2895 -0
  86. data/ext/protobuf-2.4.1/src/google/protobuf/unittest_embed_optimize_for.pb.h +211 -0
  87. data/ext/protobuf-2.4.1/src/google/protobuf/unittest_empty.pb.h +56 -0
  88. data/ext/protobuf-2.4.1/src/google/protobuf/unittest_import.pb.h +188 -0
  89. data/ext/protobuf-2.4.1/src/google/protobuf/unittest_import_lite.pb.h +151 -0
  90. data/ext/protobuf-2.4.1/src/google/protobuf/unittest_lite.pb.h +4752 -0
  91. data/ext/protobuf-2.4.1/src/google/protobuf/unittest_lite_imports_nonlite.pb.h +150 -0
  92. data/ext/protobuf-2.4.1/src/google/protobuf/unittest_mset.pb.h +816 -0
  93. data/ext/protobuf-2.4.1/src/google/protobuf/unittest_no_generic_services.pb.h +197 -0
  94. data/ext/protobuf-2.4.1/src/google/protobuf/unittest_optimize_for.pb.h +403 -0
  95. data/ext/protobuf-2.4.1/src/google/protobuf/unknown_field_set.h +268 -0
  96. data/ext/protobuf-2.4.1/src/google/protobuf/wire_format.h +304 -0
  97. data/ext/protobuf-2.4.1/src/google/protobuf/wire_format_lite.h +620 -0
  98. data/ext/protobuf-2.4.1/src/google/protobuf/wire_format_lite_inl.h +774 -0
  99. data/ext/ruby_generator/Makefile +10 -0
  100. data/ext/ruby_generator/RubyGenerator.cpp +450 -0
  101. data/ext/ruby_generator/RubyGenerator.h +199 -0
  102. data/ext/ruby_generator/extconf.rb +36 -0
  103. data/ext/ruby_generator/protoc-ruby +0 -0
  104. data/lib/protobuf/cli.rb +188 -0
  105. data/lib/protobuf/enum.rb +58 -0
  106. data/lib/protobuf/enum_value.rb +59 -0
  107. data/lib/protobuf/evented.rb +22 -0
  108. data/lib/protobuf/exceptions.rb +11 -0
  109. data/lib/protobuf/ext/eventmachine.rb +14 -0
  110. data/lib/protobuf/field/base_field.rb +240 -0
  111. data/lib/protobuf/field/bool_field.rb +36 -0
  112. data/lib/protobuf/field/bytes_field.rb +38 -0
  113. data/lib/protobuf/field/double_field.rb +19 -0
  114. data/lib/protobuf/field/enum_field.rb +50 -0
  115. data/lib/protobuf/field/extension_fields.rb +32 -0
  116. data/lib/protobuf/field/field_array.rb +65 -0
  117. data/lib/protobuf/field/fixed32_field.rb +19 -0
  118. data/lib/protobuf/field/fixed64_field.rb +22 -0
  119. data/lib/protobuf/field/float_field.rb +31 -0
  120. data/lib/protobuf/field/int32_field.rb +12 -0
  121. data/lib/protobuf/field/int64_field.rb +12 -0
  122. data/lib/protobuf/field/integer_field.rb +19 -0
  123. data/lib/protobuf/field/message_field.rb +53 -0
  124. data/lib/protobuf/field/sfixed32_field.rb +21 -0
  125. data/lib/protobuf/field/sfixed64_field.rb +24 -0
  126. data/lib/protobuf/field/signed_integer_field.rb +23 -0
  127. data/lib/protobuf/field/sint32_field.rb +12 -0
  128. data/lib/protobuf/field/sint64_field.rb +12 -0
  129. data/lib/protobuf/field/string_field.rb +14 -0
  130. data/lib/protobuf/field/uint32_field.rb +12 -0
  131. data/lib/protobuf/field/uint64_field.rb +12 -0
  132. data/lib/protobuf/field/varint_field.rb +61 -0
  133. data/lib/protobuf/field.rb +57 -0
  134. data/lib/protobuf/logger.rb +86 -0
  135. data/lib/protobuf/message/decoder.rb +83 -0
  136. data/lib/protobuf/message/encoder.rb +48 -0
  137. data/lib/protobuf/message/extend.rb +8 -0
  138. data/lib/protobuf/message/message.rb +1 -0
  139. data/lib/protobuf/message.rb +320 -0
  140. data/lib/protobuf/rpc/buffer.rb +79 -0
  141. data/lib/protobuf/rpc/client.rb +166 -0
  142. data/lib/protobuf/rpc/connector.rb +19 -0
  143. data/lib/protobuf/rpc/connectors/base.rb +38 -0
  144. data/lib/protobuf/rpc/connectors/common.rb +156 -0
  145. data/lib/protobuf/rpc/connectors/em_client.rb +84 -0
  146. data/lib/protobuf/rpc/connectors/eventmachine.rb +87 -0
  147. data/lib/protobuf/rpc/connectors/socket.rb +73 -0
  148. data/lib/protobuf/rpc/connectors/zmq.rb +69 -0
  149. data/lib/protobuf/rpc/error/client_error.rb +31 -0
  150. data/lib/protobuf/rpc/error/server_error.rb +43 -0
  151. data/lib/protobuf/rpc/error.rb +25 -0
  152. data/lib/protobuf/rpc/rpc.pb.rb +118 -0
  153. data/lib/protobuf/rpc/server.rb +89 -0
  154. data/lib/protobuf/rpc/servers/evented/server.rb +41 -0
  155. data/lib/protobuf/rpc/servers/evented_runner.rb +21 -0
  156. data/lib/protobuf/rpc/servers/socket/server.rb +111 -0
  157. data/lib/protobuf/rpc/servers/socket/worker.rb +66 -0
  158. data/lib/protobuf/rpc/servers/socket_runner.rb +27 -0
  159. data/lib/protobuf/rpc/servers/zmq/broker.rb +87 -0
  160. data/lib/protobuf/rpc/servers/zmq/server.rb +50 -0
  161. data/lib/protobuf/rpc/servers/zmq/util.rb +27 -0
  162. data/lib/protobuf/rpc/servers/zmq/worker.rb +60 -0
  163. data/lib/protobuf/rpc/servers/zmq_runner.rb +25 -0
  164. data/lib/protobuf/rpc/service.rb +173 -0
  165. data/lib/protobuf/rpc/service_dispatcher.rb +130 -0
  166. data/lib/protobuf/rpc/service_filters.rb +267 -0
  167. data/lib/protobuf/rpc/stat.rb +83 -0
  168. data/lib/protobuf/socket.rb +22 -0
  169. data/lib/protobuf/version.rb +4 -0
  170. data/lib/protobuf/wire_type.rb +10 -0
  171. data/lib/protobuf/zmq.rb +21 -0
  172. data/lib/protobuf.rb +86 -0
  173. data/proto/rpc.pb.rb +48 -0
  174. data/proto/rpc.proto +73 -0
  175. data/protobuf.gemspec +44 -0
  176. data/spec/benchmark/tasks.rb +179 -0
  177. data/spec/functional/embedded_service_spec.rb +7 -0
  178. data/spec/functional/evented_server_spec.rb +64 -0
  179. data/spec/functional/socket_server_spec.rb +58 -0
  180. data/spec/functional/zmq_server_spec.rb +58 -0
  181. data/spec/lib/protobuf/cli_spec.rb +212 -0
  182. data/spec/lib/protobuf/enum_spec.rb +98 -0
  183. data/spec/lib/protobuf/enum_value_spec.rb +15 -0
  184. data/spec/lib/protobuf/logger_spec.rb +131 -0
  185. data/spec/lib/protobuf/message/encoder_spec.rb +19 -0
  186. data/spec/lib/protobuf/message_spec.rb +209 -0
  187. data/spec/lib/protobuf/rpc/client_spec.rb +158 -0
  188. data/spec/lib/protobuf/rpc/connector_spec.rb +32 -0
  189. data/spec/lib/protobuf/rpc/connectors/base_spec.rb +50 -0
  190. data/spec/lib/protobuf/rpc/connectors/common_spec.rb +128 -0
  191. data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +36 -0
  192. data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +22 -0
  193. data/spec/lib/protobuf/rpc/servers/evented_server_spec.rb +18 -0
  194. data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +43 -0
  195. data/spec/lib/protobuf/rpc/servers/zmq/broker_spec.rb +35 -0
  196. data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +41 -0
  197. data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +45 -0
  198. data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +44 -0
  199. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +116 -0
  200. data/spec/lib/protobuf/rpc/service_filters_spec.rb +451 -0
  201. data/spec/lib/protobuf/rpc/service_spec.rb +165 -0
  202. data/spec/lib/protobuf_spec.rb +62 -0
  203. data/spec/spec_helper.rb +51 -0
  204. data/spec/support/all.rb +6 -0
  205. data/spec/support/server.rb +101 -0
  206. data/spec/support/test/enum.pb.rb +34 -0
  207. data/spec/support/test/enum.proto +12 -0
  208. data/spec/support/test/resource.pb.rb +58 -0
  209. data/spec/support/test/resource.proto +31 -0
  210. data/spec/support/test/resource_service.rb +14 -0
  211. data/spec/support/test_app_file.rb +2 -0
  212. data/spec/support/tolerance_matcher.rb +40 -0
  213. data/test/data/data.bin +3 -0
  214. data/test/data/data_source.py +14 -0
  215. data/test/data/types.bin +0 -0
  216. data/test/data/types_source.py +22 -0
  217. data/test/data/unk.png +0 -0
  218. data/test/proto/addressbook.pb.rb +66 -0
  219. data/test/proto/addressbook.proto +33 -0
  220. data/test/proto/addressbook_base.pb.rb +58 -0
  221. data/test/proto/addressbook_base.proto +26 -0
  222. data/test/proto/addressbook_ext.pb.rb +20 -0
  223. data/test/proto/addressbook_ext.proto +6 -0
  224. data/test/proto/collision.pb.rb +17 -0
  225. data/test/proto/collision.proto +5 -0
  226. data/test/proto/ext_collision.pb.rb +24 -0
  227. data/test/proto/ext_collision.proto +8 -0
  228. data/test/proto/ext_range.pb.rb +22 -0
  229. data/test/proto/ext_range.proto +7 -0
  230. data/test/proto/float_default.proto +10 -0
  231. data/test/proto/lowercase.pb.rb +30 -0
  232. data/test/proto/lowercase.proto +9 -0
  233. data/test/proto/merge.pb.rb +39 -0
  234. data/test/proto/merge.proto +15 -0
  235. data/test/proto/nested.pb.rb +30 -0
  236. data/test/proto/nested.proto +9 -0
  237. data/test/proto/optional_field.pb.rb +35 -0
  238. data/test/proto/optional_field.proto +12 -0
  239. data/test/proto/packed.pb.rb +22 -0
  240. data/test/proto/packed.proto +6 -0
  241. data/test/proto/rpc.proto +6 -0
  242. data/test/proto/types.pb.rb +84 -0
  243. data/test/proto/types.proto +37 -0
  244. data/test/test_addressbook.rb +56 -0
  245. data/test/test_enum_value.rb +41 -0
  246. data/test/test_extension.rb +36 -0
  247. data/test/test_lowercase.rb +11 -0
  248. data/test/test_message.rb +128 -0
  249. data/test/test_optional_field.rb +103 -0
  250. data/test/test_packed_field.rb +40 -0
  251. data/test/test_parse.rb +15 -0
  252. data/test/test_repeated_types.rb +132 -0
  253. data/test/test_serialize.rb +61 -0
  254. data/test/test_standard_message.rb +96 -0
  255. data/test/test_types.rb +226 -0
  256. metadata +461 -0
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.swp
3
+ pkg/*
4
+ .bundle
5
+ .rvmrc
6
+ *.log
7
+ coverage
8
+ doc
9
+ .yardoc
10
+ .DS_Store
11
+ *.so
12
+ *.bin
13
+ Gemfile.lock
14
+ tmp/*
15
+ ext/defs
16
+ ext/out
17
+ ext/ruby_generator/protoc-ruby
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.2"
4
+ - "1.9.3"
5
+ - jruby-19mode
6
+ - rbx-19mode
7
+ - ruby-head
8
+ - jruby-head
9
+ script: NO_COMPILE_TEST_PROTOS=1 bundle exec rake spec
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --no-private
2
+ --hide-void-return
3
+
4
+ --markup-provider=redcarpet
5
+ --markup=markdown
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,316 @@
1
+ # protobuf
2
+
3
+ [![Build Status](https://secure.travis-ci.org/localshred/protobuf.png?branch=master)](https://travis-ci.org/localshred/protobuf)
4
+
5
+ ***IMPORTANT: Those upgrading from version 1.4.2 to 2.X should read the [UPGRADING.md](https://github.com/localshred/protobuf/blob/master/UPGRADING.md) notes***
6
+
7
+ 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:
8
+
9
+ 1. Generating ruby classes from `.proto` files.
10
+ 2. Provide an RPC mechanism for calling remote services.
11
+ 3. Provide RPC interop between ruby and other protobuf-rpc aware implementations for different languages (e.g. [protobuf-socket-rpc][]).
12
+
13
+ So let's dive in and see how to work with all three.
14
+
15
+ ## 1. Generating ruby classes from `.proto` files
16
+
17
+ 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!
18
+
19
+ Let's say you have a `defs.proto` file that defines a User message.
20
+
21
+ ```
22
+ package foo;
23
+ message User {
24
+ required string first_name = 1;
25
+ required string last_name = 1;
26
+ }
27
+
28
+ Now let's compile that definition to ruby:
29
+
30
+ $ rprotoc defs.proto --ruby_out ./lib
31
+ ```
32
+
33
+ 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:
34
+
35
+ ```
36
+ - lib
37
+ |- foo
38
+ |- defs.pb.rb
39
+ ```
40
+
41
+ The generated file `defs.pb.rb` should look something like this:
42
+
43
+ ```ruby
44
+ module Foo
45
+ class User < ::Protobuf::Message; end
46
+
47
+ class User
48
+ required :string, :first_name, 1
49
+ required :string, :last_name, 2
50
+ end
51
+ end
52
+ ```
53
+
54
+ _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._
55
+
56
+ The generated class is now just a plain old ruby object and you can use it however you wish.
57
+
58
+ ```ruby
59
+ require 'lib/foo/user.pb'
60
+
61
+ # dot notation reading/writing fields
62
+ user = Foo::User.new
63
+ user.first_name = "Lloyd"
64
+ user.last_name = "Christmas"
65
+ user.first_name # => "Lloyd"
66
+
67
+ # or pass in the fields as a hash to the initializer
68
+ user = Foo::User.new :first_name => "Lloyd", :last_name => "Christmas"
69
+ user.first_name # => Lloyd
70
+ user.last_name # => Christmas
71
+ ```
72
+
73
+ ### Message (de)serialization
74
+
75
+ 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.
76
+
77
+ ```ruby
78
+ user = Foo::User.new(:first_name => 'Bob')
79
+ bytes = user.serialize_to_string
80
+ puts bytes #=> binary representation of this message object
81
+
82
+ inflated_user = Foo::User.new.parse_from_string(bytes)
83
+ inflated_user == user #=> true
84
+ ```
85
+
86
+ ## 2. RPC
87
+
88
+ 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.
89
+
90
+ 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.
91
+
92
+ ### Services
93
+
94
+ Services are simply classes that have endpoint methods defined. Here's what one looks like in protobuf:
95
+
96
+ ```
97
+ package foo;
98
+ message UserRequest {
99
+ optional string email = 1;
100
+ }
101
+ message UserList {
102
+ repeated User users = 1;
103
+ }
104
+ service UserService {
105
+ rpc Find (UserRequest) returns (UserList);
106
+ }
107
+ ```
108
+
109
+ And the equivalent ruby stub for the service (generated with `rprotoc`):
110
+
111
+ ```ruby
112
+ # lib/foo/user.pb.rb
113
+ module Foo
114
+ # UserRequest and UserList Class definitions not shown (see above for generated output of classes).
115
+
116
+ class UserService < ::Protobuf::Rpc::Service
117
+ rpc :find, UserRequest, UserList
118
+ end
119
+ end
120
+ ```
121
+
122
+ **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.**
123
+
124
+ Did you read the note above? Go read it. I'll wait.
125
+
126
+ 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`.
127
+
128
+ ```ruby
129
+ # app/services/user_service.rb
130
+ require 'lib/foo/user.pb'
131
+
132
+ # Reopen the class and provide the implementation for each rpc method defined.
133
+ module Foo
134
+ class UserService
135
+
136
+ # request -> Foo::UserRequest
137
+ # response -> Foo::UserResponse
138
+ def find
139
+ # request.email will be the unpacked string that was sent by the client request
140
+ User.find_by_email(request.email).each do |user|
141
+ # must only use a proto instance of Foo::User when appending to the `users` field
142
+ response.users << user.to_proto
143
+ end
144
+ end
145
+
146
+ end
147
+ end
148
+ ```
149
+
150
+ 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.
151
+
152
+ 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:
153
+
154
+ 1. Response should be of same type as defined by the rpc definition (in this case, `Foo::UserList`), or
155
+ 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.
156
+ 3. Response should respond to the `to_proto` method. The object returned by `to_proto` should be an instance of the defined response type.
157
+
158
+ If at any time the implementation encounters an error, the client can be instructed of the error using `rpc_failed`:
159
+
160
+ ```ruby
161
+ #...
162
+ def find
163
+ if request.email.blank?
164
+ rpc_failed 'Unable to find user without an email'
165
+ else
166
+ # query/populate response
167
+ end
168
+ end
169
+ #...
170
+ ```
171
+
172
+ 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.
173
+
174
+ ### Service Filters
175
+
176
+ Service Filters provides ActionController-style filter support to service instances, specifically adding `before_filter`, `after_filter`, and `around_filter`.
177
+
178
+ ```ruby
179
+ class Foo::UserService < ::Protobuf::Rpc::Service
180
+ before_filter :start_request_timer
181
+ after_filter :end_request_timer
182
+ around_filter :benchmark_request
183
+
184
+ # Provide a list of rpc methods to call (or exclude calling) for the given filter(s).
185
+ # The following two filters are essentially equivalent.
186
+ before_filter :verify_user_present, :only => [ :update, :delete ]
187
+ before_filter :verify_user_present, :except => [ :find, :create ]
188
+
189
+ # Using if/unless filters options to achieve the same goal, reporting a login after the login has been processed.
190
+ # Note that you can provide a method name or lambda, but you must return a boolean value.
191
+ after_filter :report_login, :only => :login, :if => :user_found?
192
+ after_filter :report_login, :only => :login, :if => lambda { |service| service.response.user_guid.present? }
193
+ after_filter :report_login, :only => :login, :unless => :user_missing?
194
+ after_filter :report_login, :only => :login, :unless => lambda { |service| service.response.user_guid.empty? }
195
+
196
+ #... rpc instance methods
197
+
198
+ private
199
+
200
+ def start_request_timer
201
+ @time_start = Time.now
202
+ end
203
+
204
+ def end_request_timer
205
+ @time_end = Time.now
206
+ log_info { ... }
207
+ end
208
+
209
+ def benchmark_request
210
+ Benchmark.benchmark do
211
+ yield
212
+ end
213
+ end
214
+ end
215
+ ```
216
+
217
+ #### Halting execution of rpc request inside a filter
218
+
219
+ __Around Filters__ – Inside of an around filter, if you wish to halt request processing and return, simply do not `yield` the block. Since the filter is implemented as an instance method, you can use `rpc_failed` or `respond_with` just like you can in the endpoint methods.
220
+
221
+ __Before Filters__ – Returning `false` from a before filter will cancel any other filter calls which would run afterwards, as well as canceling invocation of the service method. Note: You must actually return false, not just a "falsey" value such as nil.
222
+
223
+ __After Filters__ – There is no request shortcutting since the after filter runs after the request. Duh.
224
+
225
+ #### Filter options
226
+
227
+ The following options can be applied to any of the filters as the final argument in the filter configuration. (See example above).
228
+
229
+ __:if__ – The object supplied to `:if` can either be a symbol/string indicating the instance method to call, or, an object that responds to `call`. The method or callable should return true or false indicating if the filter should be invoked or not. Akin to the `if` keyword.
230
+
231
+ __:unless__ – The opposite of the `:if` option is `:unless`. Accepts the same object types. The method or callable should return true or false indicating if the filter should be invoked or not. Akin to the `unless` keyword.
232
+
233
+ __:only__ – A string/symbol or Array of strings/symbols values that reference instance methods. The names of these methods should be the rpc method you wish to invoke the filter for. Methods not identified in this list would not have the filter applied.
234
+
235
+ __:except__ – The opposite of the `:only` option. A string/symbol or Array of strings/symbols values that reference instance methods. The names of these methods should be the rpc method you wish to skip invokation of the given filter. Methods not identified in this list would have the filter applied.
236
+
237
+ ### Servers
238
+
239
+ 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!
240
+
241
+ ```
242
+ $ rpc_server -o myserver.com -p 9939 -l ./log/protobuf.log ./config/environment.rb
243
+ ```
244
+
245
+ 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.
246
+
247
+ 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.
248
+
249
+ 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.
250
+
251
+ ```
252
+ $ ps aux | grep rpc_server
253
+ 1234 ... rpc_server myservice.com:9939
254
+
255
+ $ kill -QUIT 1234
256
+ rpc_server shutdown
257
+ ```
258
+
259
+ ### Clients
260
+
261
+ 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.
262
+
263
+ ```ruby
264
+ # require the defs from the shared gem/repo
265
+ require 'sharedgem/foo/user.pb'
266
+
267
+ # Create a request object for the method we are invoking
268
+ req = Foo::UserRequest.new(:email => 'jeff@gmail.com')
269
+
270
+ # Use the UserService class to generate a client, invoke the rpc method
271
+ # while passing the request object.
272
+ # We could also simply pass a hash to find.
273
+ Foo::UserService.client.find(req) do |c|
274
+ # This block will be executed (registering the callbacks)
275
+ # before the request actualy occurs.
276
+ # the `c` param in this block is the `.client` object
277
+ # that is generated from the call above
278
+
279
+ # Register a block for execution when the response
280
+ # is deemed successful from the service. Accepts
281
+ # the unpacked response as its only parameter
282
+ c.on_success do |response|
283
+ response.users.each do |u|
284
+ puts u.inspect
285
+ end
286
+ end
287
+
288
+ # Register a block for execution when the response
289
+ # is deemed a failure. This can be either a client-side
290
+ # or server-side failure. The object passed the to the
291
+ # block has a `message` and a `code` attribute
292
+ # to aid in logging/diagnosing the failure.
293
+ c.on_failure do |err|
294
+ puts 'It failed: ' + err.message
295
+ end
296
+ end
297
+ ```
298
+
299
+ 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.
300
+
301
+ ## 3. RPC Interop
302
+
303
+ 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.
304
+
305
+ ## Accreditation & Caveats
306
+
307
+ 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.
308
+
309
+ 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.
310
+
311
+ -- BJ Neilsen, [@localshred][]
312
+
313
+ [google-pb]: http://code.google.com/p/protobuf "Google Protocol Buffers"
314
+ [protobuf-socket-rpc]: http://code.google.com/p/protobuf-socket-rpc/ "Google's official Socket-RPC library for protobuf"
315
+ [old gem]: https://github.com/macks/ruby-protobuf "Macks ruby-protobuf on github"
316
+ [@localshred]: http://twitter.com/localshred "Follow on twitter @localshred"
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ $:.push File.expand_path("./", File.dirname(__FILE__))
2
+ $:.push File.expand_path("./spec", File.dirname(__FILE__))
3
+
4
+ require "rubygems"
5
+ require "rubygems/package_task"
6
+ require "bundler/gem_tasks"
7
+ # require "benchmark/tasks"
8
+
9
+ require "rspec/core/rake_task"
10
+
11
+ desc "Default: run specs."
12
+ task :default => :spec
13
+
14
+ desc "Run specs"
15
+ RSpec::Core::RakeTask.new(:spec)
16
+
17
+ ##
18
+ # rake-compiler
19
+ #
20
+ spec = Gem::Specification.load("protobuf.gemspec")
21
+
22
+ Gem::PackageTask.new(spec) do |pkg|
23
+ end
24
+
25
+ if RUBY_PLATFORM =~ /java/
26
+ require "rake/javaextensiontask"
27
+ else
28
+ require "rake/extensiontask"
29
+ end
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/bin/rpc_server ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'protobuf/cli'
4
+ ::Protobuf::CLI.start(ARGV)
5
+
data/bin/rprotoc ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'ffi'
5
+
6
+ base_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "lib"))
7
+
8
+ GENERATOR_FILE_PATH = case
9
+ when File.exists?(File.join(base_dir, "ruby_generator.bundle")) then
10
+ File.join(base_dir, "ruby_generator.bundle")
11
+ when File.exists?(File.join(base_dir, "ruby_generator.dll")) then
12
+ File.join(base_dir, "ruby_generator.dll")
13
+ else
14
+ File.join(base_dir, "ruby_generator.so")
15
+ end
16
+
17
+ unless File.exists?(GENERATOR_FILE_PATH)
18
+ $stdout << <<-WARNING
19
+ Cannot locate shared object to plugin to protocol buffers generator.
20
+ Thought the file would be located at #{GENERATOR_FILE_PATH}
21
+
22
+ If you are running on Windows you should compile protocol buffer
23
+ definitions on another VM and then use those definitions locally.
24
+
25
+ Only the compiler is restricted by this. The definitions should work without issue.
26
+ WARNING
27
+ $stdout << $/
28
+
29
+ exit 1
30
+ end
31
+
32
+ module Protobuf
33
+ module RProtoC
34
+ extend FFI::Library
35
+ ffi_lib ::GENERATOR_FILE_PATH
36
+ attach_function :_rprotoc_extern, [:int, :pointer], :int32
37
+
38
+ def self.compile_proto(args)
39
+ GC.disable # Don't want strings to be GC'd while protoc has them
40
+ args = args.dup
41
+ args.unshift("rprotoc")
42
+ args << "--help" if args.size == 1
43
+
44
+ ptr_params = []
45
+ args.each do |param|
46
+ ptr_params << ::FFI::MemoryPointer.from_string(param.dup)
47
+ end
48
+ ptr_params << nil
49
+
50
+ argv = ::FFI::MemoryPointer.new(:pointer, ptr_params.size)
51
+ ptr_params.each_with_index do |param_pointer, index|
52
+ argv[index].put_pointer(0, param_pointer)
53
+ end
54
+
55
+ self._rprotoc_extern(ptr_params.compact.size, argv)
56
+ ensure
57
+ GC.enable
58
+ end
59
+ end
60
+ end
61
+
62
+ ::Protobuf::RProtoC.compile_proto(ARGV)
@@ -0,0 +1,55 @@
1
+ ### Generated by rprotoc. DO NOT EDIT!
2
+ ### <proto file: examples/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
+ # }
23
+ #
24
+ # message AddressBook {
25
+ # repeated Person person = 1;
26
+ # }
27
+
28
+ require 'protobuf/message/message'
29
+ require 'protobuf/enum'
30
+ require 'protobuf/message/extend'
31
+
32
+ module Tutorial
33
+ class Person < ::Protobuf::Message
34
+ defined_in __FILE__
35
+ required :string, :name, 1
36
+ required :int32, :id, 2
37
+ optional :string, :email, 3
38
+ class PhoneType < ::Protobuf::Enum
39
+ defined_in __FILE__
40
+ MOBILE = 0
41
+ HOME = 1
42
+ WORK = 2
43
+ end
44
+ class PhoneNumber < ::Protobuf::Message
45
+ defined_in __FILE__
46
+ required :string, :number, 1
47
+ optional :PhoneType, :type, 2, :default => :HOME
48
+ end
49
+ repeated :PhoneNumber, :phone, 4
50
+ end
51
+ class AddressBook < ::Protobuf::Message
52
+ defined_in __FILE__
53
+ repeated :Person, :person, 1
54
+ end
55
+ end
@@ -0,0 +1,24 @@
1
+ package tutorial;
2
+
3
+ message Person {
4
+ required string name = 1;
5
+ required int32 id = 2;
6
+ optional string email = 3;
7
+
8
+ enum PhoneType {
9
+ MOBILE = 0;
10
+ HOME = 1;
11
+ WORK = 2;
12
+ }
13
+
14
+ message PhoneNumber {
15
+ required string number = 1;
16
+ optional PhoneType type = 2 [default = HOME];
17
+ }
18
+
19
+ repeated PhoneNumber phone = 4;
20
+ }
21
+
22
+ message AddressBook {
23
+ repeated Person person = 1;
24
+ }
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'addressbook.pb'
4
+
5
+ def list_people(address_book)
6
+ address_book.person.each do |person|
7
+ puts "Person ID: #{person.id}"
8
+ puts " Name: #{person.name}"
9
+ puts " E-mail: #{person.email}" unless person.email.empty?
10
+ person.phone.each do |phone_number|
11
+ print(case phone_number.type
12
+ when Tutorial::Person::PhoneType::MOBILE then
13
+ ' Mobile phone #: '
14
+ when Tutorial::Person::PhoneType::HOME then
15
+ ' Home phone #: '
16
+ when Tutorial::Person::PhoneType::WORK then
17
+ ' Work phone #: '
18
+ end)
19
+ puts phone_number.number
20
+ end
21
+ end
22
+ end
23
+
24
+ unless ARGV.size == 1
25
+ puts "Usage: #{$0} ADDRESS_BOOK_FILE"
26
+ exit
27
+ end
28
+
29
+ address_book = Tutorial::AddressBook.new
30
+ address_book.parse_from_file ARGV[0]
31
+
32
+ list_people address_book
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'addressbook.pb'
4
+
5
+ def prompt_for_address(person)
6
+ print 'Enter person ID number: '
7
+ person.id = STDIN.gets.strip.to_i
8
+ print 'Enter name: '
9
+ person.name = STDIN.gets.strip
10
+ print 'Enter email address (blank for none): '
11
+ email = STDIN.gets.strip
12
+ person.email = email unless email.empty?
13
+
14
+ loop do
15
+ print 'Enter a phone number (or leave blank to finish): '
16
+ break if (number = STDIN.gets.strip).empty?
17
+
18
+ person.phone << Tutorial::Person::PhoneNumber.new
19
+ person.phone.last.number = number
20
+
21
+ print 'Is this a mobile, home, or work phone? '
22
+ person.phone.last.type =
23
+ case type = STDIN.gets.strip
24
+ when 'mobile' then
25
+ Tutorial::Person::PhoneType::MOBILE
26
+ when 'home' then
27
+ Tutorial::Person::PhoneType::HOME
28
+ when 'work' then
29
+ Tutorial::Person::PhoneType::WORK
30
+ else
31
+ puts 'Unknown phone type; leaving as default value.'
32
+ nil
33
+ end
34
+ end
35
+ end
36
+
37
+ unless ARGV.size == 1
38
+ puts "Usage: #{$0} ADDRESS_BOOK_FILE"
39
+ exit
40
+ end
41
+
42
+ address_book = Tutorial::AddressBook.new
43
+ address_book.parse_from_file ARGV[0] if File.exist? ARGV[0]
44
+ address_book.person << Tutorial::Person.new
45
+ prompt_for_address address_book.person.last
46
+ address_book.serialize_to_file ARGV[0]