foobara 0.0.85 → 0.0.87

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3447ac0c4910cce965172cfdeaa22c16558bde49b0b0bcd533e2d5dba7b27188
4
- data.tar.gz: 67797d30549979ba6ef227d8c61bae996408d598b77edac19b4210a6e4e0edc6
3
+ metadata.gz: 45093221881afa7419b208493f3787403c664a149f77728ac64daaea00c7b083
4
+ data.tar.gz: 4103467f677825cd70e2d8d468cc9845511789c32e4a2f69c3cc4901fb9704c5
5
5
  SHA512:
6
- metadata.gz: 2c1b45ecd837fe9f66bb5dcd7d97d3f07dcbb8876a2c913386837378197b4ba0f33fac885283806adf9afea80cef65e3445ce13aff0e491005add424353e2475
7
- data.tar.gz: 4270856b98fb68dd813b3bde8e7284995f1293ec0f310122a4d136da4e2c02c094e5110c8e1363b80a8a6d699d0efec49148d4284302494f714e11edbbdd84b6
6
+ metadata.gz: e655dc065c7b923be1d76742e9fc651f8d7581cdfd3ad9130cc20e474ca42bb15791ebc35238bc52d8b255cef440237234c01eda4189b850287ecd142141271e
7
+ data.tar.gz: 54893503e3dfed78a608590f44a9673aca2b8c208094a306c7b10634afea59380caee6a0998cd283c1ecb7097b227705b9a3644186d2f3afe561ee0676170717
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ # [0.0.87] - 2025-03-26
2
+
3
+ - TypedTransformer refactor to reduce confusion and bugs
4
+
5
+ # [0.0.86] - 2025-03-23
6
+
7
+ - Add an AttributesTransformer.only method to quickly get a TypedTransformer (helpful with inputs_transformers)
8
+
1
9
  # [0.0.85] - 2025-03-22
2
10
 
3
11
  - Add Manifest::TypeDeclaration#sensitive?
@@ -36,12 +36,12 @@ module Foobara
36
36
  result_type = command_class.result_type
37
37
 
38
38
  if result_type&.has_sensitive_types?
39
+
39
40
  remover_class = Foobara::TypeDeclarations.sensitive_value_remover_class_for_type(result_type)
40
41
 
41
42
  remover = Namespace.use scoped_namespace do
42
43
  transformed_result_type = result_type_from_transformers(result_type, result_transformers)
43
-
44
- remover_class.new(transformed_result_type).tap do |r|
44
+ remover_class.new(from: transformed_result_type).tap do |r|
45
45
  r.scoped_path = ["SensitiveValueRemover", *transformed_result_type.scoped_full_path]
46
46
  end
47
47
  end
@@ -71,25 +71,34 @@ module Foobara
71
71
  to: :command_class
72
72
 
73
73
  def inputs_type
74
- type = command_class.inputs_type
75
-
76
- inputs_transformers.reverse.each do |transformer|
77
- if transformer.is_a?(Class) && transformer < TypeDeclarations::TypedTransformer
78
- new_type = transformer.type(type)
79
-
80
- type = new_type if new_type
81
- end
82
- end
83
-
84
- type
74
+ return @inputs_type if defined?(@inputs_type)
75
+
76
+ @inputs_type = if inputs_transformer
77
+ if inputs_transformer.is_a?(Value::Processor::Pipeline)
78
+ inputs_transformer.processors.each do |transformer|
79
+ if transformer.is_a?(TypeDeclarations::TypedTransformer)
80
+ from_type = transformer.from_type
81
+ if from_type
82
+ @inputs_type = from_type
83
+ return from_type
84
+ end
85
+ end
86
+ end
87
+
88
+ command_class.inputs_type
89
+ else
90
+ inputs_transformer.from_type || command_class.inputs_type
91
+ end
92
+ else
93
+ command_class.inputs_type
94
+ end
85
95
  end
86
96
 
87
97
  def result_type_from_transformers(result_type, transformers)
88
- Util.array(transformers).each do |transformer|
98
+ transformers.reverse.each do |transformer|
89
99
  if transformer.is_a?(Class) && transformer < TypeDeclarations::TypedTransformer
