foobara 0.0.142 → 0.1.2

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 (121) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +25 -0
  4. data/projects/builtin_types/lib/foobara/builtin_types.rb +1 -0
  5. data/projects/builtin_types/src/attributes/supported_transformers/defaults/type_declaration_extension/extend_attributes_type_declaration/desugarizers/move_defaults_from_element_types_to_root.rb +9 -2
  6. data/projects/builtin_types/src/attributes/supported_transformers/defaults/type_declaration_extension/extend_attributes_type_declaration/desugarizers/symbolize_defaults.rb +1 -1
  7. data/projects/builtin_types/src/attributes/supported_transformers/defaults/type_declaration_extension/extend_attributes_type_declaration/type_declaration_validators/hash_with_symbolic_keys.rb +1 -1
  8. data/projects/builtin_types/src/attributes/supported_validators/required/type_declaration_extension/extend_attributes_type_declaration/desugarizers/alphabetize_required.rb +5 -5
  9. data/projects/builtin_types/src/attributes/supported_validators/required/type_declaration_extension/extend_attributes_type_declaration/desugarizers/move_required_from_element_types_to_root.rb +12 -2
  10. data/projects/builtin_types/src/builtin_types.rb +1 -1
  11. data/projects/builtin_types/src/duck/supported_validators/instance_of/type_declaration_extension/extend_registered_type_declaration/desugarizers/class_desugarizer.rb +10 -2
  12. data/projects/builtin_types/src/duck/supported_validators/instance_of/type_declaration_extension/extend_registered_type_declaration/desugarizers/class_type_desugarizer.rb +7 -2
  13. data/projects/builtin_types/src/duck/supported_validators/instance_of/type_declaration_extension/extend_registered_type_declaration/desugarizers/instance_of_class_desugarizer.rb +3 -4
  14. data/projects/builtin_types/src/duck/supported_validators/instance_of/type_declaration_extension/extend_registered_type_declaration/desugarizers/instance_of_symbol_desugarizer.rb +3 -4
  15. data/projects/builtin_types/src/duck/supported_validators/instance_of/type_declaration_extension/extend_registered_type_declaration/type_declaration_validators/is_valid_class.rb +1 -1
  16. data/projects/builtin_types/src/duck/supported_validators/instance_of.rb +1 -0
  17. data/projects/builtin_types/src/duck/supported_validators/one_of/type_declaration_extension/extend_registered_type_declaration/desugarizers/cast_one_of.rb +4 -3
  18. data/projects/builtin_types/src/duck/supported_validators/one_of/type_declaration_extension/extend_registered_type_declaration/desugarizers/module_desugarizer.rb +3 -3
  19. data/projects/builtin_types/src/tuple/supported_processors/element_type_declarations/type_declaration_extension/extend_tuple_type_declaration/desugarizers/set_size.rb +1 -1
  20. data/projects/builtin_types/src/tuple/supported_processors/element_type_declarations/type_declaration_extension/extend_tuple_type_declaration/type_declaration_validators/size_matches.rb +2 -4
  21. data/projects/command/src/command_pattern_implementation/concerns/inputs_type.rb +12 -12
  22. data/projects/command/src/command_pattern_implementation/concerns/reflection.rb +4 -2
  23. data/projects/command/src/command_pattern_implementation/concerns/result_type.rb +0 -4
  24. data/projects/command_connectors/src/command_connector.rb +41 -16
  25. data/projects/command_connectors/src/command_registry.rb +7 -4
  26. data/projects/command_connectors/src/transformed_command.rb +55 -43
  27. data/projects/command_connectors/src/transformers/auth_errors_transformer.rb +1 -0
  28. data/projects/command_connectors/src/transformers/entity_to_primary_key_inputs_transformer.rb +4 -0
  29. data/projects/common/src/possible_error.rb +27 -21
  30. data/projects/detached_entity/src/concerns/types.rb +7 -1
  31. data/projects/detached_entity/src/detached_entity_type.rb +10 -0
  32. data/projects/detached_entity/src/extensions/builtin_types/detached_entity/validators/model_instance_is_valid.rb +1 -1
  33. data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/hash_desugarizer.rb +6 -9
  34. data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/primary_key_desugarizer.rb +2 -1
  35. data/projects/domain/src/domain_module_extension.rb +12 -4
  36. data/projects/domain/src/is_manifestable.rb +19 -7
  37. data/projects/entity/src/concerns/types.rb +6 -1
  38. data/projects/foobara/lib/foobara/all.rb +4 -4
  39. data/projects/manifest/src/foobara/manifest/type.rb +11 -2
  40. data/projects/manifest/src/foobara/manifest/type_declaration.rb +46 -2
  41. data/projects/model/src/concerns/aliases.rb +8 -0
  42. data/projects/model/src/concerns/reflection.rb +2 -2
  43. data/projects/model/src/concerns/types.rb +35 -20
  44. data/projects/model/src/extensions/builtin_types/model/validators/model_instance_is_valid.rb +12 -8
  45. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/array_with_symbolic_elements.rb +1 -1
  46. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/attributes_handler_desugarizer.rb +15 -1
  47. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/delegates_desugarizer.rb +7 -5
  48. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/hash_desugarizer.rb +4 -7
  49. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/model_class_desugarizer.rb +0 -2
  50. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/move_private_from_element_types_to_root.rb +28 -9
  51. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/symbolize_private.rb +8 -7
  52. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration/to_type_transformer.rb +4 -7
  53. data/projects/model/src/extensions/type_declarations/handlers/extend_model_type_declaration.rb +14 -2
  54. data/projects/model/src/extensions/type_declarations/handlers/extend_registered_model_type_declaration/hash_desugarizer.rb +8 -9
  55. data/projects/model/src/extensions/type_declarations/handlers/extend_registered_model_type_declaration/model_class_type_desugarizer.rb +6 -2
  56. data/projects/model/src/extensions/type_declarations/handlers/extend_registered_model_type_declaration/mutable_validator.rb +5 -2
  57. data/projects/model/src/extensions/type_declarations/handlers/extend_registered_model_type_declaration/normalize_mutable_attributes_desugarizer.rb +8 -7
  58. data/projects/model/src/extensions/type_declarations/handlers/extend_registered_model_type_declaration/to_type_transformer.rb +4 -1
  59. data/projects/model/src/extensions/type_declarations/handlers/extend_registered_model_type_declaration.rb +20 -5
  60. data/projects/model/src/extensions/type_declarations/handlers/registered_type_declaration/model_class_desugarizer.rb +23 -5
  61. data/projects/model/src/extensions/type_declarations/lazy_element_types/model.rb +18 -0
  62. data/projects/model_attribute_helpers/src/attribute_helpers.rb +44 -26
  63. data/projects/namespace/src/is_namespace.rb +58 -90
  64. data/projects/namespace/src/namespace/lookup_mode.rb +13 -2
  65. data/projects/namespace/src/namespace_helpers.rb +1 -0
  66. data/projects/namespace/src/prefixless_registry.rb +2 -0
  67. data/projects/persistence/src/entity_base/transaction/concerns/state_transitions.rb +20 -1
  68. data/projects/persistence/src/entity_base/transaction.rb +14 -11
  69. data/projects/persistence/src/entity_base/transaction_table.rb +10 -49
  70. data/projects/persistence/src/entity_base.rb +39 -0
  71. data/projects/persistence/src/persistence.rb +4 -0
  72. data/projects/type_declarations/src/attributes.rb +15 -9
  73. data/projects/type_declarations/src/attributes_transformers/from_yaml.rb +1 -1
  74. data/projects/type_declarations/src/desugarizer_pipeline.rb +9 -0
  75. data/projects/type_declarations/src/dsl/attributes.rb +14 -5
  76. data/projects/type_declarations/src/handlers/extend_array_type_declaration/array_desugarizer.rb +24 -4
  77. data/projects/type_declarations/src/handlers/extend_array_type_declaration/element_type_declaration_desugarizer.rb +23 -9
  78. data/projects/type_declarations/src/handlers/extend_array_type_declaration/to_type_transformer.rb +3 -7
  79. data/projects/type_declarations/src/handlers/extend_array_type_declaration/type_set_to_array_desugarizer.rb +15 -4
  80. data/projects/type_declarations/src/handlers/extend_array_type_declaration.rb +12 -2
  81. data/projects/type_declarations/src/handlers/extend_associative_array_type_declaration/key_type_declaration_desugarizer.rb +48 -0
  82. data/projects/type_declarations/src/handlers/extend_associative_array_type_declaration/to_type_transformer.rb +3 -11
  83. data/projects/type_declarations/src/handlers/extend_associative_array_type_declaration/value_type_declaration_desugarizer.rb +51 -0
  84. data/projects/type_declarations/src/handlers/extend_associative_array_type_declaration.rb +12 -2
  85. data/projects/type_declarations/src/handlers/extend_attributes_type_declaration/dsl_desugarizer.rb +1 -1
  86. data/projects/type_declarations/src/handlers/extend_attributes_type_declaration/element_type_declarations_desugarizer.rb +17 -7
  87. data/projects/type_declarations/src/handlers/extend_attributes_type_declaration/hash_desugarizer.rb +16 -19
  88. data/projects/type_declarations/src/handlers/extend_attributes_type_declaration/to_type_transformer.rb +3 -6
  89. data/projects/type_declarations/src/handlers/extend_attributes_type_declaration.rb +13 -2
  90. data/projects/type_declarations/src/handlers/extend_registered_type_declaration/to_type_transformer.rb +3 -3
  91. data/projects/type_declarations/src/handlers/extend_registered_type_declaration.rb +16 -4
  92. data/projects/type_declarations/src/handlers/extend_tuple_type_declaration/array_desugarizer.rb +20 -6
  93. data/projects/type_declarations/src/handlers/extend_tuple_type_declaration/to_type_transformer.rb +3 -9
  94. data/projects/type_declarations/src/handlers/extend_tuple_type_declaration.rb +12 -2
  95. data/projects/type_declarations/src/handlers/registered_type_declaration/to_type_transformer.rb +7 -9
  96. data/projects/type_declarations/src/handlers/registered_type_declaration.rb +22 -7
  97. data/projects/type_declarations/src/lazy_element_types/array.rb +19 -0
  98. data/projects/type_declarations/src/lazy_element_types/attributes.rb +27 -0
  99. data/projects/type_declarations/src/lazy_element_types/hash.rb +38 -0
  100. data/projects/type_declarations/src/lazy_element_types/tuple.rb +23 -0
  101. data/projects/type_declarations/src/sensitive_type_remover.rb +6 -1
  102. data/projects/type_declarations/src/sensitive_type_removers/attributes.rb +2 -0
  103. data/projects/type_declarations/src/type_builder.rb +46 -21
  104. data/projects/type_declarations/src/type_declaration.rb +295 -0
  105. data/projects/type_declarations/src/type_declaration_handler.rb +10 -11
  106. data/projects/type_declarations/src/type_declarations.rb +18 -88
  107. data/projects/types/src/type.rb +34 -9
  108. data/projects/value/src/mutator.rb +1 -4
  109. data/projects/value/src/processor/casting.rb +7 -2
  110. data/projects/value/src/processor/pipeline.rb +9 -1
  111. data/projects/value/src/processor.rb +0 -8
  112. data/projects/value/src/transformer.rb +1 -3
  113. data/projects/value/src/validator.rb +0 -2
  114. data/projects/weak_object_set/src/weak_object_set.rb +2 -0
  115. metadata +11 -8
  116. data/projects/type_declarations/src/handlers/registered_type_declaration/desugarizer_metadata_cleanup_desugarizer.rb +0 -29
  117. data/projects/type_declarations/src/handlers/registered_type_declaration/short_type_name_desugarizer.rb +0 -62
  118. data/projects/type_declarations/src/handlers/registered_type_declaration/strict_desugarizer.rb +0 -32
  119. data/projects/type_declarations/src/handlers/registered_type_declaration/strict_stringified_desugarizer.rb +0 -39
  120. data/projects/type_declarations/src/handlers/registered_type_declaration/symbol_desugarizer.rb +0 -30
  121. data/projects/type_declarations/src/handlers/registered_type_declaration/type_desugarizer.rb +0 -24
