protobuf-cucumber 3.10.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (204) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rubocop.yml +70 -0
  4. data/.rubocop_todo.yml +145 -0
  5. data/.travis.yml +40 -0
  6. data/.yardopts +5 -0
  7. data/CHANGES.md +344 -0
  8. data/CONTRIBUTING.md +16 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +33 -0
  12. data/Rakefile +64 -0
  13. data/bin/protoc-gen-ruby +22 -0
  14. data/bin/rpc_server +5 -0
  15. data/install-protobuf.sh +28 -0
  16. data/lib/protobuf.rb +129 -0
  17. data/lib/protobuf/cli.rb +257 -0
  18. data/lib/protobuf/code_generator.rb +120 -0
  19. data/lib/protobuf/decoder.rb +28 -0
  20. data/lib/protobuf/deprecation.rb +117 -0
  21. data/lib/protobuf/descriptors.rb +3 -0
  22. data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +62 -0
  23. data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +301 -0
  24. data/lib/protobuf/encoder.rb +11 -0
  25. data/lib/protobuf/enum.rb +365 -0
  26. data/lib/protobuf/exceptions.rb +9 -0
  27. data/lib/protobuf/field.rb +74 -0
  28. data/lib/protobuf/field/base_field.rb +380 -0
  29. data/lib/protobuf/field/base_field_object_definitions.rb +504 -0
  30. data/lib/protobuf/field/bool_field.rb +64 -0
  31. data/lib/protobuf/field/bytes_field.rb +78 -0
  32. data/lib/protobuf/field/double_field.rb +25 -0
  33. data/lib/protobuf/field/enum_field.rb +61 -0
  34. data/lib/protobuf/field/field_array.rb +104 -0
  35. data/lib/protobuf/field/field_hash.rb +122 -0
  36. data/lib/protobuf/field/fixed32_field.rb +25 -0
  37. data/lib/protobuf/field/fixed64_field.rb +28 -0
  38. data/lib/protobuf/field/float_field.rb +43 -0
  39. data/lib/protobuf/field/int32_field.rb +21 -0
  40. data/lib/protobuf/field/int64_field.rb +34 -0
  41. data/lib/protobuf/field/integer_field.rb +23 -0
  42. data/lib/protobuf/field/message_field.rb +51 -0
  43. data/lib/protobuf/field/sfixed32_field.rb +27 -0
  44. data/lib/protobuf/field/sfixed64_field.rb +28 -0
  45. data/lib/protobuf/field/signed_integer_field.rb +29 -0
  46. data/lib/protobuf/field/sint32_field.rb +21 -0
  47. data/lib/protobuf/field/sint64_field.rb +21 -0
  48. data/lib/protobuf/field/string_field.rb +51 -0
  49. data/lib/protobuf/field/uint32_field.rb +21 -0
  50. data/lib/protobuf/field/uint64_field.rb +21 -0
  51. data/lib/protobuf/field/varint_field.rb +77 -0
  52. data/lib/protobuf/generators/base.rb +85 -0
  53. data/lib/protobuf/generators/enum_generator.rb +39 -0
  54. data/lib/protobuf/generators/extension_generator.rb +27 -0
  55. data/lib/protobuf/generators/field_generator.rb +193 -0
  56. data/lib/protobuf/generators/file_generator.rb +262 -0
  57. data/lib/protobuf/generators/group_generator.rb +122 -0
  58. data/lib/protobuf/generators/message_generator.rb +104 -0
  59. data/lib/protobuf/generators/option_generator.rb +17 -0
  60. data/lib/protobuf/generators/printable.rb +160 -0
  61. data/lib/protobuf/generators/service_generator.rb +50 -0
  62. data/lib/protobuf/lifecycle.rb +33 -0
  63. data/lib/protobuf/logging.rb +39 -0
  64. data/lib/protobuf/message.rb +260 -0
  65. data/lib/protobuf/message/fields.rb +233 -0
  66. data/lib/protobuf/message/serialization.rb +85 -0
  67. data/lib/protobuf/optionable.rb +70 -0
  68. data/lib/protobuf/rpc/buffer.rb +78 -0
  69. data/lib/protobuf/rpc/client.rb +140 -0
  70. data/lib/protobuf/rpc/connectors/base.rb +221 -0
  71. data/lib/protobuf/rpc/connectors/ping.rb +89 -0
  72. data/lib/protobuf/rpc/connectors/socket.rb +78 -0
  73. data/lib/protobuf/rpc/connectors/zmq.rb +319 -0
  74. data/lib/protobuf/rpc/dynamic_discovery.pb.rb +50 -0
  75. data/lib/protobuf/rpc/env.rb +60 -0
  76. data/lib/protobuf/rpc/error.rb +28 -0
  77. data/lib/protobuf/rpc/error/client_error.rb +31 -0
  78. data/lib/protobuf/rpc/error/server_error.rb +43 -0
  79. data/lib/protobuf/rpc/middleware.rb +25 -0
  80. data/lib/protobuf/rpc/middleware/exception_handler.rb +40 -0
  81. data/lib/protobuf/rpc/middleware/logger.rb +95 -0
  82. data/lib/protobuf/rpc/middleware/request_decoder.rb +79 -0
  83. data/lib/protobuf/rpc/middleware/response_encoder.rb +83 -0
  84. data/lib/protobuf/rpc/middleware/runner.rb +18 -0
  85. data/lib/protobuf/rpc/rpc.pb.rb +64 -0
  86. data/lib/protobuf/rpc/rpc_method.rb +16 -0
  87. data/lib/protobuf/rpc/server.rb +39 -0
  88. data/lib/protobuf/rpc/servers/socket/server.rb +121 -0
  89. data/lib/protobuf/rpc/servers/socket/worker.rb +56 -0
  90. data/lib/protobuf/rpc/servers/socket_runner.rb +46 -0
  91. data/lib/protobuf/rpc/servers/zmq/broker.rb +194 -0
  92. data/lib/protobuf/rpc/servers/zmq/server.rb +321 -0
  93. data/lib/protobuf/rpc/servers/zmq/util.rb +48 -0
  94. data/lib/protobuf/rpc/servers/zmq/worker.rb +105 -0
  95. data/lib/protobuf/rpc/servers/zmq_runner.rb +70 -0
  96. data/lib/protobuf/rpc/service.rb +172 -0
  97. data/lib/protobuf/rpc/service_directory.rb +261 -0
  98. data/lib/protobuf/rpc/service_dispatcher.rb +45 -0
  99. data/lib/protobuf/rpc/service_filters.rb +250 -0
  100. data/lib/protobuf/rpc/stat.rb +119 -0
  101. data/lib/protobuf/socket.rb +21 -0
  102. data/lib/protobuf/tasks.rb +1 -0
  103. data/lib/protobuf/tasks/compile.rake +80 -0
  104. data/lib/protobuf/varint.rb +20 -0
  105. data/lib/protobuf/varint_pure.rb +31 -0
  106. data/lib/protobuf/version.rb +3 -0
  107. data/lib/protobuf/wire_type.rb +10 -0
  108. data/lib/protobuf/zmq.rb +21 -0
  109. data/profile.html +5103 -0
  110. data/proto/dynamic_discovery.proto +44 -0
  111. data/proto/google/protobuf/compiler/plugin.proto +147 -0
  112. data/proto/google/protobuf/descriptor.proto +779 -0
  113. data/proto/rpc.proto +69 -0
  114. data/protobuf-cucumber.gemspec +57 -0
  115. data/spec/benchmark/tasks.rb +143 -0
  116. data/spec/bin/protoc-gen-ruby_spec.rb +23 -0
  117. data/spec/encoding/all_types_spec.rb +103 -0
  118. data/spec/encoding/extreme_values_spec.rb +0 -0
  119. data/spec/functional/class_inheritance_spec.rb +52 -0
  120. data/spec/functional/code_generator_spec.rb +58 -0
  121. data/spec/functional/socket_server_spec.rb +59 -0
  122. data/spec/functional/zmq_server_spec.rb +105 -0
  123. data/spec/lib/protobuf/cli_spec.rb +317 -0
  124. data/spec/lib/protobuf/code_generator_spec.rb +87 -0
  125. data/spec/lib/protobuf/enum_spec.rb +307 -0
  126. data/spec/lib/protobuf/field/bool_field_spec.rb +91 -0
  127. data/spec/lib/protobuf/field/double_field_spec.rb +9 -0
  128. data/spec/lib/protobuf/field/enum_field_spec.rb +44 -0
  129. data/spec/lib/protobuf/field/field_array_spec.rb +105 -0
  130. data/spec/lib/protobuf/field/field_hash_spec.rb +168 -0
  131. data/spec/lib/protobuf/field/fixed32_field_spec.rb +7 -0
  132. data/spec/lib/protobuf/field/fixed64_field_spec.rb +7 -0
  133. data/spec/lib/protobuf/field/float_field_spec.rb +90 -0
  134. data/spec/lib/protobuf/field/int32_field_spec.rb +120 -0
  135. data/spec/lib/protobuf/field/int64_field_spec.rb +7 -0
  136. data/spec/lib/protobuf/field/message_field_spec.rb +132 -0
  137. data/spec/lib/protobuf/field/sfixed32_field_spec.rb +9 -0
  138. data/spec/lib/protobuf/field/sfixed64_field_spec.rb +9 -0
  139. data/spec/lib/protobuf/field/sint32_field_spec.rb +9 -0
  140. data/spec/lib/protobuf/field/sint64_field_spec.rb +9 -0
  141. data/spec/lib/protobuf/field/string_field_spec.rb +79 -0
  142. data/spec/lib/protobuf/field/uint32_field_spec.rb +7 -0
  143. data/spec/lib/protobuf/field/uint64_field_spec.rb +7 -0
  144. data/spec/lib/protobuf/field_spec.rb +192 -0
  145. data/spec/lib/protobuf/generators/base_spec.rb +154 -0
  146. data/spec/lib/protobuf/generators/enum_generator_spec.rb +82 -0
  147. data/spec/lib/protobuf/generators/extension_generator_spec.rb +42 -0
  148. data/spec/lib/protobuf/generators/field_generator_spec.rb +197 -0
  149. data/spec/lib/protobuf/generators/file_generator_spec.rb +119 -0
  150. data/spec/lib/protobuf/generators/message_generator_spec.rb +0 -0
  151. data/spec/lib/protobuf/generators/service_generator_spec.rb +99 -0
  152. data/spec/lib/protobuf/lifecycle_spec.rb +94 -0
  153. data/spec/lib/protobuf/message_spec.rb +944 -0
  154. data/spec/lib/protobuf/optionable_spec.rb +265 -0
  155. data/spec/lib/protobuf/rpc/client_spec.rb +66 -0
  156. data/spec/lib/protobuf/rpc/connectors/base_spec.rb +226 -0
  157. data/spec/lib/protobuf/rpc/connectors/ping_spec.rb +69 -0
  158. data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +34 -0
  159. data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +110 -0
  160. data/spec/lib/protobuf/rpc/middleware/exception_handler_spec.rb +62 -0
  161. data/spec/lib/protobuf/rpc/middleware/logger_spec.rb +49 -0
  162. data/spec/lib/protobuf/rpc/middleware/request_decoder_spec.rb +115 -0
  163. data/spec/lib/protobuf/rpc/middleware/response_encoder_spec.rb +91 -0
  164. data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +38 -0
  165. data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +43 -0
  166. data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +55 -0
  167. data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +35 -0
  168. data/spec/lib/protobuf/rpc/service_directory_spec.rb +293 -0
  169. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +35 -0
  170. data/spec/lib/protobuf/rpc/service_filters_spec.rb +517 -0
  171. data/spec/lib/protobuf/rpc/service_spec.rb +162 -0
  172. data/spec/lib/protobuf/rpc/stat_spec.rb +101 -0
  173. data/spec/lib/protobuf/varint_spec.rb +29 -0
  174. data/spec/lib/protobuf_spec.rb +105 -0
  175. data/spec/spec_helper.rb +42 -0
  176. data/spec/support/all.rb +6 -0
  177. data/spec/support/packed_field.rb +23 -0
  178. data/spec/support/protos/all_types.data.bin +0 -0
  179. data/spec/support/protos/all_types.data.txt +119 -0
  180. data/spec/support/protos/enum.pb.rb +63 -0
  181. data/spec/support/protos/enum.proto +37 -0
  182. data/spec/support/protos/extreme_values.data.bin +0 -0
  183. data/spec/support/protos/google_unittest.bin +0 -0
  184. data/spec/support/protos/google_unittest.pb.rb +798 -0
  185. data/spec/support/protos/google_unittest.proto +884 -0
  186. data/spec/support/protos/google_unittest_custom_options.bin +0 -0
  187. data/spec/support/protos/google_unittest_custom_options.pb.rb +361 -0
  188. data/spec/support/protos/google_unittest_custom_options.proto +424 -0
  189. data/spec/support/protos/google_unittest_import.pb.rb +55 -0
  190. data/spec/support/protos/google_unittest_import.proto +73 -0
  191. data/spec/support/protos/google_unittest_import_public.pb.rb +31 -0
  192. data/spec/support/protos/google_unittest_import_public.proto +41 -0
  193. data/spec/support/protos/map-test.bin +157 -0
  194. data/spec/support/protos/map-test.pb.rb +85 -0
  195. data/spec/support/protos/map-test.proto +68 -0
  196. data/spec/support/protos/multi_field_extensions.pb.rb +59 -0
  197. data/spec/support/protos/multi_field_extensions.proto +35 -0
  198. data/spec/support/protos/resource.pb.rb +172 -0
  199. data/spec/support/protos/resource.proto +137 -0
  200. data/spec/support/resource_service.rb +23 -0
  201. data/spec/support/server.rb +65 -0
  202. data/spec/support/test_app_file.rb +2 -0
  203. data/varint_prof.rb +82 -0
  204. metadata +579 -0