90
- new_type = transformer.type(result_type)
91
-
92
- result_type = new_type if new_type
100
+ new_type = transformer.to_type
101
+ return new_type if new_type
93
102
  end
94
103
  end
95
104
 
@@ -217,6 +226,69 @@ module Foobara
217
226
  )
218
227
  )
219
228
  end
229
+
230
+ def inputs_transformer
231
+ return @inputs_transformer if defined?(@inputs_transformer)
232
+
233
+ if inputs_transformers.empty?
234
+ @inputs_transformer = nil
235
+ return
236
+ end
237
+
238
+ @inputs_transformer = begin
239
+ transformers = transformers_to_processors(inputs_transformers,
240
+ command_class.inputs_type, direction: :to)
241
+
242
+ if transformers.size == 1
243
+ transformers.first
244
+ else
245
+ Value::Processor::Pipeline.new(processors: transformers)
246
+ end
247
+ end
248
+ end
249
+
250
+ def result_transformer
251
+ return @result_transformer if defined?(@result_transformer)
252
+
253
+ if result_transformers.empty?
254
+ @result_transformer = nil
255
+ return
256
+ end
257
+
258
+ @result_transformer = begin
259
+ transformers = transformers_to_processors(result_transformers, command_class.result_type, direction: :from)
260
+
261
+ if transformers.size == 1
262
+ transformers.first
263
+ else
264
+ Value::Processor::Pipeline.new(processors: transformers)
265
+ end
266
+ end
267
+ end
268
+
269
+ # TODO: this is pretty messy with smells.
270
+ def transformers_to_processors(transformers, target_type, direction: :from, declaration_data: self)
271
+ transformers.map do |transformer|
272
+ if transformer.is_a?(Class)
273
+ if transformer < TypeDeclarations::TypedTransformer
274
+ transformer.new(direction => target_type).tap do |tx|
275
+ new_type = direction == :from ? tx.to_type : tx.from_type
276
+ target_type = new_type if new_type
277
+ end
278
+ else
279
+ transformer.new(declaration_data)
280
+ end
281
+ elsif transformer.is_a?(Value::Processor)
282
+ transformer
283
+ elsif transformer.respond_to?(:call)
284
+ Value::Transformer.create(transform: transformer)
285
+ else
286
+ # :nocov:
287
+ raise "Not sure how to apply #{inputs_transformer}"
288
+ # :nocov:
289
+ end
290
+ end
291
+ end
220
292
  end
221
293
 
222
294
  attr_accessor :command, :untransformed_inputs, :transformed_inputs, :outcome, :authenticated_user
@@ -259,16 +331,16 @@ module Foobara
259
331
  end
260
332
 
261
333
  def transform_inputs
262
- self.transformed_inputs = if inputs_transformer
263
- inputs_transformer.process_value!(untransformed_inputs)
334
+ self.transformed_inputs = if self.class.inputs_transformer
335
+ self.class.inputs_transformer.process_value!(untransformed_inputs)
264
336
  else
265
337
  untransformed_inputs
266
338
  end
267
339
  end
268
340
 
269
341
  def transform_result
270
- if result_transformer
271
- self.outcome = Outcome.success(result_transformer.process_value!(result))
342
+ if self.class.result_transformer
343
+ self.outcome = Outcome.success(self.class.result_transformer.process_value!(result))
272
344
  end
273
345
  end
274
346
 
@@ -278,30 +350,6 @@ module Foobara
278
350
  end
279
351
  end
280
352
 
281
- def inputs_transformer
282
- return nil if inputs_transformers.empty?
283
-
284
- transformers = transformers_to_processors(inputs_transformers, command_class.inputs_type)
285
-
286
- if transformers.size == 1
287
- transformers.first
288
- else
289
- Value::Processor::Pipeline.new(processors: transformers)
290
- end
291
- end
292
-
293
- def result_transformer
294
- return nil if result_transformers.empty?
295
-
296
- transformers = transformers_to_processors(result_transformers, command_class.result_type)
297
-
298
- if transformers.size == 1
299
- transformers.first
300
- else
301
- Value::Processor::Pipeline.new(processors: transformers)
302
- end
303
- end
304
-
305
353
  # TODO: let's get this out of here...
306
354
  # we might want to have different serializers for different command instances of the same class.
