gapic-generator 0.2.3 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/lib/gapic/formatting_utils.rb +9 -7
  4. data/lib/gapic/generator/version.rb +1 -1
  5. data/lib/gapic/generators/default_generator.rb +11 -10
  6. data/lib/gapic/helpers/filepath_helper.rb +1 -0
  7. data/lib/gapic/helpers/namespace_helper.rb +8 -1
  8. data/lib/gapic/presenters/field_presenter.rb +9 -9
  9. data/lib/gapic/presenters/file_presenter.rb +1 -1
  10. data/lib/gapic/presenters/gem_presenter.rb +4 -0
  11. data/lib/gapic/presenters/method_presenter.rb +24 -18
  12. data/lib/gapic/presenters/package_presenter.rb +16 -1
  13. data/lib/gapic/presenters/resource_presenter.rb +3 -9
  14. data/lib/gapic/presenters/service_presenter.rb +58 -13
  15. data/lib/gapic/resource_lookup.rb +23 -45
  16. data/lib/gapic/ruby_info.rb +93 -0
  17. data/lib/gapic/schema/api.rb +102 -1
  18. data/lib/gapic/schema/loader.rb +9 -2
  19. data/lib/gapic/schema/wrappers.rb +109 -22
  20. data/templates/default/gem/entrypoint.erb +8 -0
  21. data/templates/default/gem/gemspec.erb +1 -1
  22. data/templates/default/gem/readme.erb +15 -1
  23. data/templates/default/gem/rubocop.erb +13 -41
  24. data/templates/default/layouts/_ruby.erb +5 -4
  25. data/templates/default/lib/_package.erb +17 -0
  26. data/templates/default/lib/_service.erb +32 -0
  27. data/templates/default/package.erb +5 -5
  28. data/templates/default/proto_docs/_message.erb +2 -2
  29. data/templates/default/service.erb +5 -7
  30. data/templates/default/service/_helpers.erb +3 -0
  31. data/templates/default/service/client.erb +1 -1
  32. data/templates/default/service/client/_client.erb +13 -19
  33. data/templates/default/service/client/_config.erb +26 -26
  34. data/templates/default/service/client/_credentials.erb +1 -1
  35. data/templates/default/service/client/_operations.erb +1 -5
  36. data/templates/default/service/client/_resource.erb +1 -1
  37. data/templates/default/service/client/method/def/_options_defaults.erb +2 -2
  38. data/templates/default/service/client/method/def/_request_normal.erb +2 -2
  39. data/templates/default/service/client/method/def/_request_streaming.erb +3 -3
  40. data/templates/default/service/client/method/def/_response_normal.erb +1 -1
  41. data/templates/default/service/client/method/def/_response_paged.erb +2 -2
  42. data/templates/default/service/client/method/docs/_error.erb +1 -1
  43. data/templates/default/service/client/method/docs/_request_normal.erb +11 -6
  44. data/templates/default/service/client/method/docs/_request_streaming.erb +2 -2
  45. data/templates/default/service/client/method/docs/_response.erb +1 -1
  46. data/templates/default/service/client/resource/_def.erb +1 -1
  47. data/templates/default/service/client/resource/_multi.erb +2 -2
  48. data/templates/default/service/client/resource/_single.erb +1 -1
  49. data/templates/default/service/credentials.erb +1 -1
  50. data/templates/default/service/operations.erb +1 -1
  51. data/templates/default/service/paths.erb +1 -1
  52. data/templates/default/service/test/client.erb +16 -2
  53. data/templates/default/service/test/client_operations.erb +2 -2
  54. data/templates/default/service/test/method/_configure.erb +19 -0
  55. metadata +9 -4
  56. data/templates/default/service/client/_helpers.erb +0 -9
@@ -26,64 +26,42 @@ module Gapic
26
26
 
27
27
  # @private
28
28
  def lookup!
29
- resources = @api.files.flat_map { |file| lookup_file_resource_descriptors file }
30
- resources.compact.uniq
31
- end
32
-
33
- # @private
34
- def lookup_file_resource_descriptors file
35
29
  resources = []