@@ -54,7 +54,7 @@ module Foobara
54
54
  end
55
55
 
56
56
  unless include_delegates
57
- declaration = Foobara::TypeDeclarations::Attributes.reject(declaration, *delegates.keys)
57
+ declaration = Foobara::TypeDeclarations::Attributes.reject(declaration, *foobara_delegates.keys)
58
58
  end
59
59
 
60
60
  Domain.current.foobara_type_from_declaration(declaration)
@@ -80,9 +80,11 @@ module Foobara
80
80
  if type.extends?(BuiltinTypes[:entity])
81
81
  target_class = type.target_class
82
82
 
83
- entry = foobara_type_declaration_value_at(declaration, DataPath.new(data_path).path)
84
- entry.clear
85
- entry.merge!(target_class.foobara_attributes_for_aggregate_update(initial: false))
83
+ set_foobara_type_declaration_value_at(
84
+ declaration,
85
+ DataPath.new(data_path).path,
86
+ target_class.foobara_attributes_for_aggregate_update(initial: false)
87
+ )
86
88
  end
87
89
  end
88
90
 
@@ -111,9 +113,12 @@ module Foobara
111
113
  # TODO: do we really need declaration_data? Why cant we use the type directly?
112
114
  # TODO: make this work with the type directly for performance reasons.