307
355
  # but currently serializers is set on the class. Since this class should not be concerned with serialization, we
@@ -309,7 +357,7 @@ module Foobara
309
357
  def serializer
310
358
  return nil if serializers.empty?
311
359
 
312
- transformers = transformers_to_processors(serializers, nil)
360
+ transformers = self.class.transformers_to_processors(serializers, nil, declaration_data: self)
313
361
 
314
362
  if transformers.size == 1
315
363
  transformers.first
@@ -321,7 +369,8 @@ module Foobara
321
369
  def errors_transformer
322
370
  return nil if errors_transformers.empty?
323
371
 
324
- transformers = transformers_to_processors(errors_transformers, nil)
372
+ transformers = self.class.transformers_to_processors(errors_transformers, nil, direction: :from,
373
+ declaration_data: self)
325
374
 
326
375
  if transformers.size == 1
327
376
  transformers.first
@@ -334,7 +383,11 @@ module Foobara
334
383
  def pre_commit_transformer
335
384
  return nil if pre_commit_transformers.empty?
336
385
 
337
- transformers = transformers_to_processors(pre_commit_transformers, nil)
386
+ transformers = self.class.transformers_to_processors(
387
+ pre_commit_transformers,
388
+ nil,
389
+ declaration_data: self
390
+ )
338
391
 
339
392
  if transformers.size == 1
340
393
  transformers.first
@@ -343,29 +396,6 @@ module Foobara
343
396
  end
344
397
  end
345
398
 
346
- def transformers_to_processors(transformers, from_type)
347
- transformers.map do |transformer|
348
- if transformer.is_a?(Class)
349
- if transformer < TypeDeclarations::TypedTransformer
350
- transformer.new(from_type).tap do |tx|
351
- new_type = tx.type
352
- from_type = new_type if new_type
353
- end
354
- else
355
- transformer.new(self)
356
- end
357
- elsif transformer.is_a?(Value::Processor)
358
- transformer
359
- elsif transformer.respond_to?(:call)
360
- Value::Transformer.create(transform: transformer)
361
- else
362
- # :nocov:
363
- raise "Not sure how to apply #{inputs_transformer}"
364
- # :nocov:
365
- end
366
- end
367
- end
368
-
369
399
  def construct_command
370
400
  self.command = command_class.new(transformed_inputs)
371
401
  end
@@ -387,14 +387,28 @@ module Foobara
387
387
  additional_to_include.delete(o)
388
388
 
389
389
  if o.is_a?(::Module)
390
- if o.foobara_domain? || o.foobara_organization? || (o.is_a?(::Class) && o < Foobara::Command)
390
+ if o.foobara_domain? || o.foobara_organization?
391
+ unless o.foobara_root_namespace == command_registry
392
+ next
393
+ end
394
+ elsif o.is_a?(::Class) && o < Foobara::Command
391
395
  next
392
396
  end
393
- elsif o.is_a?(Types::Type) && remove_sensitive && o.sensitive?
394
- # :nocov:
395
- raise UnexpectedSensitiveTypeInManifestError,
396
- "Unexpected sensitive type in manifest: #{o.scoped_full_path}. Make sure these are not included."
397
+ elsif o.is_a?(Types::Type)
398
+ if remove_sensitive && o.sensitive?
399
+ # :nocov:
400
+ raise UnexpectedSensitiveTypeInManifestError,
401
+ "Unexpected sensitive type in manifest: #{o.scoped_full_path}. Make sure these are not included."
397
402
  # :nocov:
403
+ else
404
+ domain_name = o.foobara_domain.scoped_full_name
405
+
406
+ unless command_registry.foobara_registered?(domain_name)
407
+ domain = command_registry.build_and_register_exposed_domain(domain_name)
408
+ additional_to_include << domain
409
+ additional_to_include << domain.foobara_organization
410
+ end
411
+ end
398
412
  end
399
413
 
400
414
  object = o
@@ -4,7 +4,6 @@ module Foobara
4
4
  module CommandConnectors
5
5
  module Serializers
6
6
  class ErrorsSerializer < Serializer
7
- # TODO: always_applicable? instead?
8
7
  def always_applicable?
9
8
  !request.outcome.success?
