Skip to content

Commit

Permalink
Merge pull request #4 from twof/recursive-relations
Browse files Browse the repository at this point in the history
Infinitely recursive relations
  • Loading branch information
twof authored Oct 27, 2018
2 parents 19a886c + 3938aff commit eb50c01
Show file tree
Hide file tree
Showing 5 changed files with 462 additions and 56 deletions.
153 changes: 143 additions & 10 deletions Sources/CrudRouter/CrudChildrenController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,163 @@ public struct CrudChildrenController<ChildT: Model & Content, ParentT: Model & C
public typealias ChildType = ChildT

public var children: KeyPath<ParentT, Children<ParentT, ChildT>>
let basePath: [PathComponentsRepresentable]
let path: [PathComponentsRepresentable]
let router: Router
let activeMethods: Set<ChildrenRouterMethod>

init(
childrenRelation: KeyPath<ParentT,
Children<ParentT, ChildT>>,
basePath: [PathComponentsRepresentable],
childrenRelation: KeyPath<ParentT, Children<ParentT, ChildT>>,
path: [PathComponentsRepresentable],
router: Router,
activeMethods: Set<ChildrenRouterMethod>
) {
let adjustedPath = path.adjustedPath(for: ChildType.self)

self.children = childrenRelation
self.basePath = basePath
self.path = adjustedPath
self.path = path
self.router = router
self.activeMethods = activeMethods
}
}

extension CrudChildrenController {
public func crud<ParentType>(
at path: PathComponentsRepresentable...,
parent relation: KeyPath<ChildType, Parent<ChildType, ParentType>>,
_ either: OnlyExceptEither<ParentRouterMethod> = .only([.read, .update]),
relationConfiguration: ((CrudParentController<ChildType, ParentType>) throws -> Void)?=nil
) throws where
ParentType: Model & Content,
ChildType.Database == ParentType.Database,
ParentType.ID: Parameter {
let baseIdPath = self.path.appending(ChildType.ID.parameter)
let adjustedPath = path.adjustedPath(for: ParentType.self)

let fullPath = baseIdPath.appending(adjustedPath)


let allMethods: Set<ParentRouterMethod> = Set([.read, .update])
let controller: CrudParentController<ChildType, ParentType>

switch either {
case .only(let methods):
controller = CrudParentController(relation: relation, path: fullPath, router: self.router, activeMethods: Set(methods))
case .except(let methods):
controller = CrudParentController(relation: relation, path: fullPath, router: self.router, activeMethods: allMethods.subtracting(Set(methods)))
}

try controller.boot(router: self.router)

try relationConfiguration?(controller)
}
}


// MARK: ChildController methods
extension CrudChildrenController {
public func crud<ChildChildType>(
at path: PathComponentsRepresentable...,
children relation: KeyPath<ChildType, Children<ChildType, ChildChildType>>,
_ either: OnlyExceptEither<ChildrenRouterMethod> = .only([.read, .readAll, .create, .update, .delete]),
relationConfiguration: ((CrudChildrenController<ChildChildType, ChildType>) throws -> Void)?=nil
) throws where
ChildChildType: Model & Content,
ChildType.Database == ChildChildType.Database,
ChildChildType.ID: Parameter {
let baseIdPath = self.path.appending(ChildType.ID.parameter)
let adjustedPath = path.adjustedPath(for: ChildType.self)

let fullPath = baseIdPath.appending(adjustedPath)

let allMethods: Set<ChildrenRouterMethod> = Set([.read, .update])
let controller: CrudChildrenController<ChildChildType, ChildType>

switch either {
case .only(let methods):
controller = CrudChildrenController<ChildChildType, ChildType>(childrenRelation: relation, path: fullPath, router: self.router, activeMethods: Set(methods))
case .except(let methods):
controller = CrudChildrenController<ChildChildType, ChildType>(childrenRelation: relation, path: fullPath, router: self.router, activeMethods: allMethods.subtracting(Set(methods)))
}

try controller.boot(router: self.router)

try relationConfiguration?(controller)
}
}