113
115
  primary_key_type_declaration = target_class.foobara_primary_key_type.declaration_data
114
- entry = foobara_type_declaration_value_at(declaration, DataPath.new(data_path).path)
115
- entry.clear
116
- entry.merge!(primary_key_type_declaration)
116
+
117
+ set_foobara_type_declaration_value_at(
118
+ declaration,
119
+ DataPath.new(data_path).path,
120
+ primary_key_type_declaration
121
+ )
117
122
  end
118
123
  end
119
124
 
@@ -131,31 +136,44 @@ module Foobara
131
136
  TypeDeclarations::Handlers::ExtendAttributesTypeDeclaration
132
137
  )
133
138
 
134
- handler.desugarize(
135
- type: "::attributes",
136
- element_type_declarations:
137
- )
139
+ declaration = TypeDeclaration.new(type: :attributes, element_type_declarations:)
140
+ declaration.is_absolutified = true
141
+ declaration.is_duped = true
142
+
143
+ handler.desugarize(declaration).declaration_data
138
144
  end
139
145
 
140
146
  private
141
147
 
142
- def foobara_type_declaration_value_at(declaration, path_parts)
143
- return declaration if path_parts.empty?
144
-
148
+ def set_foobara_type_declaration_value_at(declaration, path_parts, value)
145
149
  path_part, *path_parts = path_parts