36
- resources += file.resources.select { |resource| service_resource_types.include? resource.type }
37
- resources += file.messages.flat_map { |message| lookup_message_resources_descriptors message }
38
- resources
39
- end
40
-
41
- # @private
42
- def service_resource_types
43
- @service_resource_types ||= begin
44
- @service.methods.flat_map do |method|
45
- input_resource_types = message_resource_types method.input
46
-
47
- if @api.generate_path_helpers_output?
48
- output_resource_types = message_resource_types method.output
49
- input_resource_types + output_resource_types
50
- else
51
- input_resource_types
52
- end
53
- end.uniq
30
+ @service.methods.each do |method|
31
+ resources.concat resources_for_message(method.input)
32
+ resources.concat resources_for_message(method.output) if @api.generate_path_helpers_output?
54
33
  end
34
+ resources.uniq
55
35
  end
56
36
 
57
37
  # @private
58
- def message_resource_types message, seen_messages = []
59
- return [] if seen_messages.include? message
38
+ def resources_for_message message, seen_messages = []
39
+ resources = []
40
+ return resources if seen_messages.include? message
60
41
  seen_messages << message
61
- resource_types = []
62
- resource_types << message.resource.type if message.resource
63
- resource_types += message.nested_messages.map do |nested_message|
64
- message_resource_types nested_message, seen_messages
42
+ resources << message.resource if message.resource
43
+ message.nested_messages.each do |nested_message|
44
+ resources.concat resources_for_message(nested_message, seen_messages)
65
45
  end
66
46
  message.fields.each do |field|
67
- resource_types << field.resource_reference.type if field.resource_reference
68
- resource_types += message_resource_types field.message, seen_messages if field.message?
47
+ resources.concat resources_for_reference(field.resource_reference) if field.resource_reference
48
+ resources.concat resources_for_message(field.message, seen_messages) if field.message?
69
49
  end
70
- resource_types.flatten
50
+ resources
71
51
  end
72
52
 
73
53
  # @private
74
- def lookup_message_resources_descriptors message
75
- resources = []
76
-
77
- # We don't expect service_resource_types to iclude nil, so we can use message.resource&.type
78
- resources << message.resource if service_resource_types.include? message.resource&.type
79
-
80
- if message.nested_messages
81
- resources += message.nested_messages.flat_map do |nested_message|
82
- lookup_message_resources_descriptors nested_message
83
- end
54
+ # Given a reference (either a type or child type), return the corresponding
55
+ # resources.
56
+ def resources_for_reference reference
57
+ if (type = reference.type) && !type.empty?
58
+ Array(@api.lookup_resource_type(type))
59
+ elsif (child_type = reference.child_type) && !child_type.empty?
60
+ child_resource = @api.lookup_resource_type child_type
61
+ child_resource ? child_resource.parent_resources : []
62
+ else
63
+ []
84
64
  end
85
-
86
- resources
87
65
  end
88
66
 
89
67
  # Lookup all resources for a given service.
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright 2020 Google LLC
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module Gapic
18
+ ##
19
+ # Various Ruby language information useful for generators.
20
+ #
21
+ module RubyInfo
22
+ class << self
23
+ ##
24
+ # A sorted list of Ruby's keywords.
25
+ #
26
+ # @see https://docs.ruby-lang.org/en/2.7.0/keywords_rdoc.html
27
+ #
28
+ # @return [Array<String>]
29
+ #
30
+ def keywords
31
+ @keywords ||= [
32
+ "__ENCODING__",
33
+ "__LINE__",
34
+ "__FILE__",
35
+ "BEGIN",
36
+ "END",
37
+ "alias",
38
+ "and",
39
+ "begin",
40
+ "break",
41
+ "case",
42
+ "class",
43
+ "def",
44
+ "defined?",
45
+ "do",
46
+ "else",
47
+ "elsif",
48
+ "end",
49
+ "ensure",
50
+ "false",
51
+ "for",
52
+ "if",
53
+ "in",
54
+ "module",
55
+ "next",
56
+ "nil",
57
+ "not",
58
+ "or",
59
+ "redo",
60
+ "rescue",
61
+ "retry",
62
+ "return",
63
+ "self",
64
+ "super",
65
+ "then",
66
+ "true",
67
+ "undef",
68
+ "unless",
69
+ "until",
70
+ "when",
71
+ "while",
72
+ "yield"
73
+ ].freeze
74
+ end
75
+
76
+ ##
77
+ # A sorted list of method names that generated code should avoid.
78
+ # This includes methods of the Object class (including BasicObject and
79
+ # Kernel), Ruby keywords, and a few special names including "initialize"
80
+ # and "configure".
81
+ #
82
+ # @return [Array<String>]
83
+ #
84
+ def excluded_method_names
85
+ @excluded_method_names ||= begin
86
+ object_methods = (Object.instance_methods + Object.private_instance_methods).map(&:to_s)
87
+ other_methods = ["configure", "initialize"]
88
+ (object_methods + other_methods + keywords).sort.freeze
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -51,6 +51,7 @@ module Gapic
51
51
  loader.load_file fd, request.file_to_generate.include?(fd.name)
