foobara 0.0.36 → 0.0.38

Sign up to get free protection for your applications and to get access to all the features.
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