foobara 0.0.92 → 0.0.93

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/projects/command/src/command_pattern_implementation/concerns/reflection.rb +39 -18
  4. data/projects/command/src/transformed_command.rb +83 -40
  5. data/projects/command_connectors/src/command_connector/commands/describe.rb +1 -5
  6. data/projects/command_connectors/src/command_connector.rb +61 -59
  7. data/projects/command_connectors/src/command_registry/exposed_command.rb +2 -2
  8. data/projects/command_connectors/src/command_registry/exposed_domain.rb +10 -3
  9. data/projects/command_connectors/src/command_registry/exposed_organization.rb +2 -2
  10. data/projects/command_connectors/src/serializers/atomic_serializer.rb +1 -1
  11. data/projects/command_connectors/src/transformers/load_delegated_attributes_entities_pre_commit_transformer.rb +40 -0
  12. data/projects/common/src/data_path.rb +16 -0
  13. data/projects/common/src/error.rb +10 -4
  14. data/projects/common/src/possible_error.rb +9 -3
  15. data/projects/detached_entity/src/concerns/associations.rb +26 -20
  16. data/projects/detached_entity/src/concerns/reflection.rb +3 -3
  17. data/projects/detached_entity/src/detached_entity_type.rb +8 -3
  18. data/projects/detached_entity/src/extensions/builtin_types/detached_entity/validators/{attributes_declaration.rb → model_instance_is_valid.rb} +1 -1
  19. data/projects/domain/src/domain_module_extension.rb +12 -4
  20. data/projects/domain/src/extensions/foobara.rb +31 -28
  21. data/projects/domain/src/is_manifestable.rb +6 -2
  22. data/projects/domain/src/organization_module_extension.rb +6 -2
  23. data/projects/entity/src/concerns/queries.rb +29 -31
  24. data/projects/entity/src/extensions/builtin_types/entity/validators/{attributes_declaration.rb → model_instance_is_valid.rb} +1 -1
  25. data/projects/model/src/concerns/reflection.rb +8 -2
  26. data/projects/model/src/concerns/types.rb +121 -23
  27. data/projects/model/src/extensions/builtin_types/model/supported_transformers/mutable.rb +1 -2
  28. data/projects/model/src/extensions/builtin_types/model/validators/{attributes_declaration.rb → model_instance_is_valid.rb} +1 -1
  29. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/array_with_symbolic_elements.rb +29 -0
  30. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/delegates_desugarizer.rb +39 -0
  31. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/delegates_validator.rb +44 -0
  32. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/move_private_from_element_types_to_root.rb +48 -0
  33. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/symbolize_private.rb +34 -0
  34. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/to_type_transformer.rb +34 -2
  35. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/valid_attribute_names.rb +54 -0
  36. data/projects/model/src/model.rb +9 -0
  37. data/projects/model_attribute_helpers/src/attribute_helpers.rb +25 -4
  38. data/projects/namespace/src/is_namespace.rb +34 -15
  39. data/projects/persistence/src/entity_attributes_crud_driver.rb +25 -1
  40. data/projects/persistence/src/entity_base/transaction_table.rb +28 -3
  41. data/projects/type_declarations/src/desugarizer.rb +1 -1
  42. data/projects/type_declarations/src/type_builder.rb +42 -39
  43. data/projects/type_declarations/src/type_declaration_handler.rb +1 -1
  44. data/projects/type_declarations/src/type_declaration_handler_registry.rb +1 -1
  45. data/projects/type_declarations/src/type_declaration_validator.rb +1 -1
  46. data/projects/type_declarations/src/type_declarations.rb +39 -10
  47. data/projects/types/src/extensions/error.rb +3 -3
  48. data/projects/types/src/type/concerns/reflection.rb +79 -4
  49. data/projects/types/src/type.rb +42 -17
  50. data/projects/value/src/caster.rb +1 -1
  51. data/projects/value/src/mutator.rb +1 -1
  52. data/projects/value/src/processor/casting.rb +1 -1
  53. data/projects/value/src/processor/pipeline.rb +1 -1
  54. data/projects/value/src/processor/selection.rb +1 -1
  55. data/projects/value/src/processor.rb +13 -5
  56. data/projects/value/src/transformer.rb +1 -1
  57. data/projects/value/src/validator.rb +1 -1
  58. metadata +12 -5
