Skip to content

Commit 86ae879

Browse files
committed
fix: Assembler & Assembly need to Throw
1 parent dbab63a commit 86ae879

File tree

5 files changed

+157
-28
lines changed

5 files changed

+157
-28
lines changed

Sources/AstrojectCore/Assemble/Assembler.swift

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,52 @@ public class Assembler {
1919
/// This property returns the `Container` itself, as it conforms to the `Resolver` protocol.
2020
var resolver: Resolver { container }
2121

22-
/// Initializes an `Assembler` with a given `Container`.
22+
/// Initializes an `Assembler` with a specified `Container`.
2323
///
24-
/// - parameter container: The `Container` instance to assemble dependencies into.
25-
public init(container: Container) {
24+
/// - Parameter container: The `Container` to use for dependency assembly. Defaults to a new `Container` instance.
25+
public init(container: Container = .init()) {
2626
self.container = container
2727
}
2828

29+
/// Initializes an `Assembler` with an array of `Assembly` instances and a `Container`.
30+
///
31+
/// This initializer first calls the designated initializer and then applies the provided assemblies.
32+
///
33+
/// - Parameters:
34+
///   - assemblies: An array of `Assembly` instances to apply.
35+
///   - container: The `Container` to use for dependency assembly. Defaults to a new `Container` instance.
36+
public convenience init(_ assemblies: [Assembly], container: Container = .init()) throws {
37+
self.init(container: container)
38+
try self.run(assemblies: assemblies)
39+
}
40+
41+
/// Initializes an `Assembler` with a single `Assembly` instance and a `Container`.
42+
///
43+
/// This initializer first calls the designated initializer and then applies the provided assembly.
44+
///
45+
/// - Parameters:
46+
///   - assembly: The `Assembly` instance to apply.
47+
///   - container: The `Container` to use for dependency assembly. Defaults to a new `Container` instance.
48+
public convenience init(_ assembly: Assembly, container: Container = .init()) throws {
49+
try self.init([assembly], container: container)
50+
}
51+
2952
/// Applies a single `Assembly` to the `Container`.
3053
///
3154
/// This function applies the `assemble` and `loaded` methods of the provided `Assembly` instance.
3255
///
33-
/// - parameter assembly: The `Assembly` instance to apply.
34-
public func apply(assembly: Assembly) {
35-
run(assemblies: [assembly])
56+
/// - Parameter assembly: The `Assembly` instance to apply.
57+
public func apply(assembly: Assembly) throws {
58+
try run(assemblies: [assembly])
3659
}
3760

3861
/// Applies an array of `Assembly` instances to the `Container`.
3962
///
4063
/// This function applies the `assemble` and `loaded` methods of each `Assembly` instance in the provided array.
4164
///
42-
/// - parameter assemblies: The array of `Assembly` instances to apply.
43-
public func apply(assemblies: [Assembly]) {
44-
run(assemblies: assemblies)
65+
/// - Parameter assemblies: The array of `Assembly` instances to apply.
66+
public func apply(assemblies: [Assembly]) throws {
67+
try run(assemblies: assemblies)
4568
}
4669

4770
/// Runs the assembly process for an array of `Assembly` instances.
@@ -51,12 +74,16 @@ public class Assembler {
5174
/// The `assemble` method registers dependencies in the `Container`,
5275
/// and the `loaded` method performs any post-registration setup.
5376
///
54-
/// - parameter assemblies: The array of `Assembly` instances to run.
55-
func run(assemblies: [Assembly]) {
77+
/// - Parameter assemblies: The array of `Assembly` instances to run.
78+
func run(assemblies: [Assembly]) throws {
5679
// Iterate through the assemblies and call the assemble method on each one to register the dependencies.
57-
assemblies.forEach { $0.assemble(container: container) }
80+
try assemblies.forEach {
81+
try $0.assemble(container: container)
82+
}
5883
// Iterate through the assemblies again and call the loaded
5984
// method on each one to perform any post-registration configuration.
60-
assemblies.forEach { $0.loaded(resolver: resolver) }
85+
try assemblies.forEach {
86+
try $0.loaded(resolver: resolver)
87+
}
6188
}
6289
}

Sources/AstrojectCore/Assemble/Assembly.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public protocol Assembly {
1919
/// Implementations should use the `Container` to register factories and other configuration.
2020
///
2121
/// - parameter container: The `Container` instance to configure.
22-
func assemble(container: Container)
22+
func assemble(container: Container) throws
2323

2424
/// Called after the assembly has been loaded into the `Container`.
2525
///
@@ -28,7 +28,7 @@ public protocol Assembly {
2828
/// the resolved dependencies.
2929
///
3030
/// - parameter resolver: The `Resolver` instance providing access to the assembled dependencies.
31-
func loaded(resolver: Resolver)
31+
func loaded(resolver: Resolver) throws
3232
}
3333

3434
public extension Assembly {
@@ -38,5 +38,5 @@ public extension Assembly {
3838
/// that do not require post-registration setup to omit implementing the `loaded` function.
3939
///
4040
/// - parameter resolver: The `Resolver` instance (unused in the default implementation).
41-
func loaded(resolver: Resolver) {}
41+
func loaded(resolver: Resolver) throws {}
4242
}

Tests/CoreTests/AssemblyTests.swift

Lines changed: 97 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1+
//  AssemblyTests.swift
2+
//  CoreTests
13
//
2-
// AssemblyTests.swift
3-
// CoreTests
4-
//
5-
// Created by Porter McGary on 3/4/25.
4+
//  Created by Porter McGary on 3/4/25.
65
//
76

87
import Testing
@@ -18,29 +17,118 @@ struct AssemblyTests {
1817
}
1918

2019
@Test("Apply One Assembly")
21-
func applySingleAssembly() {
20+
func applySingleAssembly() throws {
2221
let container = Container()
2322
let assembler = Assembler(container: container)
2423
let assembly = MockAssembly()
2524

26-
assembler.apply(assembly: assembly)
27-
25+
try assembler.apply(assembly: assembly)
2826
#expect(assembly.assembleCalled)
2927
#expect(assembly.loadedCalled)
3028
}
3129

3230
@Test("Apply Multiple Assemblies")
33-
func applyMultipleAssemblies() {
31+
func applyMultipleAssemblies() throws {
3432
let container = Container()
3533
let assembler = Assembler(container: container)
3634
let assembly1 = MockAssembly()
3735
let assembly2 = MockAssembly()
3836

39-
assembler.apply(assemblies: [assembly1, assembly2])
37+
try assembler.apply(assemblies: [assembly1, assembly2])
4038

4139
#expect(assembly1.assembleCalled)
4240
#expect(assembly1.loadedCalled)
4341
#expect(assembly2.assembleCalled)
4442
#expect(assembly2.loadedCalled)
4543
}
44+
45+
@Test("Assembly Initializers")
46+
func assemblyInitializers() throws {
47+
let container1 = Container()
48+
let assembly1 = MockAssembly()
49+
let assembler1 = try Assembler(assembly1, container: container1)
50+
#expect(assembler1.container === container1)
51+
#expect(assembly1.assembleCalled)
52+
#expect(assembly1.loadedCalled)
53+
54+
let container2 = Container()
55+
let assembly2 = MockAssembly()
56+
let assembly3 = MockAssembly()
57+
let assemblies = [assembly2, assembly3]
58+
let assembler2 = try Assembler(assemblies, container: container2)
59+
#expect(assembler2.container === container2)
60+
#expect(assembly2.assembleCalled)
61+
#expect(assembly2.loadedCalled)
62+
#expect(assembly3.assembleCalled)
63+
#expect(assembly3.loadedCalled)
64+
}
65+
66+
@Test("Assemblies are applied in order")
67+
func assembliesAppliedInOrder() throws {
68+
let container = Container()
69+
let assembler = Assembler(container: container)
70+
var order = 0
71+
var order1 = 0
72+
var order2 = 0
73+
var order3 = 0
74+
var order4 = 0
75+
let assembly1 = MockAssembly()
76+
let assembly2 = MockAssembly()
77+
assembly1.whenAssemble = {
78+
order += 1
79+
order1 = order
80+
}
81+
assembly2.whenAssemble = {
82+
order += 1
83+
order2 = order
84+
}
85+
assembly1.whenLoaded = {
86+
order += 1
87+
order3 = order
88+
}
89+
assembly2.whenLoaded = {
90+
order += 1
91+
order4 = order
92+
}
93+
94+
try assembler.apply(assemblies: [assembly1, assembly2])
95+
96+
#expect(order1 < order2)
97+
#expect(order3 < order4)
98+
}
99+
100+
@Test("Empty Assemblies Array")
101+
func emptyAssembliesArray() throws {
102+
let container = Container()
103+
let assembler = Assembler(container: container)
104+
try assembler.apply(assemblies: []) // Should not throw
105+
}
106+
107+
@Test("Assembly Throwing Errors")
108+
func assemblyThrowingErrors() throws {
109+
let container = Container()
110+
let assembler = Assembler(container: container)
111+
let assembly = MockAssembly()
112+
assembly.whenAssemble = {
113+
throw MockError()
114+
}
115+
116+
#expect(throws: MockError.self) {
117+
try assembler.apply(assembly: assembly)
118+
}
119+
}
120+
121+
@Test("Assembly Throwing Errors After Assembled")
122+
func assemblyThrowingErrorsAfter() throws {
123+
let container = Container()
124+
let assembler = Assembler(container: container)
125+
let assembly = MockAssembly()
126+
assembly.whenLoaded = {
127+
throw MockError()
128+
}
129+
130+
#expect(throws: MockError.self) {
131+
try assembler.apply(assembly: assembly)
132+
}
133+
}
46134
}

Tests/CoreTests/Mocks/MockAssembly.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,18 @@ import AstrojectCore
99

1010
// Mock Assembly for testing
1111
class MockAssembly: Assembly {
12-
var assembleCalled = false
1312
var loadedCalled = false
13+
var assembleCalled = false
14+
var whenAssemble: () throws -> Void = {}
15+
var whenLoaded: () throws -> Void = {}
1416

15-
func assemble(container: Container) {
17+
func assemble(container: Container) throws {
1618
assembleCalled = true
19+
try whenAssemble()
1720
}
1821

19-
func loaded(resolver: Resolver) {
22+
func loaded(resolver: Resolver) throws {
2023
loadedCalled = true
24+
try whenLoaded()
2125
}
2226
}

Tests/CoreTests/Mocks/MockError.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// MockError.swift
3+
// Astroject
4+
//
5+
// Created by Porter McGary on 5/20/25.
6+
//
7+
8+
import Foundation
9+
10+
struct MockError: Error {}

0 commit comments

Comments
 (0)