10
9
  end
@@ -123,6 +123,29 @@ module Foobara
123
123
  end
124
124
  end
125
125
 
126
+ def construct_deep_associations(
127
+ type = attributes_type,
128
+ path = DataPath.new,
129
+ result = {},
130
+ remove_sensitive: false
131
+ )
132
+ associations = construct_associations(type, path, result, remove_sensitive:)
133
+
134
+ deep = {}
135
+
136
+ associations.each_pair do |data_path, association_type|
137
+ deep[data_path] = association_type
138
+
139
+ entity_class = association_type.target_class
140
+
141
+ entity_class.deep_associations(remove_sensitive:).each_pair do |sub_data_path, sub_type|
142
+ deep["#{data_path}.#{sub_data_path}"] = sub_type
143
+ end
144
+ end
145
+
146
+ deep
147
+ end
148
+
126
149
  # TODO: this big switch is a problem. Hard to create new types in other projects without being able
127
150
  # to modify this switch. Figure out what to do.
128
151
  def construct_associations(
@@ -224,8 +247,8 @@ module Foobara
224
247
  types = types&.reject(&:sensitive?)
225
248
  end
226
249
 
227
- types.any? do |type|
228
- contains_associations?(type, false, remove_sensitive:)
250
+ types.any? do |key_or_value_type|
251
+ contains_associations?(key_or_value_type, false, remove_sensitive:)
229
252
  end
230
253
  end
231
254
  end
@@ -231,9 +231,13 @@ module Foobara
231
231
  end
232
232
  end
233
233
 
234
- def foobara_type_from_declaration(...)
234
+ def foobara_type_from_declaration(*args, **opts, &block)
235
+ if opts.empty? && block.nil? && args.size == 1 && args.first.is_a?(Types::Type)
236
+ return args.first
237
+ end
238
+
235
239
  Foobara::Namespace.use self do
236
- foobara_type_builder.type_for_declaration(...)
240
+ foobara_type_builder.type_for_declaration(*args, **opts, &block)
237
241
  end
238
242
  end
239
243
 
@@ -3,12 +3,28 @@ module Foobara
3
3
  module SensitiveValueRemovers
4
4
  class Entity < DetachedEntity::SensitiveValueRemovers::DetachedEntity
5
5
  def transform(record)
6
- sanitized_record = super
6
+ if record.loaded? || record.created?
7
+ sanitized_record = super
7
8
 
8
- sanitized_record.is_loaded = record.loaded?
9
- sanitized_record.is_persisted = record.persisted?
9
+ sanitized_record.is_loaded = record.loaded?
10
+ sanitized_record.is_persisted = record.persisted?
10
11
 
11
- sanitized_record
12
+ sanitized_record
13
+ elsif record.persisted?
14
+ # We will assume that we do not need to clean up the primary key itself as
15
+ # we will assume we don't allow sensitive primary keys for now.
16
+ sanitized_record = to_type.target_class.build(record.class.primary_key_attribute => record.primary_key)
17
+
18
+ sanitized_record.is_persisted = true
19
+ sanitized_record.is_loaded = false
20
+ sanitized_record.is_built = false
21
+
22
+ sanitized_record
23
+ else
24
+ # :nocov:
25
+ raise "Not sure what to do with a record that isn't loaded, created, or persisted"
26
+ # :nocov:
27
+ end
12
28
  end
13
29
 
14
30
  def build_method
@@ -92,7 +92,6 @@ module Foobara
92
92
 
93
93
  def to_model
94
94
  raise "not an model" unless model?
95
- raise "model extension instead of an model" unless relevant_manifest.size == 1
96
95
 
97
96
  type = to_type
98
97
 
@@ -12,7 +12,7 @@ module Foobara
12
12
  return false if type_symbol == expected_type_symbol
13
13
 
14
14
  if type_registered?(type_symbol)
15
- type = type_for_declaration(type_symbol)
15
+ type = lookup_type!(type_symbol)
16
16
  type.extends?(BuiltinTypes[expected_type_symbol])
17
17
  end
18
18
  end
@@ -5,12 +5,10 @@ module Foobara
5
5
  def transform(record)
6
6
  attributes_type = from_type.element_types
7
7
 
8
- sanitized_attributes, changed = sanitize_value(attributes_type, record.attributes)
8
+ sanitized_attributes, _changed = sanitize_value(attributes_type, record.attributes)
9
9
 
10
- if changed
11
- type.target_class.send(build_method, sanitized_attributes)
12
- else
13
- record
10
+ Namespace.use(to_type.created_in_namespace) do
11
+ to_type.target_class.send(build_method, sanitized_attributes)
14
12
  end
15
13
  end
16
14
 
@@ -164,7 +164,20 @@ module Foobara
164
164
  def foobara_autoset_scoped_path(mod, make_top_level: false)
165
165
  return if mod.scoped_path_set?
166
166
 
167
- scoped_path = mod.name.split("::")
167
+ mod_name = mod.name
168
+
169
+ if mod_name.nil?
170
+ parent = mod.superclass
171
+ super_name = nil
172
+
173
+ begin
174
+ super_name = parent.scoped_path_set? ? parent.scoped_full_name : parent.name
175
+ end until super_name
176
+
177
+ mod_name = [super_name, mod.object_id.to_s(16)].join("::")
178
+ end
179
+
180
+ scoped_path = mod_name.split("::")
168
181
 
169
182
  adjusted_scoped_path = []
170
183
 
@@ -33,6 +33,22 @@ module Foobara
33
33
  )