// MARK: SiblingController methods
public extension CrudChildrenController {
public func crud<ChildChildType, ThroughType>(
at path: PathComponentsRepresentable...,
siblings relation: KeyPath<ChildType, Siblings<ChildType, ChildChildType, ThroughType>>,
_ either: OnlyExceptEither<ModifiableSiblingRouterMethod> = .only([.read, .readAll, .create, .update, .delete]),
relationConfiguration: ((CrudSiblingsController<ChildChildType, ChildType, ThroughType>) throws -> Void)?=nil
) throws where
ChildChildType: Content,
ChildType.Database == ThroughType.Database,
ChildChildType.ID: Parameter,
ThroughType: ModifiablePivot,
ThroughType.Database: JoinSupporting,
ThroughType.Database == ChildChildType.Database,
ThroughType.Left == ChildType,
ThroughType.Right == ChildChildType {
let baseIdPath = self.path.appending(ChildType.ID.parameter)
let adjustedPath = path.adjustedPath(for: ChildChildType.self)

let fullPath = baseIdPath.appending(adjustedPath)

let allMethods: Set<ModifiableSiblingRouterMethod> = Set([.read, .readAll, .create, .update, .delete])
let controller: CrudSiblingsController<ChildChildType, ChildType, ThroughType>

switch either {
case .only(let methods):
controller = CrudSiblingsController(siblingRelation: relation, path: fullPath, router: self.router, activeMethods: Set(methods))
case .except(let methods):
controller = CrudSiblingsController(siblingRelation: relation, path: fullPath, router: self.router, activeMethods: allMethods.subtracting(Set(methods)))
}

try controller.boot(router: self.router)

try relationConfiguration?(controller)
}

public func crud<ChildChildType, ThroughType>(
at path: PathComponentsRepresentable...,
siblings relation: KeyPath<ChildType, Siblings<ChildType, ChildChildType, ThroughType>>,
_ either: OnlyExceptEither<ModifiableSiblingRouterMethod> = .only([.read, .readAll, .create, .update, .delete]),
relationConfiguration: ((CrudSiblingsController<ChildChildType, ChildType, ThroughType>) throws -> Void)?=nil
) throws where
ChildChildType: Content,
ChildType.Database == ThroughType.Database,
ChildChildType.ID: Parameter,
ThroughType: ModifiablePivot,
ThroughType.Database: JoinSupporting,
ThroughType.Database == ChildChildType.Database,
ThroughType.Right == ChildType,
ThroughType.Left == ChildChildType {
let baseIdPath = self.path.appending(ChildType.ID.parameter)
let adjustedPath = path.adjustedPath(for: ChildChildType.self)

let fullPath = baseIdPath.appending(adjustedPath)

let allMethods: Set<ModifiableSiblingRouterMethod> = Set([.read, .readAll, .create, .update, .delete])
let controller: CrudSiblingsController<ChildChildType, ChildType, ThroughType>

switch either {
case .only(let methods):
controller = CrudSiblingsController(siblingRelation: relation, path: fullPath, router: self.router, activeMethods: Set(methods))
case .except(let methods):
controller = CrudSiblingsController(siblingRelation: relation, path: fullPath, router: self.router, activeMethods: allMethods.subtracting(Set(methods)))
}

try controller.boot(router: self.router)

try relationConfiguration?(controller)
}
}

