foobara 0.0.36 → 0.0.38

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: 796f1d29d3a7ab80b6ae136f254a80549a75ec952ec1219701960abe24bd1256
4
- data.tar.gz: c99fc6d286203521a9242dbc7ed042a27161b12fb213eec3b44c02f4cca26236
3
+ metadata.gz: bc8a84b48b8479025a63b5ae393436a12826aa19291bf8f60d453e891e5bc433
4
+ data.tar.gz: 6a0b086f7b5921f2301c566f53c8bc5b2d22f59693c9349adbeb727d49323cc4
5
5
  SHA512:
6
- metadata.gz: bd63e1dfe5afab9b6b29c855352d3db79a1fa4c04f912c385eeeeb187d2bedbf558fa929d021b276b510ce8d9b62aa4d41da92f8fba7aa6d12e6b752b47d80db
7
- data.tar.gz: 992b76e3ba595df506570c311a93174e9745ed9e9f5af310cd3b56c1b0274b668f4f38bd3344499c11dd352b5e37ab7bf400ef4421e0b9810d774f91290505c7
6
+ metadata.gz: 70b1b7689b897bc3a7b7ef790fdf3b343901602948b4107e2606cc8e5a98a3b352d70f519759246127d293fa6e81fa250ba94b7d4341199b8aa70ebbff5b603b
7
+ data.tar.gz: 00a4a5a17cacb2aeacf6d5b03a2938f2d93c55c598b4e109d72bd57d75234e66b457e3331a17e34a342124f731bedeadbda33c7e3ac7bb5b8bf540ca51be70ed
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [0.0.37] - 2024-12-11
2
+
3
+ - Make DomainMappers extend Command so they have possible errors and statefulness/a cleaner API
4
+ - Fixup domain mapper lookups to give proper values/errors in various scenarios, particularly in the context
5
+ of running a subcommand
6
+
1
7
  ## [0.0.36] - 2024-12-10
2
8
 
3
9
  - Fix bug with command-named convenience functions
data/README.md CHANGED
@@ -1386,25 +1386,29 @@ module FoobaraDemo
1386
1386
  module CapyCafe
1387
1387
  foobara_depends_on AnimalHouse
1388
1388
 
1389
- class AnimalToCapybara < Foobara::DomainMapper
1390
- from AnimalHouse::Animal
1391
- to CreateCapybara
1389
+ module DomainMappers
1390
+ class MapAnimalToCapybara < Foobara::DomainMapper
1391
+ from AnimalHouse::Animal
1392
+ to CreateCapybara
1393
+
1394
+ def map
1395
+ {
1396
+ name: "#{first_name} #{last_name}",
1397
+ age: birthday_to_age
1398
+ }
1399
+ end
1392
1400
 
1393
- def map(animal)
1394
- age = birthday_to_age(animal.birthday)
1401
+ alias animal from
1395
1402
 
1396
- {
1397
- name: "#{animal.first_name} #{animal.last_name}",
1398
- age:
1399
- }
1400
- end
1403
+ foobara_delegate :first_name, :last_name, :birthday, to: :animal
1401
1404
 
1402
- def birthday_to_age(birthday)
1403
- today = Date.today
1404
- age = today.year - birthday.year
1405
- birthday_this_year = Date.new(birthday.year + age, birthday.month, birthday.day)
1405
+ def birthday_to_age
1406
+ today = Date.today
1407
+ age = today.year - birthday.year
1408
+ birthday_this_year = Date.new(birthday.year + age, birthday.month, birthday.day)
1406
1409
 
1407
- today < birthday_this_year ? age - 1 : age
1410
+ today < birthday_this_year ? age - 1 : age
1411
+ end
1408
1412
  end
1409
1413
  end
1410
1414
  end
@@ -1420,7 +1424,7 @@ of a command. But we can play with it directly:
1420
1424
 