52
52
  end
53
53
  @files.each { |f| f.parent = self }
54
+ @resource_types = analyze_resources
54
55
  end
55
56
 
56
57
  def containing_api
@@ -201,7 +202,7 @@ module Gapic
201
202
  config = config_file ? YAML.load_file(config_file) : {}
202
203
  protoc_options.each do |k, v|
203
204
  next if k == "configuration"
204
- branch = key_to_str(k).split(".").reverse.inject(v) { |m, s| { str_to_key(s) => m } }
205
+ branch = parse_key(key_to_str(k)).reverse.inject(v) { |m, s| { str_to_key(s) => m } }
205
206
  config = deep_merge config, branch
206
207
  end
207
208
  config
@@ -224,6 +225,21 @@ module Gapic
224
225
  configuration[:generate_path_helpers_output] ||= false
225
226
  end
226
227
 
228
+ # Whether the override_proto_namespaces parameter was given in the configuration
229
+ def override_proto_namespaces_defined?
230
+ configuration.key? :override_proto_namespaces
231
+ end
232
+
233
+ # Sets the override_proto_namespaces parameter in the configuration
234
+ def override_proto_namespaces= value
235
+ configuration[:override_proto_namespaces] = value
236
+ end
237
+
238
+ # Whether namespace overrides apply to proto/grpc class references
239
+ def override_proto_namespaces?
240
+ configuration[:override_proto_namespaces] ||= false
241
+ end
242
+
227
243
  # Raw parsed json of the combined grpc service config files if provided
228
244
  # or an empty hash if no config was provided
229
245
  def grpc_service_config_raw
@@ -243,8 +259,77 @@ module Gapic
243
259
  end
244
260
  end
245
261
 
262
+ # Get a resource given its type string
263
+ def lookup_resource_type resource_type
264
+ @resource_types[resource_type]
265
+ end
266
+
267
+ # Given a service, find all common services that use it as a delegate.
268
+ def common_services_for delegate
269
+ @delegate_to_common ||= begin
270
+ (configuration[:common_services] || {}).each_with_object({}) do |(c, d), mapping|
271
+ (mapping[d] ||= []) << c
272
+ end
273
+ end
274
+ all_services = services
275
+ @delegate_to_common.fetch(delegate.address.join("."), []).map do |addr|
276
+ addr = addr.split "."
277
+ all_services.find { |s| s.address == addr }
278
+ end.compact.uniq
279
+ end
280
+
281
+ # Given a common service, return its delegate.
282
+ def delegate_service_for common
283
+ addr = (configuration[:common_services] || {})[common.address.join "."]
284
+ return nil unless addr
285
+ addr = addr.split "."
286
+ services.find { |s| s.address == addr }
287
+ end
288
+
246
289
  private
247
290
 
