foobara 0.0.29 → 0.0.31

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -1
  3. data/projects/command_connectors/src/serializers/entities_to_primary_keys_serializer.rb +2 -0
  4. data/projects/detached_entity/lib/foobara/detached_entity.rb +19 -0
  5. data/projects/{entity → detached_entity}/src/concerns/associations.rb +4 -59
  6. data/projects/detached_entity/src/concerns/equality.rb +24 -0
  7. data/projects/{entity → detached_entity}/src/concerns/primary_key.rb +2 -2
  8. data/projects/{entity → detached_entity}/src/concerns/reflection.rb +15 -1
  9. data/projects/detached_entity/src/concerns/types.rb +41 -0
  10. data/projects/detached_entity/src/detached_entity.rb +15 -0
  11. data/projects/detached_entity/src/extensions/builtin_types/detached_entity/casters/hash.rb +39 -0
  12. data/projects/detached_entity/src/extensions/builtin_types/detached_entity/validators/attributes_declaration.rb +36 -0
  13. data/projects/detached_entity/src/extensions/builtin_types/detached_entity.rb +6 -0
  14. data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/attributes_handler_desugarizer.rb +13 -0
  15. data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/hash_desugarizer.rb +43 -0
  16. data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/model_class_desugarizer.rb +21 -0
  17. data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/primary_key_desugarizer.rb +19 -0
  18. data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/to_type_transformer.rb +26 -0
  19. data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/validate_primary_key_is_symbol.rb +35 -0
  20. data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/validate_primary_key_present.rb +27 -0
  21. data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/validate_primary_key_references_attribute.rb +36 -0
  22. data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration.rb +15 -0
  23. data/projects/entity/lib/foobara/entity.rb +3 -7
  24. data/projects/entity/src/concerns/attribute_helpers.rb +1 -58
  25. data/projects/entity/src/concerns/attributes.rb +1 -1
  26. data/projects/entity/src/concerns/callbacks.rb +1 -1
  27. data/projects/entity/src/concerns/initialization.rb +1 -1
  28. data/projects/entity/src/concerns/mutations.rb +68 -0
  29. data/projects/entity/src/concerns/persistence.rb +1 -1
  30. data/projects/entity/src/concerns/queries.rb +56 -1
  31. data/projects/entity/src/concerns/transactions.rb +1 -1
  32. data/projects/entity/src/concerns/types.rb +2 -18
  33. data/projects/entity/src/entity.rb +3 -48
  34. data/projects/entity/src/extensions/builtin_types/entity/casters/hash.rb +8 -20
  35. data/projects/entity/src/extensions/builtin_types/entity/validators/attributes_declaration.rb +3 -18
  36. data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/attributes_handler_desugarizer.rb +2 -3
  37. data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/hash_desugarizer.rb +2 -32
  38. data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/model_class_desugarizer.rb +2 -6
  39. data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/primary_key_desugarizer.rb +2 -11
  40. data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/to_type_transformer.rb +2 -11
  41. data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/validate_primary_key_is_symbol.rb +2 -27
  42. data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/validate_primary_key_present.rb +2 -19
  43. data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/validate_primary_key_references_attribute.rb +3 -28
  44. data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration.rb +5 -1
  45. data/projects/entity/src/new_prepend.rb +1 -1
  46. data/projects/entity/src/not_found_error.rb +1 -1
  47. data/projects/foobara/lib/foobara/all.rb +1 -0
  48. data/projects/manifest/src/foobara/manifest/detached_entity.rb +30 -0
  49. data/projects/manifest/src/foobara/manifest/domain.rb +4 -0
  50. data/projects/manifest/src/foobara/manifest/entity.rb +3 -15
  51. data/projects/manifest/src/foobara/manifest/root_manifest.rb +8 -0
  52. data/projects/manifest/src/foobara/manifest/type.rb +17 -1
  53. data/projects/manifest/src/foobara/manifest/type_declaration.rb +17 -0
  54. data/projects/model/src/model.rb +3 -1
  55. data/projects/namespace/src/is_namespace.rb +6 -2
  56. data/projects/types/src/type.rb +0 -6
  57. metadata +24 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8722f293cfd3487965f3863448b8cf175481df41f364fe9c8a880c19430cbd86