1421
1425
  ```irb
1422
1426
  $ ./animal_house_import.rb
1423
- > create_capybara_inputs = FoobaraDemo::CapyCafe::AnimalToCapybara.call(species: :capybara, first_name: "Barbara", last_name: "Doe", birthday: "1000-01-01")
1427
+ > create_capybara_inputs = FoobaraDemo::CapyCafe::DomainMappers::MapAnimalToCapybara.map!(species: :capybara, first_name: "Barbara", last_name: "Doe", birthday: "1000-01-01")
1424
1428
  ==> {:name=>"Barbara Doe", :age=>1024}
1425
1429
  > barbara = FoobaraDemo::CapyCafe::CreateCapybara.run!(create_capybara_inputs)
1426
1430
  ==> <Capybara:2>
@@ -1430,7 +1434,11 @@ $ ./animal_house_import.rb
1430
1434
  ==> 2
1431
1435
  ```
1432
1436
 
1433
- And we can increment Barbara's age now that she has been imported into our CapyCafe:
1437
+ Now let's make use of our domain mapper in a command, which is its intended purpose:
1438
+
1439
+ ```ruby
1440
+
1441
+ ```
1434
1442
 
1435
1443
  ```irb
1436
1444
  > FoobaraDemo::CapyCafe::IncrementAge.run!(capybara: barbara)
@@ -1444,33 +1452,6 @@ And we can increment Barbara's age now that she has been imported into our CapyC
1444
1452
  Now let's create a command that makes use of our domain mapper which is the typical usage pattern:
1445
1453
 
1446
1454
  ```ruby
1447
- #!/usr/bin/env ruby
1448
-
1449
- require "foobara/remote_imports"
1450
-
1451
- [9292, 9293].each do |port|
1452
- Foobara::RemoteImports::ImportCommand.run!(manifest_url: "http://localhost:#{port}/manifest")
1453
- end
1454
-
1455
- module FoobaraDemo
1456
- module AnimalHouse
1457
- foobara_domain!
1458
- end
1459
- end
1460
-
1461
- module FoobaraDemo
1462
- module AnimalHouse
1463
- class Animal < Foobara::Model
1464
- attributes do
1465
- first_name :string
1466
- last_name :string
1467
- birthday :date
1468
- species :symbol, one_of: %i[capybara cat tartigrade]
1469
- end
1470
- end
1471
- end
1472
- end
1473
-
1474
1455
  module FoobaraDemo
1475
1456
  module CapyCafe
1476
1457
  class ImportAnimal < Foobara::Command
@@ -1487,7 +1468,7 @@ module FoobaraDemo
1487
1468
 
1488
1469
  possible_input_error :animal, NotACapybara
1489
1470
 
1490
- depends_on CreateCapybara
1471
+ depends_on CreateCapybara, DomainMappers::MapAnimalToCapybara
1491
1472
 
1492
1473
  def execute
1493
1474
  create_capybara
@@ -1497,14 +1478,6 @@ module FoobaraDemo
1497
1478
 
1498
1479
  attr_accessor :capybara
1499
1480
 
1500
- def validate
1501
- species = animal.species
1502
-
1503
- unless species == :capybara
1504
- add_input_error :animal, NotACapybara, animal: animal, species: species
1505
- end
1506
- end
1507
-
1508
1481
  def create_capybara
1509
1482
  self.capybara = run_mapped_subcommand!(CreateCapybara, animal)
1510
1483
  end
@@ -1518,8 +1491,17 @@ Note that we can automatically map `animal` to CreateCapybara inputs by calling
1518
1491
  Let's play with it:
1519
1492
 
1520
1493
  ```irb
1521
-
1494
+ $ ./animal_house_import.rb
1495
+ > basil = FoobaraDemo::CapyCafe::ImportAnimal.run!(animal: { species: :capybara, first_name: "Basil", last_name: "Doe", birthday: "1000-01-01" })
1496
+ ==> <Capybara:3>
1497
+ > FoobaraDemo::CapyCafe::FindCapybara.run!(id: basil).age
1498
+ ==> 1024
1499
+ > FoobaraDemo::CapyCafe::IncrementAge.run!(capybara: basil)
1500
+ ==> <Capybara:3>
1501
+ > FoobaraDemo::CapyCafe::FindCapybara.run!(id: basil).age
1502
+ ==> 1025
1522
1503
  ```