@@ -98,28 +98,29 @@ module Foobara
98
98
  path,
99
99
  filter: nil,
100
100
  mode: LookupMode::GENERAL,
101
- visited: Set.new
101
+ visited: Set.new,
102
+ initial: true
102
103
  )
103
- visited_key = [path, mode, self]
104
+ path = Namespace.to_registry_path(path)
105
+ visited_key = [path, mode, initial, self]
104
106
  return nil if visited.include?(visited_key)
105
107
 
106
108
  visited << visited_key
107
109
 
108
110
  LookupMode.validate!(mode)
109
111
 
110
- path = Namespace.to_registry_path(path)
111
-
112
112
  if mode == LookupMode::RELAXED
113
113
  scoped = foobara_lookup(
114
114
  path,
115
115
  filter:,
116
116
  mode: LookupMode::GENERAL,
117
- visited:
117
+ visited:,
118
+ initial: false
118
119
  )
119
120
  return scoped if scoped
120
121
 
121
122
  candidates = foobara_children.map do |namespace|
122
- namespace.foobara_lookup(path, filter:, mode:, visited:)
123
+ namespace.foobara_lookup(path, filter:, mode:, visited:, initial: false)
123
124
  end.compact
124
125
 
125
126
  if candidates.size > 1
@@ -129,7 +130,8 @@ module Foobara
129
130
  # :nocov:
130
131
  end
131
132
 
132
- return candidates.first || foobara_parent_namespace&.foobara_lookup(path, filter:, mode:, visited:)
133
+ return candidates.first ||
134
+ foobara_parent_namespace&.foobara_lookup(path, filter:, mode:, visited:, initial: false)
133
135
  end
134
136
 
135
137
  if path[0] == ""
@@ -141,7 +143,7 @@ module Foobara
141
143
  path = path[(foobara_root_namespace.scoped_path.size + 1)..]
142
144
  end
143
145
 
144
- return foobara_root_namespace.foobara_lookup(path, filter:, mode: LookupMode::ABSOLUTE)
146
+ return foobara_root_namespace.foobara_lookup(path, filter:, mode: LookupMode::ABSOLUTE, initial: true)
145
147
  end
146
148
 
147
149
  partial = foobara_registry.lookup(path, filter)
@@ -166,12 +168,16 @@ module Foobara
166
168
  return scoped if scoped
167
169
 
168
170
  if [LookupMode::GENERAL, LookupMode::STRICT].include?(mode) && foobara_parent_namespace
169
- scoped = foobara_parent_namespace.foobara_lookup(path, filter:, mode: LookupMode::STRICT, visited:)
171
+ scoped = foobara_parent_namespace.foobara_lookup(
172
+ path, filter:, mode: LookupMode::STRICT, visited:, initial: false
173
+ )
170
174
  return scoped if scoped
171
175
  end
172
176
 
173
- scoped = _lookup_in(path, foobara_depends_on_namespaces, filter:, visited:)
174
- return scoped if scoped
177
+ if mode == LookupMode::GENERAL
178
+ scoped = _lookup_in(path, foobara_depends_on_namespaces, filter:, visited:)
179
+ return scoped if scoped
180
+ end
175
181
 
176
182
  to_consider = case mode
177
183
  when LookupMode::GENERAL
@@ -181,7 +187,7 @@ module Foobara
181
187
  end
182
188
 
183
189
  candidates = to_consider.map do |namespace|
184
- namespace.foobara_lookup(path, filter:, mode:, visited:)
190
+ namespace.foobara_lookup(path, filter:, mode:, visited:, initial: false)
185
191
  end.compact
186
192
 
187
193
  if candidates.size > 1
@@ -190,7 +196,19 @@ module Foobara
190
196
  # :nocov:
191
197
  end
192
198
 
193
- candidates.first || partial
199
+ scoped = candidates.first || partial
200
+ return scoped if scoped
201
+
202
+ # As a last resort we'll see if we're trying to fetch a builtin type from a different namespace
203
+ # TODO: these lookup modes are really confusing and were designed prior to having multiple root
204
+ # namespaces playing a role in command connectors.
205
+ if initial
206
+ scoped = Namespace.global.foobara_lookup(path, filter:, mode: LookupMode::ABSOLUTE, visited:, initial: false)
207
+
208
+ if scoped.is_a?(Types::Type) && scoped.builtin?
209
+ scoped
210
+ end
211
+ end
194
212
  end