extension CrudChildrenController: RouteCollection {
public func boot(router: Router) throws {
let parentPath = self.basePath.appending(self.path)
let parentIdPath = self.basePath.appending(self.path).appending(ParentType.ID.parameter)
let parentPath = self.path
let parentIdPath = self.path.appending(ParentType.ID.parameter)

self.activeMethods.forEach {
$0.register(
Expand Down
36 changes: 28 additions & 8 deletions Sources/CrudRouter/CrudController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,23 @@ extension CrudController {
ModelType.Database == ParentType.Database,
ParentType.ID: Parameter {
let baseIdPath = self.path.appending(ModelType.ID.parameter)
let adjustedPath = path.adjustedPath(for: ParentType.self)

let fullPath = baseIdPath.appending(adjustedPath)

let allMethods: Set<ParentRouterMethod> = Set([.read, .update])
let controller: CrudParentController<ModelType, ParentType>

switch either {
case .only(let methods):
controller = CrudParentController(relation: relation, basePath: baseIdPath, path: path, activeMethods: Set(methods))
controller = CrudParentController(relation: relation, path: fullPath, router: self.router, activeMethods: Set(methods))
case .except(let methods):
controller = CrudParentController(relation: relation, basePath: baseIdPath, path: path, activeMethods: allMethods.subtracting(Set(methods)))
controller = CrudParentController(relation: relation, path: fullPath, router: self.router, activeMethods: allMethods.subtracting(Set(methods)))
}

try controller.boot(router: self.router)

try relationConfiguration?(controller)
}
}

Expand All @@ -56,18 +61,23 @@ extension CrudController {
ModelType.Database == ChildType.Database,
ChildType.ID: Parameter {
let baseIdPath = self.path.appending(ModelType.ID.parameter)
let adjustedPath = path.adjustedPath(for: ChildType.self)

let fullPath = baseIdPath.appending(adjustedPath)

let allMethods: Set<ChildrenRouterMethod> = Set([.read, .update])
let controller: CrudChildrenController<ChildType, ModelType>

switch either {
case .only(let methods):
controller = CrudChildrenController(childrenRelation: relation, basePath: baseIdPath, path: path, activeMethods: Set(methods))
controller = CrudChildrenController(childrenRelation: relation, path: fullPath, router: self.router, activeMethods: Set(methods))
case .except(let methods):
controller = CrudChildrenController(childrenRelation: relation, basePath: baseIdPath, path: path, activeMethods: allMethods.subtracting(Set(methods)))
controller = CrudChildrenController(childrenRelation: relation, path: fullPath, router: self.router, activeMethods: allMethods.subtracting(Set(methods)))
}

try controller.boot(router: self.router)

try relationConfiguration?(controller)
}
}

Expand All @@ -88,18 +98,23 @@ public extension CrudController {
ThroughType.Left == ModelType,
ThroughType.Right == ChildType {
let baseIdPath = self.path.appending(ModelType.ID.parameter)
let adjustedPath = path.adjustedPath(for: ChildType.self)

let fullPath = baseIdPath.appending(adjustedPath)

let allMethods: Set<ModifiableSiblingRouterMethod> = Set([.read, .readAll, .create, .update, .delete])
let controller: CrudSiblingsController<ChildType, ModelType, ThroughType>

switch either {
case .only(let methods):
controller = CrudSiblingsController(siblingRelation: relation, basePath: baseIdPath, path: path, activeMethods: Set(methods))
controller = CrudSiblingsController(siblingRelation: relation, path: fullPath, router: self.router, activeMethods: Set(methods))
case .except(let methods):
controller = CrudSiblingsController(siblingRelation: relation, basePath: baseIdPath, path: path, activeMethods: allMethods.subtracting(Set(methods)))
controller = CrudSiblingsController(siblingRelation: relation, path: fullPath, router: self.router, activeMethods: allMethods.subtracting(Set(methods)))
}

try controller.boot(router: self.router)

try relationConfiguration?(controller)
}

public func crud<ChildType, ThroughType>(
Expand All @@ -117,18 +132,23 @@ public extension CrudController {
ThroughType.Right == ModelType,
ThroughType.Left == ChildType {
let baseIdPath = self.path.appending(ModelType.ID.parameter)
let adjustedPath = path.adjustedPath(for: ChildType.self)

let fullPath = baseIdPath.appending(adjustedPath)

let allMethods: Set<ModifiableSiblingRouterMethod> = Set([.read, .readAll, .create, .update, .delete])
let controller: CrudSiblingsController<ChildType, ModelType, ThroughType>

switch either {
case .only(let methods):
controller = CrudSiblingsController(siblingRelation: relation, basePath: baseIdPath, path: path, activeMethods: Set(methods))
controller = CrudSiblingsController(siblingRelation: relation, path: fullPath, router: self.router, activeMethods: Set(methods))
case .except(let methods):
controller = CrudSiblingsController(siblingRelation: relation, basePath: baseIdPath, path: path, activeMethods: allMethods.subtracting(Set(methods)))
controller = CrudSiblingsController(siblingRelation: relation, path: fullPath, router: self.router, activeMethods: allMethods.subtracting(Set(methods)))
}

try controller.boot(router: self.router)

try relationConfiguration?(controller)
}
}

Expand Down
Loading

0 comments on commit eb50c01

Please sign in to comment.