Skip to content

Commit

Permalink
Rename to DuplicateRegistrationDetector
Browse files Browse the repository at this point in the history
  • Loading branch information
bradfol committed Nov 12, 2024
1 parent 3dc2598 commit 74db318
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 41 deletions.
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ The following commands are available:

By default Knit generates named getters for its type safety.

## Duplicate Detection
## Duplicate Registration Detection

As an app scales, so do the number of DI registrations. It is important to detect duplicate registrations because by
default Swinject will overwrite early registrations with any duplicates.
Expand All @@ -87,23 +87,23 @@ This is always on and automatic.

Knit only parses the assemblies in a single module at a time so it is not able to detect duplicates across modules
during parsing/compile time.
Instead Knit provides a `DuplicateDetection` class, which is a Swinject Behavior.
`DuplicateDetection` allows for runtime detection of all registrations made to a single Container (DI graph),
Instead Knit provides a `DuplicateRegistrationDetector` class, which is a Swinject Behavior.
`DuplicateRegistrationDetector` allows for runtime detection of all registrations made to a single Container (DI graph),
regardless of which modules those registrations came from.
This adds safety from duplicate registrations to your whole DI graph.

An instance of DuplicateDetection should be added to the Container to make use of this feature. Configuration steps:
An instance of DuplicateRegistrationDetector should be added to the Container to make use of this feature. Configuration steps:

1. Create an instance of `DuplicateDetection`.
1. Create an instance of `DuplicateRegistrationDetector`.
1. Provide that instance to your Container (ScopedModuleAssembler/ModuleAssembler/Assembler also allow behaviors to be
passed into their initializers).
1. Perform the registration phase of your Container setup. If you are using ScopedModuleAssembler/ModuleAssembler then the registration phase will be complete after the initialer returns.
1. Check the `detectedKeys` property of your `DuplicateDetection` instance for duplicates.
1. Check the `detectedKeys` property of your `DuplicateRegistrationDetector` instance for duplicates.

Note that there are also helpers on `DuplicateDetection.Key` and `Array<DuplicateDetection.Key>`
Note that there are also helpers on `DuplicateRegistrationDetector.Key` and `Array<DuplicateRegistrationDetector.Key>`
to help with creating reports/error messages that are easier to read.

`DuplicateDetection` also provides a `duplicateWasDetected` closure hook if you would like to be informed of each
`DuplicateRegistrationDetector` also provides a `duplicateWasDetected` closure hook if you would like to be informed of each
duplicate at the moment that duplicate is registered.

---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import Swinject