1504
+
1523
1505
  TODO
1524
1506
 
1525
1507
  ### Code Generators
@@ -2,34 +2,124 @@ module Foobara
2
2
  class Command
3
3
  module Concerns
4
4
  module DomainMappers
5
+ class ForgotToDependOnDomainMapperError < Foobara::RuntimeError
6
+ context { mapper_name :string, :required }
7
+
8
+ attr_accessor :mapper
9
+
10
+ def initialize(mapper)
11
+ self.mapper = mapper
12
+ super()
13
+ end
14
+
15
+ def context
16
+ { mapper_name: mapper.name }
17
+ end
18
+
19
+ def message
20
+ "Did you maybe forget to add depends_on #{mapper.name}?"
21
+ end
22
+ end
23
+
24
+ class NoDomainMapperFoundError < Foobara::RuntimeError
25
+ context do
26
+ subcommand_name :string, :required
27
+ to :duck
28
+ end
29
+
30
+ attr_accessor :subcommand, :to
31
+
32
+ def initialize(subcommand, to)
33
+ self.subcommand = subcommand
34
+ self.to = to
35
+
36
+ super()
37
+ end
38
+
39
+ def context
40
+ { subcommand_name: subcommand.name, to: }
41
+ end
42
+
43
+ def message
44
+ "No DomainMapper found that maps to #{subcommand.name} or from its result"
45
+ end
46
+ end
47
+
5
48
  include Concern
6
49
 
7
- def run_mapped_subcommand!(subcommand_class, *args)
8
- unmapped_inputs, has_result_type, result_type =
9
- case args.size
10
- when 1
11
- [args.first]
12
- when 2
13
- [args[1], true, args[0]]
14
- else
15
- # :nocov:
16
- raise ArgumentError,
17
- "Wrong number of arguments: (#{args.size}. Expected 2 or 3 argument."
18
- # :nocov:
19
- end
50
+ def run_mapped_subcommand!(subcommand_class, unmapped_inputs = {}, to = nil)
51
+ mapped_something = false
52
+ no_mapper_found = nil
53
+
54
+ criteria = ->(mapper) { self.class.depends_on?(mapper) }
55
+
56
+ inputs_mapper = self.class.domain.lookup_matching_domain_mapper(
57
+ from: unmapped_inputs,
58
+ to: subcommand_class,
59
+ criteria:,
60
+ strict: true
61
+ )
62
+
63
+ inputs = if inputs_mapper
64
+ mapped_something = true
65
+ run_subcommand!(inputs_mapper, from: unmapped_inputs)
66
+ else
67
+ unmapped_inputs
68
+ end
69
+
70
+ result_mapper = if subcommand_class.result_type
71
+ mapper = self.class.domain.lookup_matching_domain_mapper(
72
+ from: subcommand_class.result_type,
73
+ to:,
74
+ criteria:,
75
+ strict: true
76
+ )
20
77
 
21
- inputs = domain_map!(unmapped_inputs, to: subcommand_class, strict: true)
78
+ no_mapper_found = mapper.nil? && inputs_mapper.nil?
22
79
 
23
- result = run_subcommand!(subcommand_class, inputs)
80
+ mapper
81
+ end
24
82
 
