protobuf_descriptor 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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