foobara 0.0.30 → 0.0.31

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -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. metadata +24 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2052d9d5b024d1a47bc58db2e556345569adc23512580280d636dee90e21a624
4
- data.tar.gz: baeb2f5c0a5d0f3f9d16e57309246a9e0c188ebf4c2089c7fa61e578fed04940
3
+ metadata.gz: 8d39eaab1139573fba75d906c51de6091e81d77a1af812f60262bca2ff2064ab
4
+ data.tar.gz: da9a2b06e850ac495edc2f06a79969b75896b2c28134c91e724bca4c13397182
5
5
  SHA512:
6
- metadata.gz: a03fadfd01cc720964f056a826aec7bad39208a974754d919a19654e606013fc772490da495e1317cfdeae8cb3ece6de34e098d1d9710b2718643ffe808edf3f
7
- data.tar.gz: b0bb12d7f3b2368676664838a0d54575ded39a61482479cbc31ca9c23b3223fe1210c8fdf48b20c89c7bc72e3977792813d7e249cb94cde3d2a4917ce11fec3e
6
+ metadata.gz: ce7d43c817996c3301bdbc332875d7bd73dad8f29c64f12c7c070d89cc95a88974cd05bb84aa9f94bb7af2d9181f16e3a4cace92f04d8233486c12b8a25876ca
7
+ data.tar.gz: 6b6f82a0620e369c2a071f0e3ff33b648243abba31b3e0dc8c59c824a4f10d771add1f7bfbd6f802b4d7ce6a7536fc1f87f77ec9bba052f920229593e10418e2
data/CHANGELOG.md CHANGED
@@ -1,4 +1,8 @@
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
@@ -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