protobuf_descriptor 1.0.0 → 1.1.0

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis.yml +6 -0
  4. data/.yardopts +1 -1
  5. data/ChangeLog.rdoc +12 -0
  6. data/README.md +61 -0
  7. data/Rakefile +114 -0
  8. data/lib/protobuf_descriptor/enum_descriptor.rb +17 -11
  9. data/lib/protobuf_descriptor/enum_value_descriptor.rb +29 -0
  10. data/lib/protobuf_descriptor/field_descriptor.rb +80 -0
  11. data/lib/protobuf_descriptor/file_descriptor.rb +37 -17
  12. data/lib/protobuf_descriptor/has_children.rb +49 -0
  13. data/lib/protobuf_descriptor/has_parent.rb +42 -0
  14. data/lib/protobuf_descriptor/message_descriptor.rb +20 -104
  15. data/lib/protobuf_descriptor/method_descriptor.rb +54 -0
  16. data/lib/protobuf_descriptor/named_child.rb +7 -0
  17. data/lib/protobuf_descriptor/service_descriptor.rb +9 -61
  18. data/lib/protobuf_descriptor/version.rb +1 -1
  19. data/lib/protobuf_descriptor.rb +20 -5
  20. data/protobuf_descriptor.gemspec +3 -2
  21. data/spec/field_descriptor_spec.rb +1 -1
  22. data/spec/file_descriptor_spec.rb +0 -1
  23. data/spec/method_descriptor_spec.rb +1 -1
  24. data/spec/protobuf_descriptor_spec.rb +3 -3
  25. data/spec/protoc_generator_spec.rb +6 -9
  26. data/spec/protos/custom_options/custom_options.proto +53 -0
  27. data/spec/protos/custom_options/google/protobuf/descriptor.proto +620 -0
  28. data/spec/protos/custom_options.desc +0 -0
  29. data/spec/protos/custom_options.java.zip +0 -0
  30. data/spec/protos/custom_options.wire.zip +0 -0
  31. data/spec/protos/generator_test.desc +24 -0
  32. data/spec/protos/generator_test.java.zip +0 -0
  33. data/spec/protos/generator_test.wire.zip +0 -0
  34. data/spec/protos/service_rpc_test.desc +71 -0
  35. data/spec/protos/service_rpc_test.java.zip +0 -0
  36. data/spec/protos/service_rpc_test.wire.zip +0 -0
  37. data/spec/protos/single_file_test.desc +0 -0
  38. data/spec/protos/single_file_test.java.zip +0 -0
  39. data/spec/protos/single_file_test.wire.zip +0 -0
  40. data/spec/protos/source_info/foo.proto +59 -0
  41. data/spec/protos/source_info.desc +22 -0
  42. data/spec/protos/source_info.java.zip +0 -0
  43. data/spec/protos/source_info.srcinfo.desc +0 -0
  44. data/spec/protos/source_info.wire.zip +0 -0
  45. data/spec/service_descriptor_spec.rb +1 -1
  46. data/spec/source_code_info_spec.rb +57 -0
  47. data/spec/spec_helper.rb +29 -57
  48. data/spec/wire_generator_spec.rb +6 -11
  49. metadata +69 -11
  50. data/Gemfile.lock +0 -58
  51. data/README.rdoc +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 92458122a645db219c0487d36ef7ea0357716986
4
- data.tar.gz: 9a8be457e83289c5df3ff5f9103f05ef33023121
3
+ metadata.gz: 96fc09ef03f710c5955087be5c7296009c3e3122
4
+ data.tar.gz: 1be9567e58b0188dc5c9b6d84daf60168ca3d07c
5
5
  SHA512:
6
- metadata.gz: c8a7801ee8978c2f2c0ccd7520ea7a6c25b3013ffdc6af992b19daa025eed7acc3d0e015b7d062d9f2059f10123a94537e2a1f2d29624f26bb7bf32ef93fc4b8
7
- data.tar.gz: 019010a9b3e35dbdced724a078ecc31dd753a06c00adf95acbfb14c3bb712908c53af567e6006673194456d693ce27a08229ce544b924c53a3e39def6b348e12
6
+ metadata.gz: 0570eb154fab727bdcbe836ad82100ec194a730e0a906ff91178658c18bab228929816dc55ba41e9662e268f4305f8d4342c2fd8c9e5dfab5198ea64c9307974
7
+ data.tar.gz: efd4d050cd8b0298626ba3a5a882520b24c72f6b43b43adbb08ad015267b6aef10750b638c1e9ec59461b44dd0407879c9fbe89bfbb5dbd666696a2cdc85848d
data/.gitignore CHANGED
@@ -17,5 +17,8 @@ tmp
17
17
  _yardoc
18
18
  doc/
19
19
 
20
+ # Ignore gemfile.lock
21
+ Gemfile.lock
22
+
20
23
  # Ignore wire-compiler.jar, its auto-downloaded when running specs.
21
24
  wire-compiler.jar
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - "2.0.0"
5
+ - "2.1.0"
6
+ - "2.1.1"
data/.yardopts CHANGED
@@ -1 +1 @@
1
- --markup rdoc --title "protobuf_descriptor Documentation" --protected
1
+ --markup markdown --title "protobuf_descriptor Documentation" --protected
data/ChangeLog.rdoc CHANGED
@@ -1,3 +1,15 @@
1
+ === 1.1.0 / 2014-05-14
2
+
3
+ Add support for accessing the +source_code_info+ bits of protobuf
4
+ descriptors. This mainly means that the leading and trailing comments can be
5
+ extracted for various declarations.
6
+
7
+ === 1.0.1 / not released
8
+
9
+ Remove active_support dependency. The protobuf library still requires
10
+ active_support though, so this doesn't really change anything.
11
+
12
+
1
13
  === 1.0.0 / 2014-04-25
2
14
 