146
150
 
147
- declaration = case path_part
148
- when :"#"
149
- declaration[:element_type_declaration]
150
- when Symbol, Integer
151
- declaration[:element_type_declarations][path_part]
152
- else
153
- # :nocov:
154
- raise "Bad path part #{path_part}"
155
- # :nocov:
156
- end
157
-
158
- foobara_type_declaration_value_at(declaration, path_parts)
151
+ case path_part
152
+ when :"#"
153
+ if path_parts.empty?
154
+ declaration[:element_type_declaration] = value
155
+ else
156
+ set_foobara_type_declaration_value_at(
157
+ declaration[:element_type_declaration],
158
+ path_parts,
159
+ value
160
+ )
161
+ end
162
+ when Symbol, Integer
163
+ if path_parts.empty?
164
+ declaration[:element_type_declarations][path_part] = value
165
+ else
166
+ set_foobara_type_declaration_value_at(
167
+ declaration[:element_type_declarations][path_part],
168
+ path_parts,
169
+ value
170
+ )
171
+ end
172
+ else
173
+ # :nocov:
174
+ raise "Bad path part #{path_part}"
175
+ # :nocov:
176
+ end
159
177
  end
160
178
  end
161
179
  end
@@ -98,9 +98,12 @@ module Foobara
98
98
  scopedish
99
99
  end
100
100
 
101
+ # TODO: make this thread-safe
101
102
  def foobara_register(scoped)
102
103
  foobara_registry.register(scoped)
103
104
 
105
+ # TODO: do we really need to clear the whole cache? Why not just the possible
106
+ # impacted keys based on scoped.scoped_path ?
104
107
  IsNamespace.clear_lru_cache!
105
108
  # awkward??
106
109
  scoped.scoped_namespace = self
@@ -134,48 +137,29 @@ module Foobara
134
137
  IsNamespace.lru_cache
135
138
  end
136
139
 
137
- def foobara_lookup(
138
- path,
139
- filter: nil,
140
- mode: LookupMode::GENERAL,
141
- visited: nil,
142
- initial: true
143
- )
144
- # TODO: make it so this is only necessary when initial
140
+ def foobara_lookup(path, filter: nil, mode: LookupMode::GENERAL)
141
+ LookupMode.validate!(mode)
145
142
  path = Namespace.to_registry_path(path)
146
- visited_key = [path, mode, initial, self]
147
143
 
148
- if initial
149
- lru_key = [path, mode, self, filter]
150
- found, value = lru_cache.get(lru_key)
151
- return value if found
144
+ lru_cache.cached([path, mode, self, filter]) do
145
+ visited = Set.new
146
+ foobara_lookup_without_cache(path, filter:, mode:, visited:)
152
147
  end
148
+ end
153
149
 
154
- return nil if visited&.include?(visited_key)
150
+ def foobara_lookup_without_cache(path, filter:, mode:, visited:)
151
+ visited_key = [path, mode, self]
152
+ return nil if visited.include?(visited_key)
155
153
 
156
- visited ||= Set.new
157
154
  visited << visited_key
158
155
 