195
213
 
196
214
  def foobara_parent_namespace
@@ -330,14 +348,15 @@ module Foobara
330
348
  path[matching_child_score..],
331
349
  mode: LookupMode::ABSOLUTE,
332
350
  filter:,
333
- visited:
351
+ visited:,
352
+ initial: false
334
353
  )
335
354
 
336
355
  return scoped if scoped
337
356
  end
338
357
 
339
358
  last_resort.uniq.each do |namespace|
340
- scoped = namespace.foobara_lookup(path, filter:, mode: LookupMode::ABSOLUTE, visited:)
359
+ scoped = namespace.foobara_lookup(path, filter:, mode: LookupMode::ABSOLUTE, visited:, initial: false)
341
360
  return scoped if scoped
342
361
  end
343
362
 
@@ -155,7 +155,31 @@ module Foobara
155
155
  end
156
156
 
157
157
  def matches_attributes_filter?(attributes, attributes_filter)
158
- attributes_filter.all? { |attribute_name, value| attributes[attribute_name] == value }
158
+ attributes_filter.all? do |attribute_name_or_path, value|
159
+ type = nil
160
+
161
+ if attribute_name_or_path.is_a?(::Array)
162
+ values = DataPath.values_at(attribute_name_or_path, attributes)
163
+
164
+ if values.include?(value)
165
+ true
166
+ else
167
+ type ||= entity_class.model_type.type_at_path(attribute_name_or_path)
168
+ if type.extends?(:detached_entity)
169
+ values.any? do |v|
170
+ value.primary_key == v
171
+ end
172
+ end
173
+ end
174
+ elsif attributes[attribute_name_or_path] == value
175
+ true
176
+ else
177
+ type ||= entity_class.model_type.type_at_path(attribute_name_or_path)
178
+ if type.extends?(:detached_entity)
179
+ value.primary_key == attributes[attribute_name_or_path]
180
+ end
181
+ end
182
+ end
159
183
  end
160
184
 
161
185
  def insert(_attributes)
@@ -225,8 +225,8 @@ module Foobara
225
225
  find_by(attribute_name => value)
226
226
  end
227
227
 
228
- def find_all_by_attribute(attribute_name, value)
229
- find_many_by(attribute_name => value)
228
+ def find_all_by_attribute(attribute_name_or_path, value)
229
+ find_many_by(attribute_name_or_path => value)
230
230
  end
231
231
 
232
232
  def find_by_attribute_containing(attribute_name, value)
@@ -283,13 +283,38 @@ module Foobara
283
283
 
284
284
  def find_many_by(attributes_filter)
285
285
  find_by_type = entity_class.domain.foobara_type_from_declaration(entity_class.attributes_for_find_by)
286
- attributes_filter = find_by_type.process_value!(attributes_filter)
286
+
287
+ path_filters = {}
288
+ regular_filters = {}
289
+
290
+ attributes_filter.each_pair do |attribute_name_or_path, value|
291
+ case attribute_name_or_path
292
+ when ::Symbol, ::String
293
+ regular_filters[attribute_name_or_path] = value
294
+ when ::Array, Value::DataPath
295
+ path_filters[attribute_name_or_path] = value
296
+ else
297
+ # :nocov:
298
+ raise "Unexpected filter type: #{attribute_name_or_path.class}"
299
+ # :nocov:
300
+ end
301
+ end
302
+
303
+ regular_filters = find_by_type.process_value!(regular_filters)
304
+
305
+ path_filters.keys.each do |path|
306
+ type = entity_class.deep_associations[DataPath.for(path).to_s]
307
+ path_filters[path] = type.process_value!(path_filters[path])
308
+ end
309
+
310
+ attributes_filter = regular_filters.merge(path_filters)
287
311
 
288
312
  yielded_ids = Set.new
289
313
 
290
314
  Enumerator.new do |yielder|
291
315
  tracked_records.each do |record|
292
316
  next if hard_deleted?(record)