34
34
  end
35
35
 
36
+ def only(declaration, *keys)
37
+ valid_keys = declaration[:element_type_declarations].keys
38
+ keys_to_keep = keys.map(&:to_sym)
39
+ invalid_keys = keys_to_keep - valid_keys
40
+
41
+ if invalid_keys.any?
42
+ # :nocov:
43
+ raise ArgumentError, "Invalid keys: #{invalid_keys} expected only #{valid_keys}"
44
+ # :nocov:
45
+ end
46
+
47
+ keys_to_reject = valid_keys - keys_to_keep
48
+
49
+ reject(declaration, keys_to_reject)
50
+ end
51
+
36
52
  def reject(declaration, *keys)
37
53
  # TODO: do we really need a deep dup?
38
54
  declaration = Util.deep_dup(declaration)
@@ -0,0 +1,27 @@
1
+ module Foobara
2
+ class AttributesTransformer < TypeDeclarations::TypedTransformer
3
+ class << self
4
+ attr_accessor :only_attributes
5
+
6
+ def only(*attribute_names)
7
+ transformer_class = Class.new(self)
8
+ transformer_class.only_attributes = attribute_names
9
+
10
+ transformer_class
11
+ end
12
+ end
13
+
14
+ def to_type_declaration
15
+ from_declaration = from_type.declaration_data
16
+ TypeDeclarations::Attributes.only(from_declaration, *self.class.only_attributes)
17
+ end
18
+
19
+ def transform(inputs)
20
+ inputs.slice(*allowed_keys)
21
+ end
22
+
23
+ def allowed_keys
24
+ to_type.element_types.keys
25
+ end
26
+ end
27
+ end
@@ -15,7 +15,7 @@ module Foobara
15
15
  end
16
16
 
17
17
  def registered_type(strict_type_declaration)
18
- lookup_absolute_type!(type_symbol(strict_type_declaration), mode: Namespace::LookupMode::ABSOLUTE)
18
+ lookup_type!(type_symbol(strict_type_declaration))
19
19
  end
20
20
 
21
21
  def target_classes(strict_type_declaration)
@@ -3,24 +3,21 @@ require_relative "typed_transformer"
3
3
  module Foobara
4
4
  module TypeDeclarations
5
5
  class RemoveSensitiveValuesTransformer < TypedTransformer
6
- class << self
7
- def type_declaration(type_declaration)
8
- if type_declaration.is_a?(Types::Type)
9
- type_declaration = type_declaration.declaration_data
10
- end
6
+ def from(...)
7
+ super.tap do
8
+ associations = Foobara::DetachedEntity.construct_deep_associations(from_type)
9
+
10
+ associations&.values&.reverse&.each do |entity_type|
11
+ declaration = entity_type.declaration_data
12
+ sanitized_type_declaration = TypeDeclarations.remove_sensitive_types(declaration)
11
13
 