4
- data.tar.gz: 14aafe8e69546446ec65694d6f48ea14b44af2e94bbd2a05a0cd71209831bfd8
3
+ metadata.gz: 8d39eaab1139573fba75d906c51de6091e81d77a1af812f60262bca2ff2064ab
4
+ data.tar.gz: da9a2b06e850ac495edc2f06a79969b75896b2c28134c91e724bca4c13397182
5
5
  SHA512:
6
- metadata.gz: 024d6d200f25cef54f5aa8a719af536d6f5e8fbf767fa6e9aa30ac04ba6f6c2f30815964cf1e0a079944e7a588c4afb91aaadbabe6c47ca9e001f0f3b01e8649
7
- data.tar.gz: a31f5904edef9faf390075de61229dd0890e0251a1b9e5e5d870a900f7ef1744d0cf4dbedf2812e9f9559773cc8f9dc5cbec89518a237adbfc82b4dace2d5642
6
+ metadata.gz: ce7d43c817996c3301bdbc332875d7bd73dad8f29c64f12c7c070d89cc95a88974cd05bb84aa9f94bb7af2d9181f16e3a4cace92f04d8233486c12b8a25876ca
7
+ data.tar.gz: 6b6f82a0620e369c2a071f0e3ff33b648243abba31b3e0dc8c59c824a4f10d771add1f7bfbd6f802b4d7ce6a7536fc1f87f77ec9bba052f920229593e10418e2
data/CHANGELOG.md CHANGED
@@ -1,8 +1,13 @@
1
- ## [0.0.29] - 2024-12-07
1
+ ## [0.0.31] - 2024-12-09
2
+
3
+ - Introduce a DetachedEntity concept that sits between Model and Entity
4
+
5
+ ## [0.0.30] - 2024-12-07
2
6
 
3
7
  - Fix problems with extending models/entities
4
8
  - Add a mutable class helper for models and improve mutable
5
9
  use a bit
10
+ - Fix bug when passing an unregistered Type instance to .foobara_registered?
6
11
 
7
12
  ## [0.0.28] - 2024-12-05
8
13
 
@@ -6,6 +6,8 @@ module Foobara
6
6
  class EntitiesToPrimaryKeysSerializer < SuccessSerializer
7
7
  def serialize(object)
8
8
  case object
9
+ # What of DetachedEntity? I guess we should treat it as a model since we can't cast from primary key value to
10
+ # a record?
9
11
  when Entity
10
12
  # TODO: handle polymorphism? Would require iterating over the result type not the object!
11
13
  # Is there maybe prior art for this in the associations stuff?
@@ -0,0 +1,19 @@
1
+ module Foobara
2
+ class DetachedEntity < Model
3
+ abstract
4
+
5
+ class << self
6
+ # Need to override this otherwise we install Model twice
7
+ def install!
8
+ TypeDeclarations.register_type_declaration(TypeDeclarations::Handlers::ExtendDetachedEntityTypeDeclaration.new)
9
+
10
+ model = Namespace.global.foobara_lookup_type!(:model)
11
+ BuiltinTypes.build_and_register!(:detached_entity, model, nil)
12
+ end
13
+
14
+ def reset_all
15
+ install!
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Entity < Model
2
+ class DetachedEntity < Model
3
3
  module Concerns
4
4
  module Associations
5
5
  include Concern
@@ -96,7 +96,7 @@ module Foobara
96
96
  key.include?(filter)
97
97
  end
98
98
  end
99
- elsif filter.is_a?(::Class) && filter < Entity
99
+ elsif filter.is_a?(::Class) && filter < DetachedEntity
100
100
  association_keys.select do |key|
101
101
  type = deep_associations[key]
102
102
  entity_class = type.target_class
@@ -114,7 +114,7 @@ module Foobara
114
114
  path = DataPath.new,
115
115
  result = {}
116
116
  )
117
- if type.extends?(BuiltinTypes[:entity])
117
+ if type.extends?(BuiltinTypes[:detached_entity])
118
118
  result[path.to_s] = type