159
- LookupMode.validate!(mode)
160
-
161
156
  if mode == LookupMode::RELAXED
162
- scoped = foobara_lookup(
163
- path,
164
- filter:,
165
- mode: LookupMode::GENERAL,
166
- visited:,
167
- initial: false
168
- )
169
- if scoped
170
- if initial
171
- lru_cache.set_if_missing(lru_key, scoped)
172
- end
157
+ scoped = foobara_lookup_without_cache(path, filter:, mode: LookupMode::GENERAL, visited:)
173
158
 
174
- return scoped
175
- end
159
+ return scoped if scoped
176
160
 
177
161
  candidates = foobara_children.map do |namespace|
178
- namespace.foobara_lookup(path, filter:, mode:, visited:, initial: false)
162
+ namespace.foobara_lookup_without_cache(path, filter:, mode:, visited:)
179
163
  end.compact
180
164
 
181
165
  if candidates.size > 1
@@ -186,11 +170,7 @@ module Foobara
186
170
  end
187
171
 
188
172
  scoped = candidates.first ||
189
- foobara_parent_namespace&.foobara_lookup(path, filter:, mode:, visited:, initial: false)
190
-
191
- if initial
192
- lru_cache.set_if_missing(lru_key, scoped)
193
- end
173
+ foobara_parent_namespace&.foobara_lookup_without_cache(path, filter:, mode:, visited:)
194
174
 
195
175
  return scoped
196
176
  end
@@ -204,25 +184,41 @@ module Foobara
204
184
  path = path[(foobara_root_namespace.scoped_path.size + 1)..]
205
185
  end
206
186
 
207
- return foobara_root_namespace.foobara_lookup(path, filter:, mode: LookupMode::ABSOLUTE, initial: true)
187
+ return foobara_lookup_without_cache(path, filter:, mode: LookupMode::ABSOLUTE, visited:)
188
+ end
189
+
190
+ root = foobara_root_namespace
191
+
192
+ if mode == LookupMode::ABSOLUTE
193
+ [
194
+ root,
195
+ *root.foobara_depends_on_namespaces.map(&:foobara_root_namespace)
196
+ ].uniq.each do |namespace|
197
+ scoped = namespace.foobara_lookup_without_cache(path,
198
+ filter:,
199
+ mode: LookupMode::CHILDREN_ONLY,
200
+ visited:)
201
+ return scoped if scoped
202
+ end
203
+
204
+ return nil
205
+ end
206
+
207
+ if mode == LookupMode::ABSOLUTE_SINGLE_NAMESPACE
208
+ return foobara_root_namespace.foobara_lookup_without_cache(path,
209
+ filter:,
210
+ mode: LookupMode::CHILDREN_ONLY,
211
+ visited:)
208
212
  end
209
213
 
210
214
  partial = foobara_registry.lookup(path, filter)
211
215
 
212
216
  if mode == LookupMode::DIRECT
213
- if initial
214
- lru_cache.set_if_missing(lru_key, partial)
215
- end
216
-
217
217
  return partial
218
218
  end
219
219
 
220
220
  if partial
221
221
  if partial.scoped_path == path
222
- if initial
223
- lru_cache.set_if_missing(lru_key, partial)
224
- end
225
-
226
222
  return partial
227
223
  end
228
224
  end
@@ -236,23 +232,15 @@ module Foobara
236
232
  scoped = _lookup_in(path, to_consider, filter:, visited:)
237
233
 
238
234
  if scoped
239
- if initial
240
- lru_cache.set_if_missing(lru_key, scoped)
241
- end
242
-
243
235
  return scoped
244
236
  end
245
237
 