291
+ # Does a pre-analysis of all resources defined in the job. This has
292
+ # two effects:
293
+ # * Side effect: each resource has its parent_resources field set.
294
+ # * A mapping from resource type to resource wrapper is returned.
295
+ def analyze_resources
296
+ # In order to set parent_resources, we first populate a mapping from
297
+ # parsed pattern to resource mapping (in the patterns variable). This
298
+ # is done in one pass along with populating the resource type mapping.
299
+ # Then, we go through all resources again, get its expected parent
300
+ # patterns, and anything that shows up in the patterns mapping is taken
301
+ # to be a parent.
302
+ types = {}
303
+ patterns = {}
304
+ @files.each do |file|
305
+ file.resources.each { |resource| populate_resource_lookups resource, types, patterns }
306
+ file.messages.each { |message| populate_message_resource_lookups message, types, patterns }
307
+ end
308
+ types.each do |_type, resource|
309
+ parents = resource.parsed_parent_patterns
310
+ .map { |pat| patterns[pat] }
311
+ .compact.uniq
312
+ resource.parent_resources.replace parents
313
+ end
314
+ types
315
+ end
316
+
317
+ def populate_resource_lookups resource, types, patterns
318
+ types[resource.type] = resource
319
+ resource.parsed_patterns.each do |pat|
320
+ patterns[pat] = resource
321
+ end
322
+ end
323
+
324
+ def populate_message_resource_lookups message, types, patterns
325
+ populate_resource_lookups message.resource, types, patterns if message.resource
326
+ message.nested_messages.each do |nested|
327
+ populate_message_resource_lookups nested, types, patterns
328
+ end
329
+ end
330
+
331
+ # Parse a comma-delimited list of equals-delimited lists of strings, while
332
+ # mapping backslash-escaped commas and equal signs to literal characters.
248
333
  def parse_parameter str
249
334
  str.scan(/\\.|,|=|[^\\,=]+/)
250
335
  .each_with_object([[String.new]]) do |tok, arr|
@@ -261,6 +346,22 @@ module Gapic
261
346
  end
262
347
  end
263
348
 
349
+ # split the string on periods, but map backslash-escaped periods to
350
+ # literal periods.
351
+ def parse_key str
352
+ str.scan(/\.|\\.|[^\.\\]+/)
353
+ .each_with_object([String.new]) do |tok, arr|
354
+ if tok == "."
355
+ arr.append String.new
356
+ elsif tok.start_with? "\\"
357
+ arr.last << tok[1]
358
+ else
359
+ arr.last << tok
360
+ end
361
+ arr
362
+ end
363
+ end
364
+
264
365
  def str_to_key str
265
366
  str = str.to_s
266
367
  str.start_with?(":") ? str[1..-1].to_sym : str
@@ -64,9 +64,13 @@ module Gapic
64
64
  load_service registry, s, address, docs, [6, i]
65
65
  end
66
66
 
67
+ # Load top-level resources
68
+ resource_descriptors = file_descriptor.options[:".google.api.resource_definition"] if file_descriptor.options
69
+ resources = Array(resource_descriptors).map { |descriptor| Resource.new descriptor }
70
+
67
71
  # Construct and return the file.
68
72
  File.new file_descriptor, address, docs[path], messages, enums,
69
- services, file_to_generate, registry
73
+ services, resources, file_to_generate, registry
70
74
  end
71
75
 
72
76
  # Updates the fields of a message and it's nested messages.
@@ -158,9 +162,12 @@ module Gapic
158
162
  load_field registry, e, address, docs, path + [6, i]
159
163
  end
160
164
 
165
+ resource_descriptor = descriptor.options[:".google.api.resource"] if descriptor.options
166
+ resource = resource_descriptor ? Resource.new(resource_descriptor) : nil
167
+
161
168
  # Construct, cache, and return.
162
169
  msg = Message.new(descriptor, address, docs[path], fields, extensions,
163
- nested_messages, nested_enums)
170
+ resource, nested_messages, nested_enums)
164
171
  @prior_messages << msg
165
172
  add_to_registry registry, address, msg
166
173
  end
@@ -357,16 +357,19 @@ module Gapic
357
357
  # Wrapper for a protobuf file.
358
358
  #
359
359
  # @!attribute [r] messages
360
- # @ return [Enumerable<Message>] The top level messages contained in
360
+ # @return [Enumerable<Message>] The top level messages contained in
361
361
  # this file.
362
362
  # @!attribute [r] enums
363
- # @ return [Enumerable<Enum>] The top level enums contained in this
363
+ # @return [Enumerable<Enum>] The top level enums contained in this
364
364
  # file.
365
365
  # @!attribute [r] services
366
- # @ return [Enumerable<Service>] The services contained in this file.
366
+ # @return [Enumerable<Service>] The services contained in this file.
367
+ # @!attribute [r] resources
368
+ # @return [Enumerable<Resource>] The top level resources contained in
369
+ # this file.
367
370
  class File < Proto
368
371
  extend Forwardable
369
- attr_reader :messages, :enums, :services, :registry
372
+ attr_reader :messages, :enums, :services, :resources, :registry
370
373
 