119
119
  elsif type.extends?(BuiltinTypes[:tuple])
120
120
  element_types = type.element_types
@@ -150,7 +150,7 @@ module Foobara
150
150
  end
151
151
 
152
152
  def contains_associations?(type = entity_type, initial = true)
153
- if type.extends?(BuiltinTypes[:entity])
153
+ if type.extends?(BuiltinTypes[:detached_entity])
154
154
  if initial
155
155
  contains_associations?(type.element_types, false)
156
156
  else
@@ -179,61 +179,6 @@ module Foobara
179
179
  end
180
180
  end
181
181
  end
182
-
183
- def that_owns(record, filters = [])
184
- containing_records = that_own(record, filters)
185
-
186
- unless containing_records.empty?
187
- if containing_records.size == 1
188
- containing_records.first
189
- else
190
- # :nocov:
191
- raise "Expected only one record to own #{record} but found #{containing_records.size}"
192
- # :nocov:
193
- end
194
- end
195
- end
196
-
197
- def that_own(record, filters = [])
198
- association_key = association_for([record.class, *filters])
199
-
200
- data_path = DataPath.new(association_key)
201
-
202
- done = false
203
-
204
- containing_records = Util.array(record)
205
-
206
- until done
207
- last = data_path.path.last
208
-
209
- if last == :"#"
210
- method = :find_all_by_attribute_containing_any_of
211
- attribute_name = data_path.path[-2]
212
- data_path = DataPath.new(data_path.path[0..-3])
213
- else
214
- method = :find_all_by_attribute_any_of
215
- attribute_name = last
216
- data_path = DataPath.new(data_path.path[0..-2])
217
- end
218
-
219
- containing_entity_class_path = data_path.to_s
220
-
221
- entity_class = if containing_entity_class_path.empty?
222
- done = true
223
- self
224
- else
225
- deep_associations[
226
- containing_entity_class_path
227
- ].target_class
228
- end
229
-
230
- containing_records = entity_class.send(method, attribute_name, containing_records).to_a
231
-
232
- done = true unless containing_records
233
- end
234
-
235
- containing_records
236
- end
237
182
  end
238
183
  end
239
184
  end
@@ -0,0 +1,24 @@
1
+ module Foobara
2
+ class DetachedEntity
3
+ module Concerns
4
+ module Equality
5
+ include Concern
6
+
7
+ def dup
8
+ # TODO: Maybe raise instead?
9
+ self
10
+ end
11
+
12
+ def ==(other)
13
+ # Should both records be required to be persisted to be considered equal when having matching primary keys?
14
+ # For now we will consider them equal but it could make sense to consider them not equal.
15
+ equal?(other) || (self.class == other.class && primary_key && primary_key == other.primary_key)
16
+ end
17
+
18
+ def hash
19
+ (primary_key || object_id).hash
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class Entity < Model
2
+ class DetachedEntity < Model
3
3
  module Concerns
4
4
  module PrimaryKey
5
5
  include Concern
@@ -28,7 +28,7 @@ module Foobara
28
28
  def primary_key_attribute
29
29
  return @primary_key_attribute if @primary_key_attribute
30
30
 
31
- unless superclass == Entity
31
+ unless superclass == DetachedEntity
32
32
  @primary_key_attribute = superclass.primary_key_attribute
33
33
  end
34
34
  end
@@ -1,9 +1,23 @@
1
1
  module Foobara
2
- class Entity < Model
2
+ class DetachedEntity < Model
3
3
  module Concerns
4
4
  module Reflection
5
+ class CannotConvertRecordWithoutPrimaryKeyToJsonError < StandardError; end
6
+
5
7
  include Concern
6
8
 
9
+ def inspect
10
+ "<#{entity_name}:#{primary_key}>"
11
+ end
12
+
13
+ def to_json(*_args)
14
+ primary_key&.to_json || raise(
15
+ CannotConvertRecordWithoutPrimaryKeyToJsonError,
16
+ "Cannot call record.to_json on unless record has a primary key. " \
17
+ "Consider instead calling record.attributes.to_json instead."
18
+ )
19
+ end
20
+
7
21
  module ClassMethods