3
15
  Huge version bump, update gem dependency to not be non-sensical. Should have no
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # protobuf_descriptor
2
+
3
+ [![Gem Version](http://img.shields.io/gem/v/protobuf_descriptor.svg)][gem]
4
+ [![Build Status](http://img.shields.io/travis/hfwang/protobuf_descriptor.svg)][travis]
5
+ [![MIT license](http://img.shields.io/badge/license-MIT-red.svg)][license]
6
+
7
+ [gem]: https://rubygems.org/gems/protobuf_descriptor
8
+ [travis]: https://travis-ci.org/hfwang/protobuf_descriptor
9
+ [license]: https://github.com/hfwang/protobuf_descriptor/blob/master/LICENSE.txt
10
+
11
+ * [Homepage](https://rubygems.org/gems/protobuf_descriptor)
12
+ * [Documentation](http://rubydoc.info/github/hfwang/protobuf_descriptor/master/frames)
13
+
14
+ ## Description
15
+
16
+ Protobuf_descriptor provides helper methods to make working with Google Protocol
17
+ Buffer descriptors easier, go figure.. It handles type resolution, and computing
18
+ type names (both within protocol buffers and in the generated output).
19
+
20
+ ## Examples
21
+
22
+ Given the `descriptor.desc` generated by the protocol buffer compiler, you
23
+ can introspect the various data types. This example references the descriptor
24
+ that would be generated by compiling
25
+ [single_file.proto](https://github.com/hfwang/protobuf_descriptor/blob/master/spec/protos/single_file_test/single_file.proto)
26
+
27
+ ```ruby
28
+ require 'protobuf_descriptor'
29
+ descriptor = ProtobufDescriptor.load("descriptor.desc")
30
+
31
+ # Load a single file by its filename/basename
32
+ file_descriptor = descriptor[:single_file]
33
+
34
+ # Grab a handle to an enum, or a message
35
+ file_descriptor.messages[:FieldOptions]
36
+ file_descriptor.enums[:UnnestedEnum]
37
+
38
+ # Also allows resolving types by their fully qualified name:
39
+ descriptor.resolve_type(".porkbuns.UnnestedEnum") # note the leading "."
40
+ # or even doing so relative to another enum
41
+ descriptor.resolve_type("CType", ".porkbuns.FieldOptions")
42
+ ```
43
+
44
+ For even more gory details, please peruse the actual
45
+ [documentation](http://rubydoc.info/github/hfwang/protobuf_descriptor/master/frames).
46
+
47
+ ## Requirements
48
+
49
+ I've only tested this on Ruby 1.9.3+. If it works for you on an older version of
50
+ Ruby, let me know. You most likely want to install the
51
+ [Google Protocol Buffer library](https://code.google.com/p/protobuf/).
52
+
53
+ ## Install
54
+
55
+ $ gem install protobuf_descriptor
56
+
57
+ ## Copyright
58
+
59
+ Copyright (c) 2014 Hsiu-Fan Wang
60
+
61
+ See LICENSE.txt for details.
data/Rakefile CHANGED
@@ -41,6 +41,120 @@ task :spec => ["wire-compiler.jar"] do |t|
41
41
  # Declare this to ensure wire-compiler is downloaded
42
42
  end
43
43
 
44
+ desc "Compiles the protocol buffer decls used by the specs"
45
+ task :compile_spec_protos do
46
+ require "pathname"
47
+ require "tempfile"
48
+ require "tmpdir"
49
+ require "zip"
50
+
51
+ VERBOSE = ENV["VERBOSE"]
52
+
53
+ exec_proto_compiler = lambda do |args|
54
+ args = {
55
+ :source => ".",
56
+ :extra_args => [],
57
+ :plugin => nil,
58
+ :plugin_out => "",
59
+ :out => "out.desc",
60
+ :include_source_info => false,
61
+ }.merge(args)
62
+ args[:source] += '/' unless args[:source].end_with?('/')
63
+
64
+ command = []
65
+ command << "protoc"
66
+ command << "-I#{args[:source]}"
67
+ if args[:plugin]
68
+ command << "--#{args[:plugin]}_out=#{args[:plugin_out]}"
69
+ end
70
+ command += args[:extra_args]
71
+ command << "--descriptor_set_out=#{args[:out]}"
72
+ if args[:include_source_info]
73
+ command << "--include_source_info"
74
+ end
75
+ command += Dir.glob(File.join(args[:source], "**", "*.proto"))
76
+
77
+ rv = system(*command)
78
+
79
+ raise "ProtobufDescriptor generation failed!" unless rv
80
+ end
81
+
82
+ exec_wire_compiler = lambda do |args|
83
+ args = {
84
+ :out => ".",
85
+ :source => ".",
86
+ :extra_args => []
87
+ }.merge(args)
88
+
89
+ jar_path = File.realpath(File.join(File.dirname(__FILE__), "wire-compiler.jar"))
90
+ command = ["java", "-jar", jar_path]
91
+ command << "--proto_path=#{args[:source]}"
92
+ command += args[:extra_args]
93
+ command << "--java_out=#{args[:out]}"
94
+ sources = Dir.glob(File.join(args[:source], "**", "*.proto")).map { |p|
95
+ Pathname.new(p).relative_path_from(Pathname.new(args[:source])).to_s
96
+ }
97
+ command += sources
98
+
99
+ tmp_log = Tempfile.new(["wire-compiler", ".log"])
100
+ begin
101
+ rv = system(*command, out: [tmp_log.path, "a"], err: [tmp_log.path, "a"])
102
+ puts File.read(tmp_log) if VERBOSE
103
+
104
+ raise "Wire protobuf generation failed!\n#{File.read(tmp_log)}" unless rv
105
+ ensure
106
+ tmp_log.close
107
+ tmp_log.unlink
108
+ end
109
+ end
110
+ create_zip = lambda do |dir, dest|
111
+ dirpath = Pathname.new(dir)
112
+ File.delete(dest) if File.exist?(dest)
113
+ Zip::File.open(dest, Zip::File::CREATE) do |zipfile|
114
+ # require "pry"
115
+ # binding.pry
116
+ (Dir[File.join(dir, "**", "**")] + Dir[File.join(dir, "**")]).uniq.each do |file|
117
+ next if File.directory?(file)
118
+ relative_path = Pathname.new(file).relative_path_from(dirpath)
119
+ zipfile.add(relative_path, file)
120
+ puts "#{dest} <- #{relative_path}" if VERBOSE
121
+ end
122
+ end
123
+ end
124
+
125
+ file_sets = Dir["spec/protos/*"].select { |d| File.directory?(d) }
126
+
127
+ source = "spec/protos/source_info"
128
+ puts "Building #{source} (with included source info)"
129
+ exec_proto_compiler.call({
130
+ :out => "#{source}.srcinfo.desc",
131
+ :source => source,
132
+ :include_source_info => true
133
+ })
134
+ file_sets.each do |source|
135
+ puts "Building #{source}"
136
+ Dir.mktmpdir do |dir|
137
+ args = {
138
+ :out => "#{source}.desc",
139
+ :source => source,
140
+ :plugin => :java,
141
+ :plugin_out => dir
142
+ }
143
+ exec_proto_compiler.call(args)
144
+ create_zip.call(dir, "#{source}.java.zip")
145
+ end
146
+
147
+ Dir.mktmpdir do |dir|
148
+ args = {
149
+ :source => source,
150
+ :out => dir
151
+ }
152
+ exec_wire_compiler.call(args)
153
+ create_zip.call(dir, "#{source}.wire.zip")
154
+ end
155
+ end
156
+ end
157
+
44
158
  file "wire-compiler.jar" do |t|
45
159
  sh 'wget --no-check-certificate --output-document="wire-compiler.jar" "http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.squareup.wire&a=wire-compiler&v=LATEST&c=jar-with-dependencies"'
46
160
  end
@@ -1,12 +1,12 @@
1
- require "active_support"
2
- require "protobuf_descriptor/named_child"
3
- require "active_support/core_ext/module/delegation"
4
-
5
1
  class ProtobufDescriptor
6
2
  # Describes an enum type.
7
3
  #
8
- # See {+EnumDescriptorProto+}[https://code.google.com/p/protobuf/source/browse/trunk/src/google/protobuf/descriptor.proto#84]
4
+ # See {+EnumDescriptorProto+}[https://code.google.com/p/protobuf/source/browse/trunk/src/google/protobuf/descriptor.proto#171]
9
5
  class EnumDescriptor
6
+ include ProtobufDescriptor::HasParent
7
+ include ProtobufDescriptor::NamedChild
8
+ include ProtobufDescriptor::HasChildren
9
+
10
10
  # The containing {ProtobufDescriptor::FileDescriptor} or
11
11
  # {ProtobufDescriptor::MessageDescriptor} that defines this enum.
12
12
  attr_reader :parent
@@ -14,21 +14,27 @@ class ProtobufDescriptor
14
14
  # The +EnumDescriptorProto+ this +EnumDescriptor+ is wrapping.
15
15
  attr_reader :enum_descriptor_proto
16
16
 
17
+ # List of the enum values for this `EnumDescriptor` as a `NamedCollection`
18
+ # of {ProtobufDescriptor::EnumValueDescriptor}
19
+ attr_reader :value
20
+ alias_method :values, :value
21
+
22
+ register_children(:value, 2)
23
+
17
24
  def initialize(parent, enum_descriptor_proto)
18
25
  @parent = parent
19
26
  @enum_descriptor_proto = enum_descriptor_proto
27
+
28
+ @value = ProtobufDescriptor::NamedCollection.new(
29
+ enum_descriptor_proto.value.map { |m|
30
+ ProtobufDescriptor::EnumValueDescriptor.new(self, m)
31
+ })
20
32
  end
21
33
 
22
34
  # The name of the enum
23
35
  def name; enum_descriptor_proto.name; end
24
36
 
25
- # The possible values of the enum
26
- def value; enum_descriptor_proto.value; end
27
- alias_method :values, :value
28
-
29
37
  # The +EnumOptions+ defined for this enum
30
38
  def options; enum_descriptor_proto.options; end
31
-
32
- include NamedChild
33
39
  end
34
40
  end
@@ -0,0 +1,29 @@
1
+ class ProtobufDescriptor
2
+ # Describes an enum type.
3
+ #
4
+ # See {+EnumValueDescriptorProto+}[https://code.google.com/p/protobuf/source/browse/trunk/src/google/protobuf/descriptor.proto#180]
5
+ class EnumValueDescriptor
6
+ include ProtobufDescriptor::HasParent
7
+
8
+ # The containing {ProtobufDescriptor::EnumDescriptor} that this is a value
9
+ # for.
10
+ attr_reader :parent
11
+
12
+ # The +EnumValueDescriptorProto+ this +EnumValueDescriptor+ is wrapping.
13
+ attr_reader :enum_value_descriptor_proto
14
+
15
+ def initialize(parent, enum_value_descriptor_proto)
16
+ @parent = parent
17
+ @enum_value_descriptor_proto = enum_value_descriptor_proto
18
+ end
19
+
20
+ # The name of the enum value
21
+ def name; enum_value_descriptor_proto.name; end
22
+
23
+ # The number mapped to the enum value
24
+ def number; enum_value_descriptor_proto.number; end
25
+
26
+ # The +EnumValueOptions+ defined for this enum
27
+ def options; enum_value_descriptor_proto.options; end
28
+ end
29
+ end
@@ -0,0 +1,80 @@
1
+ class ProtobufDescriptor
2
+ # Describes a field within a message.
3
+ #
4
+ # See FieldDescriptorProto[https://code.google.com/p/protobuf/source/browse/trunk/src/google/protobuf/descriptor.proto#103]
5
+ class FieldDescriptor
6
+ include ProtobufDescriptor::HasParent
7
+
8
+ # The parent {ProtobufDescriptor::MessageDescriptor}
9
+ attr_reader :parent
10
+ # The +FieldDescriptorProto+ this +FieldDescriptor+ is wrapping.
11
+ attr_reader :field_descriptor_proto
12
+
13
+ def initialize(parent, field_descriptor_proto)
14
+ @parent = parent
15
+ @field_descriptor_proto = field_descriptor_proto
16
+ end
17
+
18
+ # The +FieldOptions+ for this field
19
+ def options
20
+ field_descriptor_proto.options
21
+ end
22
+
23
+ # Default value for this field.
24
+ # * For numeric types, contains the original text representation of the
25
+ # value.
26
+ # * For booleans, "true" or "false".
27
+ # * For strings, contains the default text contents (not escaped in any
28
+ # way).
29
+ # * For bytes, contains the C escaped value. All bytes >= 128 are
30
+ # escaped.
31
+ def default_value
32
+ field_descriptor_proto.default_value
33
+ end
34
+
35
+ # For extensions, this is the name of the type being extended. It is
36
+ # resolved in the same manner as type_name.
37
+ def extendee
38
+ field_descriptor_proto.extendee
39
+ end
40
+
41
+ # For message and enum types, this is the name of the type. If the name
42
+ # starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
43
+ # rules are used to find the type (i.e. first the nested types within this
44
+ # message are searched, then within the parent, on up to the root
45
+ # namespace).
46
+ #
47
+ # Note: the protocol buffer compiler always emits the fully qualified name!
48
+ def type_name
49
+ field_descriptor_proto.type_name
50
+ end
51
+
52
+ # Whether the field is optional/required/repeated.
53
+ def label
54
+ field_descriptor_proto.label
55
+ end
56
+
57
+ # The tag number of this field.
58
+ def number
59
+ field_descriptor_proto.number
60
+ end
61
+
62
+ # The name of this field.
63
+ def name
64
+ field_descriptor_proto.name
65
+ end
66
+
67
+ # If type_name is set, this need not be set. If both this and type_name
68
+ # are set, this must be either TYPE_ENUM or TYPE_MESSAGE.
69
+ def field_type
70
+ field_descriptor_proto.type
71
+ end
72
+
73
+ # Resolves the field's +type_name+, returning the
74
+ # {ProtobufDescriptor::MessageDescriptor} or
75
+ # {ProtobufDescriptor::EnumDescriptor} that this field will represent.
76
+ def resolve_type
77
+ protobuf_descriptor.resolve_type_name(self.type_name, self.parent)
78
+ end
79
+ end
80
+ end
@@ -1,16 +1,10 @@
1
- require "protobuf_descriptor/enum_descriptor"
2
- require "protobuf_descriptor/message_descriptor"
3
- require "protobuf_descriptor/service_descriptor"
4
-
5
- require "active_support"
6
- require "active_support/core_ext/object/blank"
7
- require "active_support/core_ext/string/inflections"
8
-
9
1
  class ProtobufDescriptor
10
2
  # Describes a complete .proto file.
11
3
  #
12
4
  # See {+FileDescriptorProto+}[https://code.google.com/p/protobuf/source/browse/trunk/src/google/protobuf/descriptor.proto#56]
13
5
  class FileDescriptor
6
+ include ProtobufDescriptor::HasChildren
7
+
14
8
  # The parent {ProtobufDescriptor}
15
9
  attr_reader :file_descriptor_set
16
10
 
@@ -29,6 +23,12 @@ class ProtobufDescriptor
29
23
  # NamedCollection of {ProtobufDescriptor::ServiceDescriptor}
30
24
  attr_reader :service
31
25
 
26
+ # Field index is hard-coded since these are a bit annoying to grab
27
+ # consistently with the different protocol buffer implementations.
28
+ self.register_children(:message_type, 4)
29
+ self.register_children(:enum_type, 5)
30
+ self.register_children(:service, 6)
31
+
32
32
  def initialize(file_descriptor_set, file_descriptor_proto) #:nodoc:
33
33
  # This is basically a parent pointer.
34
34
  @file_descriptor_set = file_descriptor_set
@@ -55,11 +55,13 @@ class ProtobufDescriptor
55
55
  alias_method :enums, :enum_types
56
56
  alias_method :services, :service
57
57
 
58
- # Set of all top-level messages, enums and services that are defined inside
59
- # of this file
60
- def children
61
- @children ||= ProtobufDescriptor::NamedCollection.new(
62
- @message_type.collection + @enum_type.collection + @service.collection)
58
+ # Whether source code info is associated with this descriptor
59
+ def has_source_code_info?
60
+ file_descriptor_proto.has_field?(:source_code_info)
61
+ end
62
+
63
+ def source_code_info
64
+ file_descriptor_proto.source_code_info
63
65
  end
64
66
 
65
67
  # Filename relative to root of source tree.
@@ -77,7 +79,7 @@ class ProtobufDescriptor
77
79
  # inappropriate because proto packages do not normally start with backwards
78
80
  # domain names.
79
81
  def java_package
80
- if file_descriptor_proto.has_field?(:options) && file_descriptor_proto.options.java_package.present?
82
+ if file_descriptor_proto.has_field?(:options) && present?(file_descriptor_proto.options.java_package)
81
83
  return file_descriptor_proto.options.java_package
82
84
  else
83
85
  return file_descriptor_proto.package
@@ -90,14 +92,14 @@ class ProtobufDescriptor
90
92
  # a .proto always translates to a single class, but you may want to
91
93
  # explicitly choose the class name).
92
94
  def java_outer_classname
93
- if file_descriptor_proto.has_field?(:options) && file_descriptor_proto.options.java_multiple_files.present?
95
+ if file_descriptor_proto.has_field?(:options) && present?(file_descriptor_proto.options.java_multiple_files)
94
96
  return nil
95
- elsif file_descriptor_proto.has_field?(:options) && file_descriptor_proto.options.java_outer_classname.present?
97
+ elsif file_descriptor_proto.has_field?(:options) && present?(file_descriptor_proto.options.java_outer_classname)
96
98
  return file_descriptor_proto.options.java_outer_classname
97
99
  else
98
100
  basename = name.split('/').last
99
101
  basename = basename.gsub('.proto', '')
100
- return basename.camelize
102
+ return camelize(basename)
101
103
  end
102
104
  end
103
105
 
@@ -123,5 +125,23 @@ class ProtobufDescriptor
123
125
  def fully_qualified_ruby_name
124
126
  return "::#{self.package.gsub('.', '::')}"
125
127
  end
128
+
129
+ private
130
+ # Copy over a bunch of methods that are normally part of active_support:
131
+ def present?(string)
132
+ return !blank?(string)
133
+ end
134
+
135
+ def blank?(string)
136
+ string.respond_to?(:empty?) ? !!string.empty? : !string
137
+ end
138
+
139
+ def camelize(string)
140
+ if string.respond_to?(:camelize)
141
+ return string.camelize
142
+ else
143
+ return string.split("_").map { |s| s.capitalize }.join("")
144
+ end
145
+ end
126
146
  end
127
147
  end
@@ -0,0 +1,49 @@
1
+ class ProtobufDescriptor
2
+ # Mixin module to support classes with different "kinds" of children
3
+ module HasChildren
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ # To be called by the containing class, registers a set of children
10
+ def register_children(children_method, child_kind_id)
11
+ @registered_children ||= Hash.new { |h, k| h[k] = [] }
12
+
13
+ @registered_children[child_kind_id] = children_method
14
+ end
15
+
16
+ def registered_children
17
+ return @registered_children
18
+ end
19
+ end
20
+
21
+ def named_children
22
+ return @named_children if @named_children
23
+
24
+ @named_children = NamedCollection.new([])
25
+
26
+ self.class.registered_children.each do |id, method|
27
+ collection = self.send(method)
28
+ collection = collection.is_a?(NamedCollection) ? collection.collection : collection
29
+ collection.each do |m|
30
+ @named_children << m if m.is_a?(NamedChild)
31
+ end
32
+ end
33
+ return @named_children
34
+ end
35
+
36
+ # Computes the "relative path" from this node to one of its direct children
37
+ # according to the rules specified by
38
+ # [descriptor.proto line 506](https://code.google.com/p/protobuf/source/browse/trunk/src/google/protobuf/descriptor.proto#506).
39
+ def compute_source_code_info_path_component(child)
40
+ self.class.registered_children.each do |kind_id, collection|
41
+ idx = self.send(collection).find_index(child)
42
+ if !idx.nil?
43
+ return [kind_id, idx]
44
+ end
45
+ end
46
+ raise "Could not find #{child} in #{self}"
47
+ end
48
+ end
49
+ end
@@ -1,4 +1,11 @@
1
1
  class ProtobufDescriptor
2
+ # Mixin module to make accessing ancestors easier.
3
+ #
4
+ # Also has a random method for computing the source code info path (as well as
5
+ # methods that rely on it to do things like grab comments and the like.)
6
+ #
7
+ # Classes that include this module must respond_to `parent`, in addition their
8
+ # parent include `HasChildren`
2
9
  module HasParent
3
10
  def file_descriptor
4
11
  p = self.parent
@@ -15,5 +22,40 @@ class ProtobufDescriptor
15
22
  end
16
23
  return p
17
24
  end
25
+
26
+ def compute_source_code_info_path
27
+ path_component = parent.compute_source_code_info_path_component(self)
28
+ parent_path = if parent.class.name == "ProtobufDescriptor::FileDescriptor"
29
+ []
30
+ else
31
+ parent.compute_source_code_info_path
32
+ end
33
+ return parent_path + path_component
34
+ end
35
+
36
+ def source_code_info_locations
37
+ raise "No source code info attached!" unless file_descriptor.has_source_code_info?
38
+
39
+ source_code_info_path = compute_source_code_info_path
40
+ return file_descriptor.source_code_info.location.select { |location|
41
+ location.path == source_code_info_path
42
+ }
43
+ end
44
+
45
+ def source_code_info_location
46
+ return source_code_info_locations.first
47
+ end
48
+
49
+ def source_code_info_span
50
+ return source_code_info_location.span
51
+ end
52
+
53
+ def leading_comments
54
+ return source_code_info_location.leading_comments
55
+ end
56
+
57
+ def trailing_comments
58
+ return source_code_info_location.trailing_comments
59
+ end
18
60
  end
19
61
  end