public final class DuplicateDetection {
public final class DuplicateRegistrationDetector {

/// If a duplicate registration is detected, the `Key` describing that registration will be provided to this closure.
/// The closure can be called multiple times, once for each duplicate found.
Expand Down Expand Up @@ -34,7 +34,7 @@ public final class DuplicateDetection {

// MARK: -

extension DuplicateDetection: Behavior {
extension DuplicateRegistrationDetector: Behavior {

public func container<Type, Service>(
_ container: Container,
Expand Down Expand Up @@ -63,7 +63,7 @@ extension DuplicateDetection: Behavior {

// MARK: -

extension DuplicateDetection.Key: Hashable, Equatable {
extension DuplicateRegistrationDetector.Key: Hashable, Equatable {

public func hash(into hasher: inout Hasher) {
ObjectIdentifier(serviceType).hash(into: &hasher)
Expand All @@ -81,7 +81,7 @@ extension DuplicateDetection.Key: Hashable, Equatable {

// MARK: -

extension DuplicateDetection.Key: CustomStringConvertible {
extension DuplicateRegistrationDetector.Key: CustomStringConvertible {

// Provide a more structured string description of the key, useful for logging error messages
public var description: String {
Expand All @@ -98,7 +98,7 @@ extension DuplicateDetection.Key: CustomStringConvertible {

// MARK: -

extension Array where Element == DuplicateDetection.Key {
extension Array where Element == DuplicateRegistrationDetector.Key {

public var duplicatesDescription: String {
guard count > 0 else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
@testable import Knit
import XCTest

final class DuplicateDetectionTests: XCTestCase {
final class DuplicateRegistrationDetectorTests: XCTestCase {

func testBasicDetection() throws {
var reportedDuplicates = [DuplicateDetection.Key]()
let duplicateDetection = DuplicateDetection(duplicateWasDetected: { key in
var reportedDuplicates = [DuplicateRegistrationDetector.Key]()
let duplicateRegistrationDetector = DuplicateRegistrationDetector(duplicateWasDetected: { key in
reportedDuplicates.append(key)
})
let container = Container(
behaviors: [duplicateDetection]
behaviors: [duplicateRegistrationDetector]
)

XCTAssertEqual(reportedDuplicates.count, 0)
Expand All @@ -32,12 +32,12 @@ final class DuplicateDetectionTests: XCTestCase {
}

func testNames() throws {
var reportedDuplicates = [DuplicateDetection.Key]()
let duplicateDetection = DuplicateDetection(duplicateWasDetected: { key in
var reportedDuplicates = [DuplicateRegistrationDetector.Key]()
let duplicateRegistrationDetector = DuplicateRegistrationDetector(duplicateWasDetected: { key in
reportedDuplicates.append(key)
})
let container = Container(
behaviors: [duplicateDetection]
behaviors: [duplicateRegistrationDetector]
)

XCTAssertEqual(reportedDuplicates.count, 0)
Expand All @@ -55,12 +55,12 @@ final class DuplicateDetectionTests: XCTestCase {
}

func testArguments() throws {
var reportedDuplicates = [DuplicateDetection.Key]()
let duplicateDetection = DuplicateDetection(duplicateWasDetected: { key in
var reportedDuplicates = [DuplicateRegistrationDetector.Key]()
let duplicateRegistrationDetector = DuplicateRegistrationDetector(duplicateWasDetected: { key in
reportedDuplicates.append(key)
})
let container = Container(
behaviors: [duplicateDetection]
behaviors: [duplicateRegistrationDetector]
)

XCTAssertEqual(reportedDuplicates.count, 0)
Expand All @@ -78,12 +78,12 @@ final class DuplicateDetectionTests: XCTestCase {
}

func testNoDuplicates() throws {
var reportedDuplicates = [DuplicateDetection.Key]()
let duplicateDetection = DuplicateDetection(duplicateWasDetected: { key in
var reportedDuplicates = [DuplicateRegistrationDetector.Key]()
let duplicateRegistrationDetector = DuplicateRegistrationDetector(duplicateWasDetected: { key in
reportedDuplicates.append(key)
})
let container = Container(
behaviors: [duplicateDetection]
behaviors: [duplicateRegistrationDetector]
)

container.register(String.self, factory: { _ in "" })
Expand All @@ -96,17 +96,17 @@ final class DuplicateDetectionTests: XCTestCase {
// A parent container is allowed to contain the same registration key as a child container
// This is not a duplicate but a "shadow" registration

var reportedDuplicates = [DuplicateDetection.Key]()
var reportedDuplicates = [DuplicateRegistrationDetector.Key]()

let parentDuplicateDetection = DuplicateDetection(duplicateWasDetected: { key in
let parentDuplicateRegistrationDetector = DuplicateRegistrationDetector(duplicateWasDetected: { key in
reportedDuplicates.append(key)
})
let parentContainer = Container(behaviors: [parentDuplicateDetection])
let parentContainer = Container(behaviors: [parentDuplicateRegistrationDetector])

let childDuplicateDetection = DuplicateDetection(duplicateWasDetected: { key in
let childDuplicateRegistrationDetector = DuplicateRegistrationDetector(duplicateWasDetected: { key in
reportedDuplicates.append(key)
})
let childContainer = Container(parent: parentContainer, behaviors: [childDuplicateDetection])
let childContainer = Container(parent: parentContainer, behaviors: [childDuplicateRegistrationDetector])

parentContainer.register(String.self, factory: { _ in "parent" })
childContainer.register(String.self, factory: { _ in "child" })
Expand All @@ -116,24 +116,24 @@ final class DuplicateDetectionTests: XCTestCase {

func testTypeForwarding() throws {
// A forwarded type (`.implements()`) should not cause a duplicate registration
let duplicateDetection = DuplicateDetection()
let container = Container(behaviors: [duplicateDetection])
let duplicateRegistrationDetector = DuplicateRegistrationDetector()
let container = Container(behaviors: [duplicateRegistrationDetector])

XCTAssertEqual(duplicateDetection.detectedKeys.count, 0)
XCTAssertEqual(duplicateRegistrationDetector.detectedKeys.count, 0)
container.register(String.self, factory: { _ in "string"} )
.implements((any StringProtocol).self)
XCTAssertEqual(duplicateDetection.detectedKeys.count, 0)
XCTAssertEqual(duplicateRegistrationDetector.detectedKeys.count, 0)

// Registering `Substring` does not cause a duplicate
let substringEntry = container.register(Substring.self, factory: { _ in "substring"} )
XCTAssertEqual(duplicateDetection.detectedKeys.count, 0)
XCTAssertEqual(duplicateRegistrationDetector.detectedKeys.count, 0)
// However forwarding to the same type twice still results in a duplicate
substringEntry.implements((any StringProtocol).self)
XCTAssertEqual(duplicateDetection.detectedKeys.count, 1)
XCTAssertEqual(duplicateRegistrationDetector.detectedKeys.count, 1)
}

func testCustomStringDescription() throws {
assertCustomStringDescription(key: DuplicateDetection.Key(
assertCustomStringDescription(key: DuplicateRegistrationDetector.Key(
serviceType: String.self,
argumentsType: ((Resolver)).self,
name: nil
Expand All @@ -146,7 +146,7 @@ final class DuplicateDetectionTests: XCTestCase {
"""
)

assertCustomStringDescription(key: DuplicateDetection.Key(
assertCustomStringDescription(key: DuplicateRegistrationDetector.Key(
serviceType: Int.self,
argumentsType: (Resolver, Bool).self,
name: nil
Expand All @@ -159,7 +159,7 @@ final class DuplicateDetectionTests: XCTestCase {
"""
)

assertCustomStringDescription(key: DuplicateDetection.Key(
assertCustomStringDescription(key: DuplicateRegistrationDetector.Key(
serviceType: String.self,
argumentsType: ((Resolver)).self,
name: "namedRegistration"
Expand All @@ -178,7 +178,7 @@ final class DuplicateDetectionTests: XCTestCase {
// MARK: -

private func assertCustomStringDescription(
key: DuplicateDetection.Key,
key: DuplicateRegistrationDetector.Key,
expectedDescription: String,
file: StaticString = #filePath,
line: UInt = #line
Expand Down

0 comments on commit 74db318

Please sign in to comment.