12
- TypeDeclarations.remove_sensitive_types(type_declaration)
14
+ Domain.current.foobara_type_from_declaration(sanitized_type_declaration)
15
+ end
13
16
  end
14
17
  end
15
18
 
16
- attr_accessor :namespace
17
-
18
- def initialize(...)
19
- super
20
-
21
- self.namespace = Namespace.current
22
-
23
- type
19
+ def to_type_declaration
20
+ TypeDeclarations.remove_sensitive_types(from_type.declaration_data)
24
21
  end
25
22
 
26
23
  def transform(_value)
@@ -31,9 +28,11 @@ module Foobara
31
28
 
32
29
  def sanitize_value(type, value)
33
30
  if type.has_sensitive_types?
34
- remover_class = TypeDeclarations.sensitive_value_remover_class_for_type(type)
35
- remover = Namespace.use(namespace) { remover_class.new(type) }
36
- sanitized_value = remover.process_value!(value)
31
+ sanitized_value = Namespace.use to_type.created_in_namespace do
32
+ remover_class = TypeDeclarations.sensitive_value_remover_class_for_type(type)
33
+ remover = remover_class.new(from: type)
34
+ remover.process_value!(value)
35
+ end
37
36
 
38
37
  [sanitized_value, sanitized_value != value]
39
38
  else
@@ -3,87 +3,101 @@ module Foobara
3
3
  # TODO: this should instead be a processor and have its own possible_errors
4
4
  class TypedTransformer < Value::Transformer
5
5
  class << self
6
- # A,B,C,D
7
- # let's say this transformer is C...
8
- # If we are a noop, then we are going to output D.type and we expect B.type
9
- # We obviously have a problem if D is incompatible with our output type.
10
- # We need to know B.output_type in order to say what we are going to output.
11
- #
12
- # Conversely, we need to know what D expects in order to say what we expect to receive (sometimes)
13
- #
14
- # So logic is... For C to say what its type is, it must know B's output_type.
15
- # or... I guess for an inputs transformer, we need to know what D expects as its type, right?
16
- # since we have an obligation be compatible with it.
17
- #
18
- # Use case 1: command line interface gets awkward with models
19
- # 1. Command takes model A
20
- # 2. we want an inputs transformer that takes A.attributes_type
21
- # 3. Therefore its type is A.attributes_type
22
- # 4. And also, its output type is A.attributes_type in this case since there's no need to actually create the
23
- # models.
24
- # 5. So to tell our type, we must know the type of what comes next.
25
- #
26
- # Use case 2:
27
- # 1. Command takes foo: :integer but we want to take bar: :string
28
- # 2. transformer has this hard-coded knowledge.
29
- # 3. we don't need to receive either types to answer our input and output types.
30
- #
31
- # Use case 3: Changing a record into its primary key
32
- # 1. Command has result type of A which is an Entity
33
- # 2. transformer takes an A record and returns record.primary_key
34
- # 3. To know the output type, we need to know the result type of the previous type.
35
- # 4. To know the input type, we need to know the type of the previous transformer since they are the same.
36
- # (however, by convention we can just use nil in this case.)
37
- #
38
- # Use case 4: document upload
39
- # 1. Command takes input stream plus some document info
40
- # 2. controller action receives temporary file path
41
- # 3. transformer opens input stream and replaces file path with input stream
42
- # 4. In this case, we have hard-coded types.
43
- #
44
- # Challenge: we seem to not know in advance if the transformer needs to know what comes before it or what comes
45
- # after it. Unless we are writing a one-off transformer then we have hard-coded knowledge.
46
- #
47
- # Seems like input transformer really needs to know what comes next, the target type.
48
- # Seems like output transformer might require to know what came previously
49
- #
50
- # Plan:
51
- # 1. Both inputs transformer and result have similar structure... they have a relevant type that they transform.
52
- # The difference is that the result takes previous steps output and transforms it to a different type, whereas
53
- # the input transformer needs to know what comes next in order to communicate its types.
54
- # So we might be able to get away with a transformed_type that accepts the from_type. And the calling code can
55
- # interpret how it goes. This might create some awkwardness or confusion at least when creating one of the
56
- # two types of transformer.
57
- def type_declaration(_from_type)
58
- # :nocov:
59
- nil
60
- # :nocov:
6
+ def requires_declaration_data?
7
+ false
61
8
  end