317
+ next unless record.loaded? || record.built? || record.created?
293
318
 
294
319
  if entity_attributes_crud_driver_table.matches_attributes_filter?(record.attributes, attributes_filter)
295
320
  yielded_ids << record.primary_key
@@ -10,7 +10,7 @@ module Foobara
10
10
  false
11
11
  end
12
12
 
13
- def foobara_manifest(to_include: Set.new, remove_sensitive: false)
13
+ def foobara_manifest
14
14
  # :nocov:
15
15
  super.merge(processor_type: :desugarizer)
16
16
  # :nocov:
@@ -2,6 +2,46 @@ require "foobara/lru_cache"
2
2
 
3
3
  module Foobara
4
4
  module TypeDeclarations
5
+ class << self
6
+ # TODO: relocate these to a different file
7
+ def args_to_type_declaration(*args, &block)
8
+ if block
9
+ unless args.empty?
10
+ # :nocov:
11
+ raise ArgumentError, "Cannot provide both block and declaration"
12
+ # :nocov:
13
+ end
14
+
15
+ block
16
+ else
17
+ case args.size
18
+ when 0
19
+ # :nocov:
20
+ raise ArgumentError, "expected 1 argument or a block but got 0 arguments and no block"
21
+ # :nocov:
22
+ when 1
23
+ args.first
24
+ else
25
+ type, *symbolic_processors, processor_data = args
26
+
27
+ if !symbolic_processors.empty?
28
+ symbolic_processors = symbolic_processors.to_h { |symbol| [symbol, true] }
29
+
30
+ if processor_data.is_a?(::Hash) && !processor_data.empty?
31
+ processor_data.merge(symbolic_processors)
32
+ else
33
+ symbolic_processors
34
+ end
35
+ elsif processor_data.is_a?(::Hash)
36
+ processor_data
37
+ else
38
+ { processor_data.to_sym => true }
39
+ end.merge(type:)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
5
45
  class TypeBuilder
6
46
  class NoTypeDeclarationHandlerFoundError < StandardError; end
7
47
 
@@ -70,27 +110,8 @@ module Foobara
70
110
  end
71
111
  end
72
112
 
73
- def type_for_declaration_without_cache(*type_declaration_bits, &block)
74
- type_declaration = if block
75
- unless type_declaration_bits.empty?
76
- # :nocov:
77
- raise ArgumentError, "Cannot provide both block and declaration"
78
- # :nocov:
79
- end
80
-
81
- block
82
- else
83
- case type_declaration_bits.size
84
- when 0
85
- # :nocov:
86
- raise ArgumentError, "expected 1 argument or a block but got 0 arguments and no block"
87
- # :nocov:
88
- when 1
89
- type_declaration_bits.first
90
- else
91
- type_declaration_bits_to_type_declaration(type_declaration_bits)
92
- end
93
- end
113
+ def type_for_declaration_without_cache(*type_declaration_bits, &)
114
+ type_declaration = TypeDeclarations.args_to_type_declaration(*type_declaration_bits, &)
94
115
 
95
116
  handler = type_declaration_handler_for(type_declaration)
96
117
  handler.process_value!(type_declaration)
@@ -107,24 +128,6 @@ module Foobara
107
128
  def lru_cache
108
129
  @lru_cache ||= Foobara::LruCache.new(100)
109
130
  end
110
-
111
- def type_declaration_bits_to_type_declaration(type_declaration_bits)
112
- type, *symbolic_processors, processor_data = type_declaration_bits
113
-
114
- if !symbolic_processors.empty?
115
- symbolic_processors = symbolic_processors.to_h { |symbol| [symbol, true] }
116
-
117
- if processor_data.is_a?(::Hash) && !processor_data.empty?
118
- processor_data.merge(symbolic_processors)
119
- else
120
- symbolic_processors
121
- end
122
- elsif processor_data.is_a?(::Hash)
123
- processor_data
124
- else
125
- { processor_data.to_sym => true }
126
- end.merge(type:)
127
- end
128
131
  end
129
132
  end
130
133
  end
@@ -4,7 +4,7 @@ module Foobara
4
4
  module TypeDeclarations
5
5
  class TypeDeclarationHandler < Value::Processor::Pipeline