8
22
  def depends_on
9
23
  associations.values.map(&:target_class).uniq
@@ -0,0 +1,41 @@
1
+ module Foobara
2
+ class DetachedEntity < Model
3
+ module Concerns
4
+ module Types
5
+ include Concern
6
+
7
+ foobara_delegate :full_entity_name, :entity_name, to: :class
8
+
9
+ module ClassMethods
10
+ def entity_type
11
+ model_type
12
+ end
13
+
14
+ def type_declaration(...)
15
+ raise "No primary key set yet" unless primary_key_attribute
16
+
17
+ super.merge(type: :detached_entity, primary_key: primary_key_attribute)
18
+ end
19
+
20
+ def set_model_type
21
+ if primary_key_attribute
22
+ super
23
+ end
24
+ end
25
+
26
+ def primary_key_type
27
+ @primary_key_type ||= attributes_type.element_types[primary_key_attribute]
28
+ end
29
+
30
+ def full_entity_name
31
+ full_model_name
32
+ end
33
+
34
+ def entity_name
35
+ model_name
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ module Foobara
2
+ class DetachedEntity < Model
3
+ include Concerns::Equality
4
+ include Concerns::Associations
5
+ include Concerns::PrimaryKey
6
+ include Concerns::Reflection
7
+ include Concerns::Types
8
+
9
+ class << self
10
+ def allowed_subclass_opts
11
+ [:primary_key, *super]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,39 @@
1
+ module Foobara
2
+ module BuiltinTypes
3
+ module DetachedEntity
4
+ module Casters
5
+ class Hash < Attributes::Casters::Hash
6
+ class << self
7
+ def requires_parent_declaration_data?
8
+ true
9
+ end
10
+ end
11
+
12
+ def cast(attributes)
13
+ symbolized_attributes = super
14
+
15
+ entity_class.send(build_method(symbolized_attributes), symbolized_attributes)
16
+ end
17
+
18
+ def build_method(_symbolized_attributes)
19
+ :new
20
+ end
21
+
22
+ def entity_class
23
+ type = parent_declaration_data[:type]
24
+
25
+ if type == expected_type_symbol
26
+ Object.const_get(parent_declaration_data[:model_class])
27
+ else
28
+ Foobara::Namespace.current.foobara_lookup_type!(type).target_class
29
+ end
30
+ end
31
+
32
+ def expected_type_symbol
33
+ :detached_entity
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ module Foobara
2
+ module BuiltinTypes
3
+ module DetachedEntity
4
+ module Validators
5
+ class AttributesDeclaration < Model::Validators::AttributesDeclaration
6
+ # Why is this here in entity/ instead of in model/?
7
+ def possible_errors
8
+ return [] if parent_declaration_data == { type: expected_type_symbol }
9
+
10
+ # TODO: we should also ask the class if it is mutable...
11
+ mutable = parent_declaration_data.key?(:mutable) ? parent_declaration_data[:mutable] : false
12
+
13
+ entity_class.possible_errors(mutable:)
14
+ end
15
+
16
+ def entity_class
17
+ if parent_declaration_data.key?(:model_class)
18
+ Object.const_get(parent_declaration_data[:model_class])
19
+ elsif parent_declaration_data[:type] != expected_type_symbol
20
+ model_type = type_for_declaration(parent_declaration_data[:type])
21
+ model_type.target_class
22
+ else
23
+ # :nocov:
24
+ raise "Missing :model_class in parent_declaration_data for #{parent_declaration_data}"
25
+ # :nocov:
26
+ end
27
+ end
28
+
29
+ def expected_type_symbol
30
+ :detached_entity
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,6 @@
1
+ module Foobara
2
+ module BuiltinTypes
3
+ module DetachedEntity
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,13 @@
1
+ module Foobara
2
+ module TypeDeclarations
3
+ module Handlers
4
+ class ExtendDetachedEntityTypeDeclaration < ExtendModelTypeDeclaration
5
+ class AttributesHandlerDesugarizer < ExtendModelTypeDeclaration::AttributesHandlerDesugarizer
6
+ def expected_type_symbol
7
+ :detached_entity
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,43 @@
1
+ module Foobara
2
+ module TypeDeclarations
3
+ module Handlers
4
+ class ExtendDetachedEntityTypeDeclaration < ExtendModelTypeDeclaration
5
+ class HashDesugarizer < TypeDeclarations::Desugarizer
6
+ def applicable?(sugary_type_declaration)
7
+ return false unless sugary_type_declaration.is_a?(::Hash)
8
+ return false unless Util.all_symbolizable_keys?(sugary_type_declaration)
9
+
10
+ sugary_type_declaration = Util.symbolize_keys(sugary_type_declaration)
11
+
12
+ type_symbol = sugary_type_declaration[:type] || sugary_type_declaration["type"]
13
+ return false unless type_symbol
14
+
15
+ type_symbol = type_symbol.to_sym if type_symbol.is_a?(::String)
16
+
17
+ return true if type_symbol == expected_type_symbol
18
+
19
+ if type_symbol.is_a?(::Symbol) && type_registered?(type_symbol)
20
+ type = Foobara.foobara_root_namespace.foobara_lookup_type(
21
+ type_symbol, mode: Namespace::LookupMode::ABSOLUTE
22
+ )
23
+
24
+ type&.extends?(BuiltinTypes[expected_type_symbol])
25
+ end
26
+ end
27
+
28
+ def expected_type_symbol
29
+ :detached_entity
30
+ end
31
+
32
+ def desugarize(sugary_type_declaration)
33
+ Util.symbolize_keys(sugary_type_declaration)
34
+ end
35
+
36
+ def priority
37
+ Priority::FIRST + 1
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,21 @@
1
+ module Foobara
2
+ module TypeDeclarations
3
+ module Handlers
4
+ class ExtendDetachedEntityTypeDeclaration < ExtendModelTypeDeclaration
5
+ class ModelClassDesugarizer < ExtendModelTypeDeclaration::ModelClassDesugarizer
6
+ def expected_type_symbol
7
+ :detached_entity
8
+ end
9
+
10
+ def default_model_base_class
11
+ Foobara::DetachedEntity
12
+ end
13
+
14
+ def create_model_class_args(model_module:, type_declaration:)
15
+ super.merge(primary_key: type_declaration[:primary_key])
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ module Foobara
2
+ module TypeDeclarations
3
+ module Handlers
4
+ class ExtendDetachedEntityTypeDeclaration < ExtendModelTypeDeclaration
5
+ class PrimaryKeyDesugarizer < Desugarizer
6
+ def applicable?(sugary_type_declaration)
7
+ primary_key = sugary_type_declaration[:primary_key]
8
+
9
+ primary_key.is_a?(::String)
10
+ end
11
+
12
+ def desugarize(sugary_type_declaration)
13
+ sugary_type_declaration.merge(primary_key: sugary_type_declaration[:primary_key].to_sym)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ module Foobara
2
+ module TypeDeclarations
3
+ module Handlers
4
+ class ExtendDetachedEntityTypeDeclaration < ExtendModelTypeDeclaration
5
+ class ToTypeTransformer < ExtendModelTypeDeclaration::ToTypeTransformer
6
+ def non_processor_keys
7
+ [:primary_key, *super]
8
+ end
9
+
10
+ def process_value(strict_declaration_type)
11
+ super.tap do |outcome|
12
+ if outcome.success?
13
+ type = outcome.result
14
+ entity_class = type.target_class
15
+
16
+ unless entity_class.primary_key_attribute
17
+ entity_class.primary_key(strict_declaration_type[:primary_key])
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,35 @@
1
+ module Foobara
2
+ module TypeDeclarations
3
+ module Handlers
4
+ class ExtendDetachedEntityTypeDeclaration < ExtendModelTypeDeclaration
5
+ class ValidatePrimaryKeyIsSymbol < TypeDeclarations::TypeDeclarationValidator
6
+ class PrimaryKeyNotSymbolError < TypeDeclarationError
7
+ class << self
8
+ def context_type_declaration
9
+ {
10
+ primary_key: :duck
11
+ }
12
+ end
13
+ end
14
+ end
15
+
16
+ def applicable?(strict_type_declaration)
17
+ strict_type_declaration.key?(:primary_key)
18
+ end
19
+
20
+ def validation_errors(strict_type_declaration)
21
+ primary_key = strict_type_declaration[:primary_key]
22
+ unless primary_key.is_a?(::Symbol)
23
+ build_error(
24
+ message: "Expected #{primary_key} to be a symbol but it was a #{primary_key.class}",
25
+ context: {
26
+ primary_key:
27
+ }
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ module Foobara
2
+ module TypeDeclarations
3
+ module Handlers
4
+ class ExtendDetachedEntityTypeDeclaration < ExtendModelTypeDeclaration
5
+ class ValidatePrimaryKeyPresent < TypeDeclarations::TypeDeclarationValidator
6
+ # TODO: seems like maybe we could actually check against types now...
7
+ # like make a type for primary_key: :symbol ??
8
+ class MissingPrimaryKeyError < TypeDeclarationError; end
9
+
10
+ def validation_errors(strict_type_declaration)
11
+ unless strict_type_declaration.key?(:primary_key)
12
+ build_error
13
+ end
14
+ end
15
+
16
+ def error_message(_value)
17
+ "Missing required :primary_key to state which attribute is the primary key"
18
+ end
19
+
20
+ def error_context(_value)
21
+ {}
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ module Foobara
2
+ module TypeDeclarations
3
+ module Handlers
4
+ class ExtendDetachedEntityTypeDeclaration < ExtendModelTypeDeclaration
5
+ class ValidatePrimaryKeyReferencesAttribute < TypeDeclarations::TypeDeclarationValidator
6
+ class InvalidPrimaryKeyError < TypeDeclarationError
7
+ class << self
8
+ def context_type_declaration
9
+ {
10
+ allowed_primary_keys: [:symbol],
11
+ primary_key: :symbol
12
+ }
13
+ end
14
+ end
15
+ end
16
+
17
+ def applicable?(strict_type_declaration)
18
+ strict_type_declaration.key?(:primary_key) && strict_type_declaration[:primary_key].is_a?(::Symbol)
19
+ end
20
+
21
+ def validation_errors(strict_type_declaration)
22
+ allowed_primary_keys = strict_type_declaration[:attributes_declaration][:element_type_declarations].keys
23
+
24
+ primary_key = strict_type_declaration[:primary_key]
25
+ unless allowed_primary_keys.include?(primary_key)
26
+ build_error(
27
+ message: "Invalid primary key. Expected #{primary_key} to be one of #{allowed_primary_keys}",
28
+ context: { allowed_primary_keys:, primary_key: }
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,15 @@
1
+ module Foobara
2
+ module TypeDeclarations
3
+ module Handlers
4
+ class ExtendDetachedEntityTypeDeclaration < ExtendModelTypeDeclaration
5
+ def expected_type_symbol
6
+ :detached_entity
7
+ end
8
+
9
+ def priority
10
+ super - 1
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,20 +1,16 @@
1
- require "date"
2
- require "time"
3
- require "bigdecimal"
4
-
5
1
  module Foobara
6
2
  # TODO: I think we should have a configuration that indicates if created records can have primary keys past to them
7
3
  # or not. That is, do primary keys get issued by the database upon insertion? Or are they generated externally
8
4
  # and passed in? Would be nice to have programmatic clarification via explicit configuration.
9
- class Entity < Model
5
+ class Entity < DetachedEntity
10
6
  abstract
11
7
 
12
8
  class << self
13
9
  def install!
14
10
  TypeDeclarations.register_type_declaration(TypeDeclarations::Handlers::ExtendEntityTypeDeclaration.new)
15
11
 
16
- model = Namespace.global.foobara_lookup_type!(:model)
17
- BuiltinTypes.build_and_register!(:entity, model, nil)
12
+ detached_entity = Namespace.global.foobara_lookup_type!(:detached_entity)
13
+ BuiltinTypes.build_and_register!(:entity, detached_entity, nil)
18
14
  end
19
15
 
20
16
  def reset_all