371
374
  # Initializes a message object.
372
375
  # @param descriptor [Google::Protobuf::DescriptorProto] the protobuf
@@ -379,20 +382,23 @@ module Gapic
379
382
  # file.
380
383
  # @param enums [Enumerable<Enum>] The top level enums of this file.
381
384
  # @param services [Enumerable<Service>] The services of this file.
385
+ # @param resources [Enumerable<Resource>] The resources from this file.
382
386
  # @param generate [Boolean] Whether this file should be generated.
383
387
  def initialize descriptor, address, docs, messages, enums, services,
384
- generate, registry
388
+ resources, generate, registry
385
389
  super descriptor, address, docs
386
390
  @messages = messages || []
387
391
  @enums = enums || []
388
392
  @services = services || []
393
+ @resources = resources || []
389
394
  @generate = generate
390
395
  @registry = registry
391
396
 
392
397
  # Apply parent
393
398
  @messages.each { |m| m.parent = self }
394
- @enums.each { |m| m.parent = self }
399
+ @enums.each { |m| m.parent = self }
395
400
  @services.each { |m| m.parent = self }
401
+ @resources.each { |m| m.parent = self }
396
402
  end
397
403
 
398
404
  def containing_file
@@ -413,13 +419,6 @@ module Gapic
413
419
  options[:ruby_package] if options
414
420
  end
415
421
 
416
- # @return [Array<Google::Api::ResourceDescriptor>] A representation of the resource.
417
- # This is generally intended to be attached to the "name" field.
418
- # See `google/api/resource.proto`.
419
- def resources
420
- options[:".google.api.resource_definition"] if options
421
- end
422
-
423
422
  # @!method name
424
423
  # @return [String] file name, relative to root of source tree.
425
424
  # @!method package
@@ -511,6 +510,8 @@ module Gapic
511
510
  # @ return [Enumerable<Field>] The fields of a message.
512
511
  # @!attribute [r] extensions
513
512
  # @ return [Enumerable<Field>] The extensions of a message.
513
+ # @!attribute [r] resource
514
+ # @ return [Resource,nil] A representation of the resource.
514
515
  # @!attribute [r] nested_messages
515
516
  # @ return [Enumerable<Message>] The nested message declarations of a
516
517
  # message.
@@ -518,7 +519,7 @@ module Gapic
518
519
  # @ return [Enumerable<Enum>] The nested enum declarations of a message.
519
520
  class Message < Proto
520
521
  extend Forwardable
521
- attr_reader :fields, :extensions, :nested_messages, :nested_enums
522
+ attr_reader :fields, :extensions, :resource, :nested_messages, :nested_enums
522
523
 
523
524
  # Initializes a message object.
524
525
  # @param descriptor [Google::Protobuf::DescriptorProto] the protobuf
@@ -529,15 +530,17 @@ module Gapic
529
530
  # of the proto. See #docs for more info.
530
531
  # @param fields [Enumerable<Field>] The fields of this message.
531
532
  # @param extensions [Enumerable<Field>] The extensions of this message.
533
+ # @param resource [Resource,nil] The resource of this message, or nil if none.
532
534
  # @param nested_messages [Enumerable<Message>] The nested message
533
535
  # declarations of this message.
534
536
  # @param nested_enums [Enumerable<Enum>] The nested enum declarations
535
537
  # of this message.
536
- def initialize descriptor, address, docs, fields, extensions,
538
+ def initialize descriptor, address, docs, fields, extensions, resource,
537
539
  nested_messages, nested_enums
538
540
  super descriptor, address, docs
539
541
  @fields = fields || []
540
542
  @extensions = extensions || []
543
+ @resource = resource
541
544
  @nested_messages = nested_messages || []
542
545
  @nested_enums = nested_enums || []
543
546
 
@@ -545,13 +548,7 @@ module Gapic
545
548
  @extensions.each { |x| x.parent = self }
546
549
  @nested_messages.each { |m| m.parent = self }
547
550
  @nested_enums.each { |e| e.parent = self }
548
- end
549
-
550
- # @return [Google::Api::ResourceDescriptor] A representation of the resource.
551
- # This is generally intended to be attached to the "name" field.
552
- # See `google/api/resource.proto`.
553
- def resource
554
- options[:".google.api.resource"] if options
551
+ @resource.parent = self if @resource
555
552
  end