6
6
  class << self
7
- def foobara_manifest(to_include: Set.new, remove_sensitive: false)
7
+ def foobara_manifest
8
8
  # :nocov:
9
9
  super.merge(processor_type: :type_declaration_handler)
10
10
  # :nocov:
@@ -2,7 +2,7 @@ module Foobara
2
2
  module TypeDeclarations
3
3
  class TypeDeclarationHandlerRegistry < Value::Processor::Selection
4
4
  class << self
5
- def foobara_manifest(to_include: Set.new, remove_sensitive: false)
5
+ def foobara_manifest
6
6
  # :nocov:
7
7
  super.merge(processor_type: :type_declaration_handler_registry)
8
8
  # :nocov:
@@ -4,7 +4,7 @@ module Foobara
4
4
  include WithRegistries
5
5
 
6
6
  class << self
7
- def foobara_manifest(to_include: Set.new, remove_sensitive: false)
7
+ def foobara_manifest
8
8
  # :nocov:
9
9
  super.merge(processor_type: :type_declaration_validator)
10
10
  # :nocov:
@@ -68,22 +68,17 @@ module Foobara
68
68
  using_mode(Mode::STRICT_STRINGIFIED, &)
69
69
  end
70
70
 
71
- def using_mode(new_mode)
72
- old_mode = Thread.foobara_var_get(:foobara_type_declarations_mode)
73
- begin
74
- Thread.foobara_var_set(:foobara_type_declarations_mode, new_mode)
75
- yield
76
- ensure
77
- Thread.foobara_var_set(:foobara_type_declarations_mode, old_mode)
78
- end
71
+ # TODO: use manifest context instead
72
+ def using_mode(new_mode, &)
73
+ with_manifest_context(mode: new_mode, &)
79
74
  end
80
75
 
81
76
  def strict?
82
- Thread.foobara_var_get(:foobara_type_declarations_mode) == Mode::STRICT
77
+ foobara_manifest_context_mode == Mode::STRICT
83
78
  end
84
79
 
85
80
  def strict_stringified?
86
- Thread.foobara_var_get(:foobara_type_declarations_mode) == Mode::STRICT_STRINGIFIED
81
+ foobara_manifest_context_mode == Mode::STRICT_STRINGIFIED
87
82
  end
88
83
 
89
84
  # TODO: we should desugarize these but can't because of a bug where desugarizing entities results in creating the
@@ -94,6 +89,40 @@ module Foobara
94
89
 
95
90
  declaration1 == declaration2
96
91
  end
92
+
93
+ def foobara_manifest_context
94
+ Thread.foobara_var_get("foobara_manifest_context")
95
+ end
96
+
97
+ allowed_context_keys = %i[detached to_include mode remove_sensitive]
98
+ booleans = %i[detached remove_sensitive]
99
+
100
+ booleans.each do |context_item|
101
+ define_method "foobara_manifest_context_#{context_item}?" do
102
+ !!foobara_manifest_context&.[](context_item)
103
+ end
104
+ end
105
+
106
+ (allowed_context_keys - booleans).each do |context_item|
107
+ define_method "foobara_manifest_context_#{context_item}" do
108
+ foobara_manifest_context&.[](context_item)
109
+ end
110
+ end
111
+
112
+ def manifest_context_set?(context_item)
113
+ foobara_manifest_context&.key?(context_item)
114
+ end
115
+
116
+ def with_manifest_context(context)
117
+ old_context = foobara_manifest_context
118
+ begin
119
+ new_context = (old_context || {}).merge(context)
120
+ Thread.foobara_var_set("foobara_manifest_context", new_context)
121
+ yield
122
+ ensure
123
+ Thread.foobara_var_set("foobara_manifest_context", old_context)
124
+ end
125
+ end
97
126
  end
98
127
  end
99
128
  end
@@ -1,13 +1,13 @@
1
1
  module Foobara
2
2
  class Error
3
3
  class << self
4
- def types_depended_on(*args, remove_sensitive: false)
4
+ def types_depended_on(*args)
5
5
  if args.size == 1
6
- context_type.types_depended_on(args.first, remove_sensitive:)
6
+ context_type.types_depended_on(args.first)
7
7
  elsif args.empty?
8
8
  begin