246
- if [LookupMode::GENERAL, LookupMode::STRICT].include?(mode) && foobara_parent_namespace
247
- scoped = foobara_parent_namespace.foobara_lookup(
248
- path, filter:, mode: LookupMode::STRICT, visited:, initial: false
238
+ if (mode == LookupMode::GENERAL || mode == LookupMode::STRICT) && foobara_parent_namespace
239
+ scoped = foobara_parent_namespace.foobara_lookup_without_cache(
240
+ path, filter:, mode: LookupMode::STRICT, visited:
249
241
  )
250
242
 
251
243
  if scoped
252
- if initial
253
- lru_cache.set_if_missing(lru_key, scoped)
254
- end
255
-
256
244
  return scoped
257
245
  end
258
246
  end
@@ -261,10 +249,6 @@ module Foobara
261
249
  scoped = _lookup_in(path, foobara_depends_on_namespaces, filter:, visited:)
262
250
 
263
251
  if scoped
264
- if initial
265
- lru_cache.set_if_missing(lru_key, scoped)
266
- end
267
-
268
252
  return scoped
269
253
  end
270
254
  end
@@ -277,7 +261,7 @@ module Foobara
277
261
  end
278
262
 
279
263
  candidates = to_consider.map do |namespace|
280
- namespace.foobara_lookup(path, filter:, mode:, visited:, initial: false)
264
+ namespace.foobara_lookup_without_cache(path, filter:, mode:, visited:)
281
265
  end.compact
282
266
 
283
267
  if candidates.size > 1
@@ -286,27 +270,7 @@ module Foobara
286
270
  # :nocov:
287
271
  end
288
272
 
289
- scoped = candidates.first || partial
290
-
291
- if scoped
292
- if initial
293
- lru_cache.set_if_missing(lru_key, scoped)
294
- end
295
-
296
- return scoped
297
- end
298
-
299
- # As a last resort we'll see if we're trying to fetch a builtin type from a different namespace
300
- # TODO: these lookup modes are really confusing and were designed prior to having multiple root
301
- # namespaces playing a role in command connectors.
302
- if initial
303
- scoped = Namespace.global.foobara_lookup(path, filter:, mode: LookupMode::ABSOLUTE, visited:, initial: false)
304
-
305
- if scoped.is_a?(Types::Type) && scoped.builtin?
306
- lru_cache.set_if_missing(lru_key, scoped)
307
- scoped
308
- end
309
- end
273
+ candidates.first || partial
310
274
  end
311
275
 
312
276
  def foobara_parent_namespace
@@ -328,9 +292,10 @@ module Foobara
328
292
  def foobara_each(filter: nil, mode: Namespace::LookupMode::GENERAL, &)
329
293
  foobara_registry.each_scoped(filter:, &)
330
294
 
331
- return if mode == Namespace::LookupMode::DIRECT
332
-
333
- if [Namespace::LookupMode::GENERAL, Namespace::LookupMode::ABSOLUTE].include?(mode)
295
+ if mode == LookupMode::GENERAL ||
296
+ mode == LookupMode::CHILDREN_ONLY ||
297
+ mode == LookupMode::ABSOLUTE ||
298
+ mode == LookupMode::ABSOLUTE_SINGLE_NAMESPACE
334
299
  foobara_children.each do |child|
335
300
  child.foobara_each(filter:, mode:, &)
336
301
  end
@@ -414,6 +379,10 @@ module Foobara
414
379
  Foobara::Namespace::UnambiguousRegistry
415
380
  when Foobara::Namespace::BaseRegistry::WouldMakeRegistryAmbiguousError
416
381
  Foobara::Namespace::AmbiguousRegistry
382
+ else
383
+ # :nocov:
384
+ raise ArgumentError, "Not sure how to upgrade a #{error.class}"
385
+ # :nocov:
417
386
  end
418
387
 
419
388
  old_registry = foobara_registry
@@ -442,19 +411,18 @@ module Foobara
442
411
  end
443
412
 
444
413
  matching_children.sort_by(&:first).reverse.each do |(matching_child_score, matching_child)|
445
- scoped = matching_child.foobara_lookup(
414
+ scoped = matching_child.foobara_lookup_without_cache(
446
415
  path[matching_child_score..],
447
- mode: LookupMode::ABSOLUTE,
416
+ mode: LookupMode::CHILDREN_ONLY,
448
417
  filter:,
449
- visited:,
450
- initial: false
418
+ visited:
451
419
  )
452
420
 
453
421
  return scoped if scoped
454
422
  end
455
423
 
456
424
  last_resort.uniq.each do |namespace|
457
- scoped = namespace.foobara_lookup(path, filter:, mode: LookupMode::ABSOLUTE, visited:, initial: false)
425
+ scoped = namespace.foobara_lookup_without_cache(path, filter:, mode: LookupMode::CHILDREN_ONLY, visited:)
458
426
  return scoped if scoped
459
427
  end
460
428
 
@@ -1,6 +1,6 @@
1
1
  module Foobara
2
2
  class Namespace
3
- # TODO: need to define these better...
3
+ # TODO: need to define these better/more intuitively
4
4
  # use-cases
5
5
  # 1. general: just general lookup. find it wherever the heck it might be
6
6
  # children: y
@@ -17,6 +17,14 @@ module Foobara
17
17
  # 4. absolute
18
18
  # children: y
19
19
  # parent: n
20
+ # dependent: y
21
+ # 5. absolute_single_namespace
22
+ # children: y
23
+ # parent: n
24
+ # dependent: n
25
+ # 6. children_only (internal use... absolute and absolute_single_namespace jump to top, children only does not)
26
+ # children: y
27
+ # parent: n
20
28
  # dependent: n
21
29
  # TODO: don't we have an enumerated class/project for this?
22
30
  module LookupMode
@@ -25,7 +33,10 @@ module Foobara
25
33
  DIRECT = :direct
26
34
  STRICT = :strict
27
35
  ABSOLUTE = :absolute
28
- ALL = [GENERAL, RELAXED, DIRECT, STRICT, ABSOLUTE].freeze
36
+ ABSOLUTE_SINGLE_NAMESPACE = :absolute_single_namespace
37
+ CHILDREN_ONLY = :children_only
38
+
39
+ ALL = [GENERAL, RELAXED, DIRECT, STRICT, ABSOLUTE, ABSOLUTE_SINGLE_NAMESPACE, CHILDREN_ONLY].freeze
29
40
 
30
41
  class << self
31
42
  def validate!(mode)
@@ -243,6 +243,7 @@ module Foobara
243
243
  # I guess we could just iterate over all objects and patch up any with matching prefixes?
244
244
  # is this expensive to have to look at all of them or no? I guess at least it's a one-time thing.
245
245
  # TODO: somehow look things up by prefixes since we know mod's path
246
+ # TODO: can't there be multiple namespace roots??
246
247
  Foobara.foobara_root_namespace.foobara_each do |scoped|
247
248
  parent = scoped.scoped_namespace
248
249
  next if parent == mod
@@ -41,6 +41,8 @@ module Foobara
41
41
  registry.each_value(&)
42
42
  end
43
43
 
44
+ private
45
+
44
46
  def to_key(scoped)
45
47
  if scoped.scoped_prefix
46
48
  raise RegisteringScopedWithPrefixError,
@@ -80,7 +80,26 @@ module Foobara
80
80
 
81
81
  # TODO: this belongs elsewhere
82
82
  def each_table(&)
83
- tables.values.each(&)
83
+ @ordered_tables ||= if tables.size <= 1
84
+ tables.values
85
+ else
86
+ entity_class_to_table = {}
87
+ entity_classes = []
88
+
89
+ tables.each_value do |table|
90
+ entity_class = table.entity_class
91
+ entity_classes << entity_class
92
+ entity_class_to_table[entity_class] = table
93
+ end
94
+
95
+ ordered_entity_classes = EntityBase.order_entity_classes(entity_classes)
96
+
97
+ ordered_entity_classes.map do |entity_class|
98
+ entity_class_to_table[entity_class]
99
+ end
100
+ end
101
+
102
+ @ordered_tables.each(&)
84
103
  end
85
104
 
86
105
  def rollback!(because_of = nil)
@@ -62,7 +62,20 @@ module Foobara
62
62
  # :nocov:
63
63
  end
64
64
 
65
- tables[entity_class] ||= TransactionTable.new(self, entity_class)
65
+ table = tables[entity_class]
66
+
67
+ if table
68
+ table
69
+ else
70
+ if defined?(@ordered_tables)
71
+ # TODO: test this path
72
+ # :nocov:
73
+ remove_instance_variable(:@ordered_tables)
74
+ # :nocov:
75
+ end
76
+
77
+ tables[entity_class] = TransactionTable.new(self, entity_class)
78
+ end
66
79
  end
67
80
 
68
81
  def updated(record)
@@ -149,16 +162,6 @@ module Foobara
149
162
  table_for(entity).track_created(entity)
150
163
  end
151
164
 
152
- def created?(record)
153
- table_for(record).created?(record)
154
- end
155
-
156
- # WARNING! this seems to bypass validations, hmmm....
157
- def flush_created_record!(record)
158
- table_for(record).flush_created_record!(record)
159
- end
160
-
161
- # convenience method...
162
165
  def perform(&)
163
166
  entity_base.using_transaction(self, &)
164
167
  end
@@ -642,11 +642,12 @@ module Foobara
642
642
 
643
643
  def flush_created!
644
644
  marked_created.each do |record|
645
- flush_created_associations!(record)
646
-
647
645
  # TODO: do this in bulk
648
646
  attributes = entity_attributes_crud_driver_table.insert(to_persistable(record))
649
- record.write_attributes_without_callbacks(attributes)
647
+ primary_key_attribute = entity_class.primary_key_attribute
648
+ primary_key = attributes[primary_key_attribute]
649
+
650
+ record.write_attributes_without_callbacks(primary_key_attribute => primary_key)
650
651
 
651
652
  # we need to update finding the tracked object by key and removing/reading it seems to be the simplest
652
653
  # way to accomplish that at the moment
@@ -660,48 +661,10 @@ module Foobara
660
661
  marked_created.clear
661
662
  end
662
663
 
663
- def flush_created_record!(record)
664
- flush_created_associations!(record)
665
-
666
- unmark_created(record)
667
-
668
- attributes = entity_attributes_crud_driver_table.insert(to_persistable(record))
669
- record.write_attributes_without_callbacks(attributes)
670
-
671
- # we need to update finding the tracked object by key and removing/reading it seems to be the simplest
672
- # way to accomplish that at the moment
673
- tracked(record)
674
-
675
- record.is_persisted = record.is_loaded = true
676
- record.is_created = false
677
- record.save_persisted_attributes
678
- end
679
-
680
- def flush_created_associations!(record)
681
- entity_class.associations.each_key do |association_data_path|
682
- DataPath.values_at(association_data_path, record).each do |associated_record|
683
- next unless associated_record.created?
684
-
685
- transaction = Persistence::EntityBase::Transaction.open_transaction_for(associated_record)
686
-
687
- unless transaction
688
- # :nocov:
689
- raise "No open transaction for #{associated_record}"
690
- # :nocov:
691
- end
692
-
693
- if transaction.created?(associated_record)
694
- transaction.flush_created_record!(associated_record)
695
- end
696
- end
697
- end
698
- end
699
-
700
664
  def flush_updated_and_hard_deleted!
701
665
  # TODO: use bulk operations to improve performance...
702
666
  marked_updated.each do |record|
703
- attributes = entity_attributes_crud_driver_table.update(to_persistable(record))
704
- record.write_attributes_without_callbacks(attributes)
667
+ entity_attributes_crud_driver_table.update(to_persistable(record))
705
668
  record.save_persisted_attributes
706
669
  end
707
670
 
@@ -753,16 +716,14 @@ module Foobara
753
716
 
754
717
  def normalize_attributes(attributes)
755
718
  if attributes
756
- begin
757
- attributes = attributes.transform_keys(&:to_sym)
719
+ attributes = attributes.transform_keys(&:to_sym)
758
720
 
759
- primary_key_name = entity_class.primary_key_attribute
760
- primary_key_value = attributes[primary_key_name]
721
+ primary_key_name = entity_class.primary_key_attribute
722
+ primary_key_value = attributes[primary_key_name]
761
723
 
762
- attributes[primary_key_name] = entity_class.primary_key_type.cast!(primary_key_value)
724
+ attributes[primary_key_name] = entity_class.primary_key_type.cast!(primary_key_value)
763
725
 
764
- attributes
765
- end
726
+ attributes
766
727
  end
767
728
  end
768
729
  end
@@ -21,6 +21,45 @@ module Foobara
21
21
  end.call
22
22
  end
23
23
  end
24
+
25
+ def order_entity_classes(entity_classes)
26
+ return entity_classes if entity_classes.size <= 1
27
+
28
+ without_associations, with_associations = entity_classes.partition do |entity_class|
29
+ entity_class.associations.empty?
30
+ end
31
+
32
+ if with_associations.size > 1
33
+ i = 0
34
+ end_at = with_associations.size - 1
35
+
36
+ while i < end_at
37
+ entity_class = with_associations[i]
38
+ associations = entity_class.associations.values.uniq
39
+
40
+ changed = false
41
+
42
+ j = i + 1
43
+
44
+ while j <= end_at
45
+ other = with_associations[j]
46
+
47
+ if associations.include?(other.foobara_type)
48
+ with_associations[j] = entity_class
49
+ with_associations[i] = other
50
+ changed = true
51
+ break
52
+ end
53
+
54
+ j += 1
55
+ end
56
+
57
+ i += 1 unless changed
58
+ end
59
+ end
60
+
61
+ without_associations + with_associations
62
+ end
24
63
  end
25
64
 
26
65
  def initialize(name, entity_attributes_crud_driver:)
@@ -99,6 +99,10 @@ module Foobara
99
99
  end
100
100
 
101
101
  def objects_to_bases(objects)
102
+ if objects.size > 1 && objects.all? { |o| o.is_a?(::Class) && o < Entity }
103
+ objects = EntityBase.order_entity_classes(objects)
104
+ end
105
+
102
106
  objects.map do |object|
103
107
  object_to_base(object)
104
108
  end.uniq