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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +36 -54
- data/projects/command/src/concerns/domain_mappers.rb +109 -19
- data/projects/common/src/error.rb +4 -1
- data/projects/domain/lib/foobara/domain.rb +0 -6
- data/projects/domain/src/domain_module_extension.rb +6 -14
- data/projects/domain_mapper/lib/foobara/domain_mapper.rb +10 -0
- data/projects/domain_mapper/src/domain_mapper.rb +106 -0
- data/projects/{domain/src/domain_mapper/registry.rb → domain_mapper/src/domain_mapper_lookups.rb} +21 -19
- data/projects/foobara/lib/foobara/all.rb +3 -3
- metadata +6 -4
- data/projects/domain/src/domain_mapper.rb +0 -166
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc8a84b48b8479025a63b5ae393436a12826aa19291bf8f60d453e891e5bc433
|
4
|
+
data.tar.gz: 6a0b086f7b5921f2301c566f53c8bc5b2d22f59693c9349adbeb727d49323cc4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
1390
|
-
|
1391
|
-
|
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
|
-
|
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
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
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
|
-
|
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::
|
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
|
-
|
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,
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
78
|
+
no_mapper_found = mapper.nil? && inputs_mapper.nil?
|
22
79
|
|
23
|
-
|
80
|
+
mapper
|
81
|
+
end
|
24
82
|
|
25
|
-
|
26
|
-
|
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
|
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
|
-
|
122
|
+
raise NoDomainMapperFoundError.new(subcommand_class, to)
|
33
123
|
end
|
34
124
|
|
35
125
|
result
|
@@ -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 =
|
151
|
+
mapper = lookup_matching_domain_mapper(from:, to:, strict:)
|
152
152
|
|
153
|
-
mapper&.
|
153
|
+
mapper&.map!(value)
|
154
154
|
end
|
155
155
|
|
156
156
|
def foobara_domain_map!(value, from: value, to: nil, strict: false)
|
157
|
-
mapper =
|
157
|
+
mapper = lookup_matching_domain_mapper(from:, to:, strict:)
|
158
|
+
|
158
159
|
unless mapper
|
159
|
-
raise Foobara::
|
160
|
+
raise Foobara::DomainMapperLookups::NoDomainMapperFoundError.new(from, to, value:)
|
160
161
|
end
|
161
162
|
|
162
|
-
mapper.
|
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,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
|
data/projects/{domain/src/domain_mapper/registry.rb → domain_mapper/src/domain_mapper_lookups.rb}
RENAMED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Foobara
|
2
|
-
|
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
|
29
|
-
|
30
|
-
attr_accessor :candidates, :from, :to
|
28
|
+
class AmbiguousDomainMapperError < StandardError
|
29
|
+
attr_accessor :candidates, :from, :to
|
31
30
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
def initialize(from, to, candidates)
|
32
|
+
self.to = to
|
33
|
+
self.from = from
|
34
|
+
self.candidates = [*candidates].flatten
|
36
35
|
|
37
|
-
|
38
|
-
end
|
36
|
+
super("#{candidates.size} ambiguous candidates found.")
|
39
37
|
end
|
38
|
+
end
|
40
39
|
|
41
|
-
|
42
|
-
mappers << mapper
|
43
|
-
end
|
40
|
+
include Concern
|
44
41
|
|
45
|
-
|
46
|
-
|
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
|
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
|
-
|
68
|
+
lookup_matching_domain_mapper(from: nil, to:)
|
67
69
|
elsif to
|
68
|
-
|
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 "
|
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.
|
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-
|
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
|