556
553
 
557
554
  # @return [Boolean] whether this type is a map entry
@@ -736,5 +733,95 @@ module Gapic
736
733
  :options
737
734
  )
738
735
  end
736
+
737
+ # Wrapper for a protobuf Resource.
738
+ #
739
+ # Unlike most wrappers, this does not subclass the {Proto} wrapper because
740
+ # it does not use the fields exposed by that wrapper (`address`, `docs`,
741
+ # etc.) This is here principally to augment the resource definition with
742
+ # information about resource parent-child relationships.
743
+ #
744
+ # Resource parentage is defined implicitly by path patterns. The algorithm
745
+ # is as follows:
746
+ # * If the final segment of a pattern is an ID segment (i.e. `*` or some
747
+ # `{name}`) then remove it and the previous segment (which we assume to
748
+ # be the corresponding collection identifier, as described in AIP-122.)
749
+ # The resulting pattern is what we expect a parent to have.
750
+ # * If the final segment is static, then assume the pattern represents a
751
+ # singleton resource (AIP-156) and remove only that one segment. The
752
+ # resulting pattern is what we expect a parent to have.
753
+ #
754
+ # The {Resource#parsed_parent_patterns} method returns the set of patterns
755
+ # we expect of parents. It is then possible to search for resources with
756
+ # those patterns to determine what the parents are.
757
+ #
758
+ # @!attribute [rw] parent
759
+ # @return [Gapic::Schema::File,Gapic::Schema::Message] The parent object.
760
+ # @!attribute [r] descriptor
761
+ # @return [Array<Gapic::Schema::ResourceDescriptor>] The resource
762
+ # descriptor.
763
+ # @!attribute [r] parsed_patterns
764
+ # @return [Array<Array<String>>] The normalized, segmented forms of the
765
+ # patterns. Normalized means all ID segments are replaced by asterisks
766
+ # to remove non-structural differences due to different names being
767
+ # used. Segmented means simply split on slashes.
768
+ # For example, if a pattern is `"projects/{project}""`, the
769
+ # corresponding parsed pattern would be `["projects", "*"]`.
770
+ # @!attribute [r] parent_resources
771
+ # @return [Array<Gapic::Schema::Resource>] Parent resources
772
+ class Resource
773
+ extend Forwardable
774
+ attr_reader :descriptor, :parsed_patterns, :parent_resources
775
+ attr_accessor :parent
776
+
777
+ # Initializes a resource object.
778
+ # @param descriptor [Google::Protobuf::ResourceDescriptor] the protobuf
779
+ # representation of this resource.
780
+ def initialize descriptor
781
+ @parent = nil
782
+ @descriptor = descriptor
783
+ @parsed_patterns = descriptor.pattern.map do |pattern|
784
+ pattern.split("/").map do |segment|
785
+ segment =~ %r{\{[^/\}]+(=[^\}]+)?\}} ? "*" : segment
786
+ end.freeze
787
+ end.freeze
788
+ @parent_resources = []
789
+ end
790
+
791
+ # Returns the "root" of this schema.
792
+ # @return [Gapic::Schema::Api]
793
+ def containing_api
794
+ parent&.containing_api
795
+ end
796
+
797
+ # Returns the file containing this proto entity
798
+ # @return [Gapic::Schema::File]
799
+ def containing_file
800
+ parent&.containing_file
801
+ end
802
+
803
+ # Returns parsed patterns for the expected parents.
804
+ # @return [Array<Array<String>>]
805
+ def parsed_parent_patterns
806
+ @parsed_patterns.map do |pat|
807
+ parent = pat.last =~ /^\*\*?$/ ? pat[0...-2] : pat[0...-1]
808
+ parent.empty? ? nil : parent
809
+ end.compact.uniq
810
+ end
811
+
812
+ # @!method type
813
+ # @return [String] the resource type string.
814
+ # @!method pattern
815
+ # @return [Array<String>] the set of patterns.
816
+ # @!method name_field
817
+ # @return [String] the field on the resource that designates the
818
+ # resource name field. If omitted, this is assumed to be "name".
819
+ def_delegators(
820
+ :descriptor,
821
+ :type,
822
+ :pattern,
823
+ :name_field
824
+ )
825
+ end
739
826
  end
740
827
  end