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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -1
- data/projects/command_connectors/src/serializers/entities_to_primary_keys_serializer.rb +2 -0
- data/projects/detached_entity/lib/foobara/detached_entity.rb +19 -0
- data/projects/{entity → detached_entity}/src/concerns/associations.rb +4 -59
- data/projects/detached_entity/src/concerns/equality.rb +24 -0
- data/projects/{entity → detached_entity}/src/concerns/primary_key.rb +2 -2
- data/projects/{entity → detached_entity}/src/concerns/reflection.rb +15 -1
- data/projects/detached_entity/src/concerns/types.rb +41 -0
- data/projects/detached_entity/src/detached_entity.rb +15 -0
- data/projects/detached_entity/src/extensions/builtin_types/detached_entity/casters/hash.rb +39 -0
- data/projects/detached_entity/src/extensions/builtin_types/detached_entity/validators/attributes_declaration.rb +36 -0
- data/projects/detached_entity/src/extensions/builtin_types/detached_entity.rb +6 -0
- data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/attributes_handler_desugarizer.rb +13 -0
- data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/hash_desugarizer.rb +43 -0
- data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/model_class_desugarizer.rb +21 -0
- data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/primary_key_desugarizer.rb +19 -0
- data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/to_type_transformer.rb +26 -0
- data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/validate_primary_key_is_symbol.rb +35 -0
- data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/validate_primary_key_present.rb +27 -0
- data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/validate_primary_key_references_attribute.rb +36 -0
- data/projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration.rb +15 -0
- data/projects/entity/lib/foobara/entity.rb +3 -7
- data/projects/entity/src/concerns/attribute_helpers.rb +1 -58
- data/projects/entity/src/concerns/attributes.rb +1 -1
- data/projects/entity/src/concerns/callbacks.rb +1 -1
- data/projects/entity/src/concerns/initialization.rb +1 -1
- data/projects/entity/src/concerns/mutations.rb +68 -0
- data/projects/entity/src/concerns/persistence.rb +1 -1
- data/projects/entity/src/concerns/queries.rb +56 -1
- data/projects/entity/src/concerns/transactions.rb +1 -1
- data/projects/entity/src/concerns/types.rb +2 -18
- data/projects/entity/src/entity.rb +3 -48
- data/projects/entity/src/extensions/builtin_types/entity/casters/hash.rb +8 -20
- data/projects/entity/src/extensions/builtin_types/entity/validators/attributes_declaration.rb +3 -18
- data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/attributes_handler_desugarizer.rb +2 -3
- data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/hash_desugarizer.rb +2 -32
- data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/model_class_desugarizer.rb +2 -6
- data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/primary_key_desugarizer.rb +2 -11
- data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/to_type_transformer.rb +2 -11
- data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/validate_primary_key_is_symbol.rb +2 -27
- data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/validate_primary_key_present.rb +2 -19
- data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration/validate_primary_key_references_attribute.rb +3 -28
- data/projects/entity/src/extensions/type_declarations/handlers/extend_entity_type_declaration.rb +5 -1
- data/projects/entity/src/new_prepend.rb +1 -1
- data/projects/entity/src/not_found_error.rb +1 -1
- data/projects/foobara/lib/foobara/all.rb +1 -0
- data/projects/manifest/src/foobara/manifest/detached_entity.rb +30 -0
- data/projects/manifest/src/foobara/manifest/domain.rb +4 -0
- data/projects/manifest/src/foobara/manifest/entity.rb +3 -15
- data/projects/manifest/src/foobara/manifest/root_manifest.rb +8 -0
- data/projects/manifest/src/foobara/manifest/type.rb +17 -1
- data/projects/manifest/src/foobara/manifest/type_declaration.rb +17 -0
- data/projects/model/src/model.rb +3 -1
- data/projects/namespace/src/is_namespace.rb +6 -2
- data/projects/types/src/type.rb +0 -6
- metadata +24 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d39eaab1139573fba75d906c51de6091e81d77a1af812f60262bca2ff2064ab
|
4
|
+
data.tar.gz: da9a2b06e850ac495edc2f06a79969b75896b2c28134c91e724bca4c13397182
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce7d43c817996c3301bdbc332875d7bd73dad8f29c64f12c7c070d89cc95a88974cd05bb84aa9f94bb7af2d9181f16e3a4cace92f04d8233486c12b8a25876ca
|
7
|
+
data.tar.gz: 6b6f82a0620e369c2a071f0e3ff33b648243abba31b3e0dc8c59c824a4f10d771add1f7bfbd6f802b4d7ce6a7536fc1f87f77ec9bba052f920229593e10418e2
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,13 @@
|
|
1
|
-
## [0.0.
|
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
|
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 <
|
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[:
|
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[:
|
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
|
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 ==
|
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
|
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,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
|
@@ -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 <
|
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
|
-
|
17
|
-
BuiltinTypes.build_and_register!(:entity,
|
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
|