25
- if has_result_type
26
- result_mapper = self.class.domain.foobara_domain_mapper_registry.lookup!(
83
+ result = unless no_mapper_found
84
+ run_subcommand!(subcommand_class, inputs)
85
+ end
86
+
87
+ unless subcommand_class.result_type
88
+ result_mapper = self.class.domain.lookup_matching_domain_mapper(
27
89
  from: result,
28
- to: result_type,
90
+ to:,
91
+ criteria: ->(domain_mapper) { self.class.depends_on?(domain_mapper) },
29
92
  strict: true
30
93
  )
94
+ end
95
+
96
+ if result_mapper
97
+ mapped_something = true
98
+ result = run_subcommand!(result_mapper, from: result)
99
+ end
100
+
101
+ unless mapped_something
102
+ mapper = self.class.domain.lookup_matching_domain_mapper(
103
+ from: unmapped_inputs,
104
+ to: subcommand_class,
105
+ strict: true
106
+ )
107
+
108
+ if mapper
109
+ raise ForgotToDependOnDomainMapperError, mapper
110
+ end
111
+
112
+ mapper = self.class.domain.lookup_matching_domain_mapper(
113
+ from: subcommand_class.result_type,
114
+ to:,
115
+ strict: true
116
+ )
117
+
118
+ if mapper
119
+ raise ForgotToDependOnDomainMapperError, mapper
120
+ end
31
121
 
32
- result = result_mapper.map(result)
122
+ raise NoDomainMapperFoundError.new(subcommand_class, to)
33
123
  end
34
124
 
35
125
  result
@@ -67,7 +67,10 @@ module Foobara
67
67
  end
68
68
  end
69
69
 
70
- def context(*args)
70
+ def context(*args, &block)
71
+ if block_given?
72
+ args = [*args, block]
73
+ end
71
74
  args_size = args.size
72
75
 
73
76
  case args_size
@@ -14,12 +14,6 @@ module Foobara
14
14
  Namespace.global.foobara_add_category(:organization) { is_a?(Module) && foobara_organization? }
15
15
  Namespace.global.foobara_add_category(:domain) { is_a?(Module) && foobara_domain? }
16
16
  end
17
-
18
- def reset_all
19
- if Foobara::DomainMapper.instance_variable_defined?(:@foobara_domain_mappers_to_process)
20
- Foobara::DomainMapper.remove_instance_variable(:@foobara_domain_mappers_to_process)
21
- end
22
- end
23
17
  end
24
18
  end
25
19
  end
@@ -148,27 +148,19 @@ module Foobara
148
148
  value
149
149
  end
150
150
 
151
- mapper = foobara_domain_mapper_registry.lookup(from:, to:, strict:)
151
+ mapper = lookup_matching_domain_mapper(from:, to:, strict:)
152
152
 
153
- mapper&.call(value)
153
+ mapper&.map!(value)
154
154
  end
155
155
 
156
156
  def foobara_domain_map!(value, from: value, to: nil, strict: false)
157
- mapper = foobara_domain_mapper_registry.lookup(from:, to:, strict:)
157
+ mapper = lookup_matching_domain_mapper(from:, to:, strict:)
158
+
158
159
  unless mapper
159
- raise Foobara::DomainMapper::NoDomainMapperFoundError.new(from, to, value:)
160
+ raise Foobara::DomainMapperLookups::NoDomainMapperFoundError.new(from, to, value:)
160
161
  end
161
162
 
162
- mapper.call(value)
163
- end
164
-
165
- def foobara_domain_mapper(mapper)
166
- foobara_domain_mapper_registry(skip_check: true).register(mapper)
167
- end
168
-
169
- def foobara_domain_mapper_registry(skip_check: false)
170
- Foobara::DomainMapper.foobara_process_domain_mappers unless skip_check
171
- @foobara_domain_mapper_registry ||= DomainMapper::Registry.new
163
+ mapper.map!(value)
172
164
  end
173
165
 
174
166
  def foobara_domain_name
@@ -0,0 +1,10 @@
1
+ module Foobara
2
+ class DomainMapper < Foobara::Command
3
+ class << self
4
+ def install!
5
+ Namespace.global.foobara_add_category_for_subclass_of(:domain_mapper, self)
6
+ Domain::DomainModuleExtension.include Foobara::DomainMapperLookups
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,106 @@
1
+ module Foobara
2
+ # TODO: would be nice to inherit from something other than Command. Can we hoist a bunch of common behavior up
3
+ # out of Command into some other class maybe called Service or Runnable or something?
4
+ class DomainMapper < Foobara::Command
5
+ class << self
6
+ def map(value)
7
+ new(from: value).run
8
+ end
9
+
10
+ def map!(value)
11
+ new(from: value).run!
12
+ end
13
+
14
+ # A bit hacky because Command only supports attributes inputs at the moment, ugg.
15
+ def from(...)
16
+ from_type = args_to_type(...)
17
+
18
+ inputs do
19
+ from from_type, :required
20
+ end
21
+ end
22
+
23
+ def to(...)
24
+ result_type = args_to_type(...)
25
+ result(result_type)
26
+ end
27
+
28
+ def from_type
29
+ inputs_type.element_types[:from]
30
+ end
31
+
32
+ def to_type
33
+ result_type
34
+ end
35
+
36
+ def applicable?(from_value, to_value)
37
+ matches?(from_type, from_value) && matches?(to_type, to_value)
38
+ end
39
+
40
+ def matches?(type_indicator, value)
41
+ return true if type_indicator.nil? || value.nil? || type_indicator == value
42
+
43
+ type = object_to_type(type_indicator)
44
+
45
+ return true if type.nil? || type == value
46
+ return true if type.applicable?(value) && type.process_value(value).success?
47
+
48
+ if value.is_a?(Types::Type)
49
+ if !value.registered? && !type.registered?
50
+ value.declaration_data == type.declaration_data
51
+ end
52
+ else
53
+ value_type = object_to_type(value)
54
+
55
+ if value_type
56
+ matches?(type, value_type)
57
+ end
58
+ end
59
+ end
60
+
61
+ # TODO: should this be somewhere more general-purpose?
62
+ def args_to_type(*args, **opts, &block)
63
+ if args.size == 1 && opts.empty? && block.nil?
64
+ object_to_type(args.first)
65
+ else
66
+ domain.foobara_type_from_declaration(*args, **opts, &block)
67
+ end
68
+ end
69
+
70
+ def object_to_type(object)
71
+ if object
72
+ if object.is_a?(::Class)
73
+ if object < Foobara::Model
74
+ object.model_type
75
+ elsif object < Foobara::Command
76
+ object.inputs_type
77
+ else
78
+ domain.foobara_type_from_declaration(object)
79
+ end
80
+ else
81
+ case object
82
+ when Types::Type
83
+ object
84
+ when ::Symbol
85
+ domain.foobara_lookup_type!(object)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ def execute
93
+ map
94
+ end
95
+
96
+ def from
97
+ inputs[:from]
98
+ end
99
+
100
+ def map
101
+ # :nocov:
102
+ raise "subclass responsibility"
103
+ # :nocov:
104
+ end
105
+ end
106
+ end
@@ -1,5 +1,5 @@
1
1
  module Foobara
2
- class DomainMapper
2
+ module DomainMapperLookups
3
3
  class NoDomainMapperFoundError < StandardError
4
4
  attr_accessor :value, :from, :to, :has_value
5
5
 
@@ -25,31 +25,33 @@ module Foobara
25
25
  end
26
26
  end
27
27
 
28
- class Registry
29
- class AmbiguousDomainMapperError < StandardError
30
- attr_accessor :candidates, :from, :to
28
+ class AmbiguousDomainMapperError < StandardError
29
+ attr_accessor :candidates, :from, :to
31
30
 
32
- def initialize(from, to, candidates)
33
- self.to = to
34
- self.from = from
35
- self.candidates = [*candidates].flatten
31
+ def initialize(from, to, candidates)
32
+ self.to = to
33
+ self.from = from
34
+ self.candidates = [*candidates].flatten
36
35
 
37
- super("#{candidates.size} ambiguous candidates found.")
38
- end
36
+ super("#{candidates.size} ambiguous candidates found.")
39
37
  end
38
+ end
40
39
 
41
- def register(mapper)
42
- mappers << mapper
43
- end
40
+ include Concern
44
41
 
45
- def lookup!(from: nil, to: nil, strict: false)
46
- result = lookup(from:, to:, strict:)
42
+ module ClassMethods
43
+ def lookup_matching_domain_mapper!(from: nil, to: nil, strict: false, criteria: nil)
44
+ result = lookup_matching_domain_mapper(from:, to:, strict:, criteria:)
47
45
 
48
46
  result || raise(NoDomainMapperFoundError.new(from, to))
49
47
  end
50
48
 
51
- def lookup(from: nil, to: nil, strict: false)
49
+ def lookup_matching_domain_mapper(from: nil, to: nil, strict: false, criteria: nil)
52
50
  candidates = mappers.select do |mapper|
51
+ if criteria
52
+ next unless criteria.call(mapper)
53
+ end
54
+
53
55
  mapper.applicable?(from, to)
54
56
  end
55
57
 
@@ -63,15 +65,15 @@ module Foobara
63
65
 
64
66
  unless strict
65
67
  if from
66
- lookup(from: nil, to:)
68
+ lookup_matching_domain_mapper(from: nil, to:)
67
69
  elsif to
68
- lookup(from:, to: nil)
70
+ lookup_matching_domain_mapper(from:, to: nil)
69
71
  end
70
72
  end
71
73
  end
72
74
 
73
75
  def mappers
74
- @mappers ||= []
76
+ @mappers ||= foobara_all_domain_mapper
75
77
  end
76
78
  end
77
79
  end
@@ -18,12 +18,11 @@ module Foobara
18
18
  "state_machine",
19
19
  "namespace"
20
20
 
21
- project "domain"
22
-
23
21
  # various components of the foobara framework that have some level of coupling.
24
22
  # for example, Error in common knows about (or could be implemented to know about)
25
23
  # type declarations to expose its context type.
26
- projects "common",
24
+ projects "domain",
25
+ "common",
27
26
  "value",
28
27
  "types",
29
28
  "type_declarations",
@@ -32,6 +31,7 @@ module Foobara
32
31
  "detached_entity",
33
32
  "entity",
34
33
  "command",
34
+ "domain_mapper",
35
35
  "persistence",
36
36
  "in_memory_crud_driver_minimal",
37
37
  "in_memory_crud_driver",
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foobara
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.36
4
+ version: 0.0.38
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miles Georgi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-10 00:00:00.000000000 Z
11
+ date: 2024-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: foobara-util
@@ -193,8 +193,6 @@ files:
193
193
  - projects/detached_entity/src/extensions/type_declarations/handlers/extend_detached_entity_type_declaration/validate_primary_key_references_attribute.rb
194
194
  - projects/domain/lib/foobara/domain.rb
195
195
  - projects/domain/src/domain.rb
196
- - projects/domain/src/domain_mapper.rb
197
- - projects/domain/src/domain_mapper/registry.rb
198
196
  - projects/domain/src/domain_module_extension.rb
199
197
  - projects/domain/src/extensions/foobara.rb
200
198
  - projects/domain/src/global_domain.rb
@@ -204,6 +202,9 @@ files:
204
202
  - projects/domain/src/module_extension.rb
205
203
  - projects/domain/src/organization.rb
206
204
  - projects/domain/src/organization_module_extension.rb
205
+ - projects/domain_mapper/lib/foobara/domain_mapper.rb
206
+ - projects/domain_mapper/src/domain_mapper.rb
207
+ - projects/domain_mapper/src/domain_mapper_lookups.rb
207
208
  - projects/entity/lib/foobara/entity.rb
208
209
  - projects/entity/src/concerns/attribute_helpers.rb
209
210
  - projects/entity/src/concerns/attributes.rb
@@ -393,6 +394,7 @@ require_paths:
393
394
  - "./projects/delegate/lib"
394
395
  - "./projects/detached_entity/lib"
395
396
  - "./projects/domain/lib"
397
+ - "./projects/domain_mapper/lib"
396
398
  - "./projects/entity/lib"
397
399
  - "./projects/enumerated/lib"
398
400
  - "./projects/foobara/lib"
@@ -1,166 +0,0 @@
1
- module Foobara
2
- class DomainMapper
3
- class << self
4
- def call(value)
5
- instance.call(value)
6
- end
7
-
8
- def from(*args)
9
- if args.empty?
10
- @from
11
- else
12
- if args.size > 1
13
- # :nocov:
14
- raise ArgumentError, "only one argument allowed"
15
- # :nocov:
16
- end
17
-
18
- @from = args.first
19
- end
20
- end
21
-
22
- def to(*args)
23
- if args.empty?
24
- @to
25
- else
26
- if args.size > 1
27
- # :nocov:
28
- raise ArgumentError, "only one argument allowed"
29
- # :nocov:
30
- end
31
-
32
- @to = args.first
33
- end
34
- end
35
-
36
- def from_type
37
- return @from_type if defined?(@from_type)
38
-
39
- @from_type = object_to_type(from)
40
- end
41
-
42
- def to_type
43
- return @to_type if defined?(@to_type)
44
-
45
- @to_type = object_to_type(to)
46
- end
47
-
48
- def instance
49
- @instance ||= new
50
- end
51
-
52
- def inherited(subclass)
53
- foobara_domain_mapper_to_process(subclass.instance)
54
-
55
- super
56
- end
57
-
58
- def foobara_domain_mapper_to_process(mapper)
59
- foobara_domain_mappers_to_process << mapper
60
- end
61
-
62
- def foobara_domain_mappers_to_process
63
- @foobara_domain_mappers_to_process ||= []
64
- end
65
-
66
- def foobara_process_domain_mappers
67
- if defined?(@foobara_domain_mappers_to_process)
68
- @foobara_domain_mappers_to_process.each do |mapper|
69
- mapper.domain.foobara_domain_mapper(mapper)
70
- end
71
- remove_instance_variable(:@foobara_domain_mappers_to_process)
72
- end
73
- end
74
-
75
- def domain
76
- candidate = self
77
-
78
- loop do
79
- candidate = Util.module_for(candidate)
80
-
81
- if candidate.nil?
82
- # :nocov:
83
- raise "Domain mapper must be scoped within a domain but #{self.class.name} is not in a domain"
84
- # :nocov:
85
- elsif candidate.foobara_domain?
86
- return candidate
87
- end
88
- end
89
- end
90
-
91
- def matches?(type_indicator, value)
92
- return true if type_indicator.nil? || value.nil? || type_indicator == value
93
-
94
- type = object_to_type(type_indicator)
95
-
96
- return true if type.nil? || type == value
97
- return true if type.applicable?(value) && type.process_value(value).success?
98
-
99
- if value.is_a?(Types::Type)
100
- if !value.registered? && !type.registered?
101
- value.declaration_data == type.declaration_data
102
- end
103
- else
104
- value_type = object_to_type(value)
105
-
106
- if value_type
107
- matches?(type, value_type)
108
- end
109
- end
110
- end
111
-
112
- def object_to_type(object)
113
- if object
114
- if object.is_a?(::Class)
115
- if object < Foobara::Model
116
- object.model_type
117
- elsif object < Foobara::Command
118
- object.inputs_type
119
- else
120
- domain.foobara_type_from_declaration(object)
121
- end
122
- else
123
- case object
124
- when Types::Type
125
- object
126
- when ::Symbol
127
- domain.foobara_lookup_type!(object)
128
- end
129
- end
130
- end
131
- end
132
- end
133
-
134
- def from_type
135
- self.class.from_type
136
- end
137
-
138
- def to_type
139
- self.class.to_type
140
- end
141
-
142
- def call(from_value)
143
- from_value = from_type.process_value!(from_value)
144
-
145
- mapped_value = map(from_value)
146
-
147
- to_type.process_value!(mapped_value)
148
- end
149
-
150
- # TODO: can we make _from_value passed to #initialize instead? That way it doesn't have to be passed around
151
- # between various helper methods
152
- def map(_from_value)
153
- # :nocov:
154
- raise "subclass repsonsibility"
155
- # :nocov:
156
- end
157
-
158
- def applicable?(from_value, to_value)
159
- self.class.matches?(from_type, from_value) && self.class.matches?(to_type, to_value)
160
- end
161
-
162
- def domain
163
- self.class.domain
164
- end
165
- end
166
- end