62
9
 
63
- def type(from_type)
64
- dec = type_declaration(from_type)
10
+ def requires_parent_declaration_data?
11
+ false
12
+ end
65
13
 
66
- if dec
67
- if dec.is_a?(Types::Type)
68
- dec
69
- else
70
- Domain.current.foobara_type_from_declaration(dec)
71
- end
72
- end
14
+ def from(...)
15
+ @from_type = Domain.current.foobara_type_from_declaration(...)
73
16
  end
17
+
18
+ def to(...)
19
+ @to_type = Domain.current.foobara_type_from_declaration(...)
20
+ end
21
+
22
+ attr_reader :from_type, :to_type
23
+ end
24
+
25
+ def from_type_declaration
26
+ nil
27
+ end
28
+
29
+ def to_type_declaration
30
+ nil
31
+ end
32
+
33
+ def from_type
34
+ return @from_type if defined?(@from_type)
35
+
36
+ @from_type = self.class.from_type || if from_type_declaration
37
+ Domain.current.foobara_type_from_declaration(from_type_declaration)
38
+ end
39
+ end
40
+
41
+ def to_type
42
+ return @to_type if defined?(@to_type)
43
+
44
+ @to_type = self.class.to_type || if to_type_declaration
45
+ Domain.current.foobara_type_from_declaration(to_type_declaration)
46
+ end
47
+ end
48
+
49
+ def has_to_type?
50
+ !!to_type
74
51
  end
75
52
 
76
- alias from_type declaration_data
53
+ def has_from_type?
54
+ !!from_type
55
+ end
56
+
57
+ def from(...)
58
+ @from_type = Domain.current.foobara_type_from_declaration(...)
59
+ end
60
+
61
+ def to(...)
62
+ @to_type = Domain.current.foobara_type_from_declaration(...)
63
+ end
77
64
 
78
- def type
79
- return @type if @type
65
+ def initialize(from: nil, to: nil)
66
+ super()
67
+
68
+ if from
69
+ self.from from
70
+ end
71
+
72
+ if to
73
+ self.to to
74
+ end
80
75
 
81
- @type = self.class.type(from_type)
76
+ # we want to force these to be created now in the current name space if they are declarations
77
+ from_type
78
+ to_type
82
79
  end
83
80
 
84
81
  def process_value(value)
82
+ if has_from_type?
83
+ outcome = Namespace.use from_type.created_in_namespace do
84
+ from_type.process_value(value)
85
+ end
86
+
87
+ return outcome unless outcome.success?
88
+
89
+ value = outcome.result
90
+ end
91
+
85
92
  output = transform(value)
86
- Outcome.success(output)
93
+
94
+ if has_to_type?
95
+ Namespace.use to_type.created_in_namespace do
96
+ to_type.process_value(output)
97
+ end
98
+ else
99
+ Outcome.success(output)
100
+ end
87
101
  end
88
102
  end
89
103
  end
@@ -12,10 +12,6 @@ module Foobara
12
12
  Domain.current.foobara_type_builder.type_declaration_handler_for(...)
13
13
  end
14
14
 
15
- def lookup_absolute_type!(*, **opts, &)
16
- Foobara::Namespace.global.foobara_lookup_type!(*, **opts.merge(mode: Namespace::LookupMode::ABSOLUTE), &)
17
- end
18
-
19
15
  def lookup_type!(...)
20
16
  Foobara::Namespace.current.foobara_lookup_type!(...)
21
17
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foobara
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.85
4
+ version: 0.0.87
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-03-23 00:00:00.000000000 Z
10
+ date: 2025-03-26 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: bigdecimal
@@ -359,6 +359,7 @@ files:
359
359
  - projects/thread_parent/src/thread_parent.rb
360
360
  - projects/type_declarations/lib/foobara/type_declarations.rb
361
361
  - projects/type_declarations/src/attributes.rb
362
+ - projects/type_declarations/src/attributes_transformer.rb
362
363
  - projects/type_declarations/src/caster.rb
363
364
  - projects/type_declarations/src/desugarizer.rb
364
365
  - projects/type_declarations/src/dsl/attributes.rb