@@ -0,0 +1,77 @@
1
+ require 'protobuf/field/base_field'
2
+
3
+ module Protobuf
4
+ module Field
5
+ class VarintField < BaseField
6
+
7
+ ##
8
+ # Constants
9
+ #
10
+ INT32_MAX = 2**31 - 1
11
+ INT32_MIN = -2**31
12
+ INT64_MAX = 2**63 - 1
13
+ INT64_MIN = -2**63
14
+ UINT32_MAX = 2**32 - 1
15
+ UINT64_MAX = 2**64 - 1
16
+
17
+ ##
18
+ # Class Methods
19
+ #
20
+
21
+ def self.default
22
+ 0
23
+ end
24
+
25
+ def self.encode(value)
26
+ ::Protobuf::Varint.encode(value)
27
+ end
28
+
29
+ ##
30
+ # Public Instance Methods
31
+ #
32
+ def acceptable?(val)
33
+ int_val = if val.is_a?(Integer)
34
+ return true if val >= 0 && val < INT32_MAX # return quickly for smallest integer size, hot code path
35
+ val
36
+ elsif val.is_a?(Numeric)
37
+ val.to_i
38
+ else
39
+ Integer(val, 10)
40
+ end
41
+
42
+ int_val >= self.class.min && int_val <= self.class.max
43
+ rescue
44
+ false
45
+ end
46
+
47
+ def coerce!(val)
48
+ if val.is_a?(Integer) && val >= 0 && val <= INT32_MAX
49
+ val
50
+ else
51
+ fail TypeError, "Expected value of type '#{type_class}' for field #{name}, but got '#{val.class}'" unless acceptable?(val)
52
+
53
+ if val.is_a?(Integer) || val.is_a?(Numeric)
54
+ val.to_i
55
+ else
56
+ Integer(val, 10)
57
+ end
58
+ end
59
+ rescue ArgumentError
60
+ fail TypeError, "Expected value of type '#{type_class}' for field #{name}, but got '#{val.class}'"
61
+ end
62
+
63
+ def decode(value)
64
+ value
65
+ end
66
+
67
+ def encode(value)
68
+ ::Protobuf::Field::VarintField.encode(value)
69
+ end
70
+
71
+ def wire_type
72
+ ::Protobuf::WireType::VARINT
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,85 @@
1
+ require 'protobuf/generators/printable'
2
+
3
+ module Protobuf
4
+ module Generators
5
+ class Base
6
+ include ::Protobuf::Generators::Printable
7
+
8
+ def self.validate_tags(type_name, tags)
9
+ return if tags.empty?
10
+
11
+ unique_tags = tags.uniq
12
+
13
+ if unique_tags.size < tags.size
14
+ ::Protobuf::CodeGenerator.fatal("#{type_name} object has duplicate tags. Expected #{unique_tags.size} tags, but got #{tags.size}. Suppress with PB_NO_TAG_WARNINGS=1.")
15
+ end
16
+
17
+ unless ENV.key?('PB_NO_TAG_WARNINGS')
18
+ expected_size = tags.max - tags.min + 1
19
+ if tags.size < expected_size
20
+ ::Protobuf::CodeGenerator.print_tag_warning_suppress
21
+ ::Protobuf::CodeGenerator.warn("#{type_name} object should have #{expected_size} tags (#{tags.min}..#{tags.max}), but found #{tags.size} tags.")
22
+ end
23
+ end
24
+ end
25
+
26
+ attr_reader :descriptor, :namespace, :options
27
+
28
+ def initialize(descriptor, indent_level = 0, options = {})
29
+ @descriptor = descriptor
30
+ @options = options
31
+ @namespace = @options.fetch(:namespace) { [] }
32
+ init_printer(indent_level)
33
+ end
34
+
35
+ def fully_qualified_type_namespace
36
+ ".#{type_namespace.join('.')}"
37
+ end
38
+
39
+ def run_once(label)
40
+ tracker_ivar = "@_#{label}_compiled"
41
+ value_ivar = "@_#{label}_compiled_value"
42
+
43
+ if instance_variable_get(tracker_ivar)
44
+ return instance_variable_get(value_ivar)
45
+ end
46
+
47
+ return_value = yield
48
+ instance_variable_set(tracker_ivar, true)
49
+ instance_variable_set(value_ivar, return_value)
50
+ return_value
51
+ end
52
+
53
+ def to_s
54
+ compile
55
+ print_contents # see Printable
56
+ end
57
+
58
+ def type_namespace
59
+ @type_namespace ||= @namespace + [descriptor.name]
60
+ end
61
+
62
+ def serialize_value(value)
63
+ case value
64
+ when Message
65
+ fields = value.each_field.map do |field, inner_value|
66
+ next unless value.field?(field.name)
67
+ serialized_inner_value = serialize_value(inner_value)
68
+ "#{field.fully_qualified_name.inspect} => #{serialized_inner_value}"
69
+ end.compact
70
+ "{ #{fields.join(', ')} }"
71
+ when Enum
72
+ "::#{value.parent_class}::#{value.name}"
73
+ when String
74
+ value.inspect
75
+ when nil
76
+ "nil"
77
+ when Array
78
+ '[' + value.map { |x| serialize_value(x) }.join(', ') + ']'
79
+ else
80
+ value
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,39 @@
1
+ require 'protobuf/generators/base'
2
+ require 'protobuf/generators/option_generator'
3
+
4
+ module Protobuf
5
+ module Generators
6
+ class EnumGenerator < Base
7
+
8
+ def compile
9
+ run_once(:compile) do
10
+ tags = []
11
+
12
+ print_class(descriptor.name, :enum) do
13
+ if descriptor.options
14
+ print OptionGenerator.new(descriptor.options, current_indent).to_s
15
+ puts
16
+ end
17
+
18
+ descriptor.value.each do |enum_value_descriptor|
19
+ tags << enum_value_descriptor.number
20
+ puts build_value(enum_value_descriptor)
21
+ end
22
+ end
23
+
24
+ unless descriptor.options.try(:allow_alias)
25
+ self.class.validate_tags(fully_qualified_type_namespace, tags)
26
+ end
27
+ end
28
+ end
29
+
30
+ def build_value(enum_value_descriptor)
31
+ name = enum_value_descriptor.name
32
+ name.upcase! if ENV.key?('PB_UPCASE_ENUMS')
33
+ number = enum_value_descriptor.number
34
+ "define :#{name}, #{number}"
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,27 @@
1
+ require 'protobuf/generators/base'
2
+ require 'protobuf/generators/group_generator'
3
+
4
+ module Protobuf
5
+ module Generators
6
+ class ExtensionGenerator < Base
7
+
8
+ def initialize(message_type, field_descriptors, indent_level)
9
+ super(nil, indent_level)
10
+ @message_type = modulize(message_type)
11
+ @field_descriptors = field_descriptors
12
+ end
13
+
14
+ def compile
15
+ run_once(:compile) do
16
+ print_class(@message_type, :message) do
17
+ group = GroupGenerator.new(current_indent)
18
+ group.add_extension_fields(@field_descriptors)
19
+ group.order = [:extension_field]
20
+ print group.to_s
21
+ end
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,193 @@
1
+ require 'protobuf/generators/base'
2
+
3
+ module Protobuf
4
+ module Generators
5
+ class FieldGenerator < Base
6
+
7
+ ##
8
+ # Constants
9
+ #
10
+ PROTO_INFINITY_DEFAULT = /^inf$/i
11
+ PROTO_NEGATIVE_INFINITY_DEFAULT = /^-inf$/i
12
+ PROTO_NAN_DEFAULT = /^nan$/i
13
+ RUBY_INFINITY_DEFAULT = '::Float::INFINITY'.freeze
14
+ RUBY_NEGATIVE_INFINITY_DEFAULT = '-::Float::INFINITY'.freeze
15
+ RUBY_NAN_DEFAULT = '::Float::NAN'.freeze
16
+
17
+ ##
18
+ # Attributes
19
+ #
20
+ attr_reader :field_options
21
+
22
+ def initialize(field_descriptor, enclosing_msg_descriptor, indent_level)
23
+ super(field_descriptor, indent_level)
24
+ @enclosing_msg_descriptor = enclosing_msg_descriptor
25
+ end
26
+
27
+ def applicable_options
28
+ # Note on the strange use of `#inspect`:
29
+ # :boom.inspect #=> ":boom"
30
+ # :".boom.foo".inspect #=> ":\".boom.foo\""
31
+ # An alternative to `#inspect` would be always adding double quotes,
32
+ # but the generatated code looks un-idiomatic:
33
+ # ":\"#{:boom}\"" #=> ":\"boom\"" <-- Note the unnecessary double quotes
34
+ @applicable_options ||= field_options.map { |k, v| "#{k.inspect} => #{v}" }
35
+ end
36
+
37
+ def default_value
38
+ @default_value ||= begin
39
+ if defaulted?
40
+ case descriptor.type.name
41
+ when :TYPE_ENUM
42
+ enum_default_value
43
+ when :TYPE_STRING, :TYPE_BYTES
44
+ string_default_value
45
+ when :TYPE_FLOAT, :TYPE_DOUBLE
46
+ float_double_default_value
47
+ else
48
+ verbatim_default_value
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def defaulted?
55
+ descriptor.respond_to_has_and_present?(:default_value)
56
+ end
57
+
58
+ def deprecated?
59
+ descriptor.options.try(:deprecated?) { false }
60
+ end
61
+
62
+ def extension?
63
+ descriptor.respond_to_has_and_present?(:extendee)
64
+ end
65
+
66
+ def compile
67
+ run_once(:compile) do
68
+ field_definition = if map?
69
+ ["map #{map_key_type_name}", map_value_type_name, name, number, applicable_options]
70
+ else
71
+ ["#{label} #{type_name}", name, number, applicable_options]
72
+ end
73
+ puts field_definition.flatten.compact.join(', ')
74
+ end
75
+ end
76
+
77
+ def label
78
+ @label ||= descriptor.label.name.to_s.downcase.sub(/label_/, '') # required, optional, repeated
79
+ end
80
+
81
+ def name
82
+ @name ||= descriptor.name.to_sym.inspect
83
+ end
84
+
85
+ def number
86
+ @number ||= descriptor.number
87
+ end
88
+
89
+ def field_options
90
+ @field_options ||= begin
91
+ opts = {}
92
+ opts[:default] = default_value if defaulted?
93
+ opts[:packed] = 'true' if packed?
94
+ opts[:deprecated] = 'true' if deprecated?
95
+ opts[:extension] = 'true' if extension?
96
+ if descriptor.options
97
+ descriptor.options.each_field do |field_option|
98
+ next unless descriptor.options.field?(field_option.name)
99
+ option_value = descriptor.options[field_option.name]
100
+ opts[field_option.fully_qualified_name] = serialize_value(option_value)
101
+ end
102
+ end
103
+ opts
104
+ end
105
+ end
106
+
107
+ def packed?
108
+ descriptor.options.try(:packed?) { false }
109
+ end
110
+
111
+ # Determine the field type
112
+ def type_name
113
+ @type_name ||= determine_type_name(descriptor)
114
+ end
115
+
116
+ # If this field is a map field, this returns a message descriptor that
117
+ # represents the entries in the map. Returns nil if this field is not
118
+ # a map field.
119
+ def map_entry
120
+ @map_entry ||= determine_map_entry
121
+ end
122
+
123
+ def map?
124
+ !map_entry.nil?
125
+ end
126
+
127
+ def map_key_type_name
128
+ return nil if map_entry.nil?
129
+ determine_type_name(map_entry.field.find { |v| v.name == 'key' && v.number == 1 })
130
+ end
131
+
132
+ def map_value_type_name
133
+ return nil if map_entry.nil?
134
+ determine_type_name(map_entry.field.find { |v| v.name == 'value' && v.number == 2 })
135
+ end
136
+
137
+ private
138
+
139
+ def enum_default_value
140
+ optionally_upcased_default =
141
+ if ENV.key?('PB_UPCASE_ENUMS')
142
+ verbatim_default_value.upcase
143
+ else
144
+ verbatim_default_value
145
+ end
146
+ "#{type_name}::#{optionally_upcased_default}"
147
+ end
148
+
149
+ def float_double_default_value
150
+ case verbatim_default_value
151
+ when PROTO_INFINITY_DEFAULT then
152
+ RUBY_INFINITY_DEFAULT
153
+ when PROTO_NEGATIVE_INFINITY_DEFAULT then
154
+ RUBY_NEGATIVE_INFINITY_DEFAULT
155
+ when PROTO_NAN_DEFAULT then
156
+ RUBY_NAN_DEFAULT
157
+ else
158
+ verbatim_default_value
159
+ end
160
+ end
161
+
162
+ def string_default_value
163
+ %("#{verbatim_default_value.gsub(/'/, '\\\\\'')}")
164
+ end
165
+
166
+ def verbatim_default_value
167
+ descriptor.default_value
168
+ end
169
+
170
+ def determine_type_name(descriptor)
171
+ case descriptor.type.name
172
+ when :TYPE_MESSAGE, :TYPE_ENUM, :TYPE_GROUP then
173
+ modulize(descriptor.type_name)
174
+ else
175
+ type_name = descriptor.type.name.to_s.downcase.sub(/^type_/, '')
176
+ ":#{type_name}"
177
+ end
178
+ end
179
+
180
+ def determine_map_entry
181
+ return nil if @enclosing_msg_descriptor.nil?
182
+ return nil unless descriptor.label.name == :LABEL_REPEATED && descriptor.type.name == :TYPE_MESSAGE
183
+ # find nested message type
184
+ name_parts = descriptor.type_name.split(".")
185
+ return nil if name_parts.size < 2 || name_parts[-2] != @enclosing_msg_descriptor.name
186
+ nested = @enclosing_msg_descriptor.nested_type.find { |e| e.name == name_parts[-1] }
187
+ return nested if !nested.nil? && nested.options.try(:map_entry?)
188
+ nil
189
+ end
190
+
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,262 @@
1
+ require 'set'
2
+ require 'protobuf/generators/base'
3
+ require 'protobuf/generators/group_generator'
4
+
5
+ module Protobuf
6
+ module Generators
7
+ class FileGenerator < Base
8
+
9
+ attr_reader :output_file
10
+
11
+ def initialize(*args)
12
+ super
13
+ @output_file = ::Google::Protobuf::Compiler::CodeGeneratorResponse::File.new(:name => file_name)
14
+ @extension_fields = Hash.new { |h, k| h[k] = [] }
15
+ @known_messages = {}
16
+ @known_enums = {}
17
+ @dangling_messages = {}
18
+ end
19
+
20
+ def file_name
21
+ convert_filename(descriptor.name, false)
22
+ end
23
+
24
+ def compile
25
+ run_once(:compile) do
26
+ map_extensions(descriptor, [descriptor.package])
27
+
28
+ print_file_comment
29
+ print_generic_requires
30
+ print_import_requires
31
+
32
+ print_package do
33
+ inject_optionable
34
+ group = GroupGenerator.new(current_indent)
35
+ group.add_options(descriptor.options) if descriptor.options
36
+ group.add_enums(descriptor.enum_type, :namespace => [descriptor.package])
37
+ group.add_message_declarations(descriptor.message_type)
38
+ group.add_messages(descriptor.message_type, :extension_fields => @extension_fields, :namespace => [descriptor.package])
39
+ group.add_extended_messages(unknown_extensions)
40
+ group.add_services(descriptor.service)
41
+
42
+ group.add_header(:enum, 'Enum Classes')
43
+ group.add_header(:message_declaration, 'Message Classes')
44
+ group.add_header(:options, 'File Options')
45
+ group.add_header(:message, 'Message Fields')
46
+ group.add_header(:extended_message, 'Extended Message Fields')
47
+ group.add_header(:service, 'Service Classes')
48
+ print group.to_s
49
+ end
50
+
51
+ end
52
+ end
53
+
54
+ def unknown_extensions
55
+ @unknown_extensions ||= @extension_fields.map do |message_name, fields|
56
+ message_klass = modulize(message_name).safe_constantize
57
+ if message_klass
58
+ unknown_fields = fields.reject do |field|
59
+ @known_messages[message_name] && message_klass.get_field(field.name, true)
60
+ end
61
+ [message_name, unknown_fields]
62
+ else
63
+ [message_name, fields]
64
+ end
65
+ end
66
+ end
67
+
68
+ def generate_output_file
69
+ compile
70
+ output_file.content = to_s
71
+ output_file
72
+ end
73
+
74
+ # Recursively map out all extensions known in this file.
75
+ # The key is the type_name of the message being extended, and
76
+ # the value is an array of field descriptors.
77
+ #
78
+ def map_extensions(descriptor, namespaces)
79
+ if fully_qualified_token?(descriptor.name)
80
+ fully_qualified_namespace = descriptor.name
81
+ elsif !(namespace = namespaces.reject(&:empty?).join(".")).empty?
82
+ fully_qualified_namespace = ".#{namespace}"
83
+ end
84
+ # Record all the message descriptor name's we encounter (should be the whole tree).
85
+ if descriptor.is_a?(::Google::Protobuf::DescriptorProto)
86
+ @known_messages[fully_qualified_namespace || descriptor.name] = descriptor
87
+ elsif descriptor.is_a?(::Google::Protobuf::EnumDescriptorProto)
88
+ @known_enums[fully_qualified_namespace || descriptor.name] = descriptor
89
+ return
90
+ end
91
+
92
+ descriptor.extension.each do |field_descriptor|
93
+ unless fully_qualified_token?(field_descriptor.name) && fully_qualified_namespace
94
+ field_descriptor.name = "#{fully_qualified_namespace}.#{field_descriptor.name}"
95
+ end
96
+ @extension_fields[field_descriptor.extendee] << field_descriptor
97
+ end
98
+
99
+ [:message_type, :nested_type, :enum_type].each do |type|
100
+ next unless descriptor.respond_to_has_and_present?(type)
101
+
102
+ descriptor.public_send(type).each do |type_descriptor|
103
+ map_extensions(type_descriptor, (namespaces + [type_descriptor.name]))
104
+ end
105
+ end
106
+ end
107
+
108
+ def print_file_comment
109
+ puts "# encoding: utf-8"
110
+ puts
111
+ puts "##"
112
+ puts "# This file is auto-generated. DO NOT EDIT!"
113
+ puts "#"
114
+ end
115
+
116
+ def print_generic_requires
117
+ print_require("protobuf")
118
+ print_require("protobuf/rpc/service") if descriptor.service.count > 0
119
+ puts
120
+ end
121
+
122
+ def print_import_requires
123
+ return if descriptor.dependency.empty?
124
+
125
+ header "Imports"
126
+
127
+ descriptor.dependency.each do |dependency|
128
+ print_require(convert_filename(dependency), ENV.key?('PB_REQUIRE_RELATIVE'))
129
+ end
130
+
131
+ puts
132
+ end
133
+
134
+ def print_package(&block)
135
+ namespaces = descriptor.package.split('.')
136
+ if namespaces.empty? && ENV.key?('PB_ALLOW_DEFAULT_PACKAGE_NAME')
137
+ namespaces = [File.basename(descriptor.name).sub('.proto', '')]
138
+ end
139
+ namespaces.reverse.reduce(block) do |previous, namespace|
140
+ -> { print_module(namespace, &previous) }
141
+ end.call
142
+ end
143
+
144
+ def eval_unknown_extensions!
145
+ @@evaled_dependencies ||= Set.new # rubocop:disable Style/ClassVars
146
+ @@all_messages ||= {} # rubocop:disable Style/ClassVars
147
+ @@all_enums ||= {} # rubocop:disable Style/ClassVars
148
+
149
+ map_extensions(descriptor, [descriptor.package])
150
+ @known_messages.each do |name, descriptor|
151
+ @@all_messages[name] = descriptor
152
+ end
153
+ @known_enums.each do |name, descriptor|
154
+ @@all_enums[name] = descriptor
155
+ end
156
+
157
+ # create package namespace
158
+ print_package {}
159
+ eval_code
160
+
161
+ unknown_extensions.each do |extendee, fields|
162
+ eval_dependencies(extendee)
163
+ fields.each do |field|
164
+ eval_dependencies(field.type_name)
165
+ end
166
+ end
167
+ group = GroupGenerator.new(0)
168
+ group.add_extended_messages(unknown_extensions, false)
169
+ print group.to_s
170
+ eval_code
171
+ rescue => e
172
+ warn "Error loading unknown extensions #{descriptor.name.inspect} error=#{e}"
173
+ raise e
174
+ end
175
+
176
+ private
177
+
178
+ def convert_filename(filename, for_require = true)
179
+ filename.sub(/\.proto/, (for_require ? '.pb' : '.pb.rb'))
180
+ end
181
+
182
+ def fully_qualified_token?(token)
183
+ token[0] == '.'
184
+ end
185
+
186
+ def eval_dependencies(name, namespace = nil)
187
+ name = "#{namespace}.#{name}" if namespace && !fully_qualified_token?(name)
188
+ return if name.empty? || @@evaled_dependencies.include?(name) || modulize(name).safe_constantize
189
+
190
+ # if name = .foo.bar.Baz look for classes / modules named ::Foo::Bar and ::Foo
191
+ # module == pure namespace (e.g. the descriptor package name)
192
+ # class == nested messages
193
+ create_ruby_namespace_heiarchy(name)
194
+
195
+ if (message = @@all_messages[name])
196
+ # Create the blank namespace in case there are nested types
197
+ eval_message_code(name)
198
+
199
+ message.nested_type.each do |nested_type|
200
+ eval_dependencies(nested_type.name, name) unless nested_type.name.empty?
201
+ end
202
+ message.field.each do |field|
203
+ eval_dependencies(field.type_name, name) unless field.type_name.empty?
204
+ end
205
+ message.enum_type.each do |enum_type|
206
+ eval_dependencies(enum_type.name, name)
207
+ end
208
+
209
+ # Check @@evaled_dependencies again in case there was a dependency
210
+ # loop that already loaded this message
211
+ return if @@evaled_dependencies.include?(name)
212
+ eval_message_code(name, message.field)
213
+ @@evaled_dependencies << name
214
+
215
+ elsif (enum = @@all_enums[name])
216
+ # Check @@evaled_dependencies again in case there was a dependency
217
+ # loop that already loaded this enum
218
+ return if @@evaled_dependencies.include?(name)
219
+ namespace = name.split(".")
220
+ eval_enum_code(enum, namespace[0..-2].join("."))
221
+ @@evaled_dependencies << name
222
+ else
223
+ fail "Error loading unknown dependencies, could not find message or enum #{name.inspect}"
224
+ end
225
+ end
226
+
227
+ def eval_message_code(fully_qualified_namespace, fields = [])
228
+ group = GroupGenerator.new(0)
229
+ group.add_extended_messages({ fully_qualified_namespace => fields }, false)
230
+ print group.to_s
231
+ eval_code
232
+ end
233
+
234
+ def eval_enum_code(enum, fully_qualified_namespace)
235
+ group = GroupGenerator.new(0)
236
+ group.add_enums([enum], :namespace => [fully_qualified_namespace])
237
+ print group.to_s
238
+ eval_code(modulize(fully_qualified_namespace).safe_constantize || Object)
239
+ end
240
+
241
+ def eval_code(context = Object)
242
+ warn "#{context.inspect}.module_eval #{print_contents.inspect}" if ENV['PB_DEBUG']
243
+ context.module_eval print_contents.to_s
244
+ @io.truncate(0)
245
+ @io.rewind
246
+ end
247
+
248
+ def create_ruby_namespace_heiarchy(namespace)
249
+ loop do
250
+ namespace, _match, _tail = namespace.rpartition(".")
251
+ break if namespace.empty?
252
+ eval_dependencies(namespace)
253
+ end
254
+ end
255
+
256
+ def inject_optionable
257
+ return if descriptor.package.empty? && !ENV.key?('PB_ALLOW_DEFAULT_PACKAGE_NAME')
258
+ puts "::Protobuf::Optionable.inject(self) { ::Google::Protobuf::FileOptions }"
259
+ end
260
+ end
261
+ end
262
+ end