9
9
  if context_type
10
- context_type.types_depended_on(remove_sensitive:)
10
+ context_type.types_depended_on
11
11
  else
12
12
  raise Foobara::TypeDeclarations::ErrorExtension::NoContextTypeSetError
13
13
  end
@@ -6,7 +6,9 @@ module Foobara
6
6
  include Concern
7
7
 
8
8
  # as soon as we hit a registered type, don't go further down that path
9
- def types_depended_on(result = nil, remove_sensitive: false)
9
+ def types_depended_on(result = nil)
10
+ remove_sensitive = TypeDeclarations.foobara_manifest_context_remove_sensitive?
11
+
10
12
  start = result.nil?
11
13
  result ||= Set.new
12
14
  return if result.include?(self)
@@ -15,7 +17,7 @@ module Foobara
15
17
 
16
18
  return if !start && registered?
17
19
 
18
- to_process = types_to_add_to_manifest(remove_sensitive:)
20
+ to_process = types_to_add_to_manifest
19
21
 
20
22
  if element_types
21
23
  to_process += case element_types
@@ -48,7 +50,7 @@ module Foobara
48
50
  end
49
51
 
50
52
  to_process.each do |type|
51
- type.types_depended_on(result, remove_sensitive:)
53
+ type.types_depended_on(result)
52
54
  end
53
55
 
54
56
  if start
@@ -58,7 +60,9 @@ module Foobara
58
60
  result
59
61
  end
60
62
 
61
- def types_to_add_to_manifest(remove_sensitive: false)
63
+ def types_to_add_to_manifest
64
+ remove_sensitive = TypeDeclarations.foobara_manifest_context_remove_sensitive?
65
+
62
66
  types = [*base_type, *possible_errors.map(&:error_class)]
63
67
 
64
68
  if element_type && (!remove_sensitive || !element_type.sensitive?)
@@ -86,6 +90,77 @@ module Foobara
86
90
  result.select(&:registered?)
87
91
  end
88
92
 
93
+ def type_at_path(data_path)
94
+ path_parts = DataPath.for(data_path).path
95
+
96
+ path_part, *path_parts = path_parts
97
+
98
+ next_type = case path_part
99
+ when :"#"
100
+ unless element_type
101
+ # :nocov:
102
+ raise "Expected element_type to be set but is not"
103
+ # :nocov:
104
+ end
105
+
106
+ element_type
107
+ when Symbol
108
+ case element_types
109
+ when ::Hash
110
+ unless element_types.key?(path_part)
111
+ # :nocov:
112
+ raise "Expected element type to have key #{path_part} but does not"
113
+ # :nocov:
114
+ end
115
+
116
+ element_types[path_part]
117
+ when Types::Type
118
+ unless element_types.extends?(BuiltinTypes[:attributes])
119
+ # :nocov:
120
+ raise "Expected element type to be a Type but is not"
121
+ # :nocov:
122
+ end
123
+
124
+ # TODO: We assume it's attributes here but maybe we should assert that
125
+ element_types.element_types[path_part]
126
+ when nil
127
+ # :nocov:
128
+ raise "Expected element_types to be set but is not"
129
+ # :nocov:
130
+ else
131
+ # :nocov:
132
+ raise "Unsure how to handle path part #{path_part}"
133
+ # :nocov:
134
+ end
135
+ when Integer
136
+ if extends?(BuiltinTypes[:tuple])
137
+ element_types[path_part]
138
+ elsif extends?(BuiltinTypes[:array])
139
+ element_type
140
+ else
141
+ # :nocov:
142
+ raise "Unsure how to handle path part #{path_part}"
143
+ # :nocov:
144
+ end
145
+ else
146
+ # :nocov:
147
+ raise "Bad path part #{path_part}"
148
+ # :nocov:
149
+ end
150
+
151
+ unless next_type
152
+ # :nocov:
153
+ raise "Expected to find a type at #{path_part}"
154
+ # :nocov:
155
+ end
156
+
157
+ if path_parts.empty?
158
+ next_type
159
+ else
160
+ next_type.type_at_path(path_parts)
161
+ end
162
+ end
163
+
89
164
  def inspect
90
165
  # :nocov:
91
166
  name = if scoped_path_set?