From 5e67fd6c68842c2cda1acc92236956cb4b9ae3fb Mon Sep 17 00:00:00 2001 From: Siemen Sikkema Date: Thu, 21 Jun 2018 09:48:12 +0200 Subject: [PATCH 1/3] Don't use private container. Needs to be tested. - API docs --- .../Flash/Extensions/Container+Flash.swift | 16 ++++ Sources/Flash/Extensions/Future+Flash.swift | 12 ++- Sources/Flash/Extensions/Response+Flash.swift | 10 --- .../Flash/Extensions/SubContainer+Flash.swift | 10 --- .../Flash/Middlewares/FlashMiddleware.swift | 40 ++++++---- Sources/Flash/Models/Flash.swift | 19 +++-- Sources/Flash/Models/FlashContainer.swift | 14 +--- Sources/Flash/Providers/FlashProvider.swift | 5 ++ Sources/Flash/Tags/FlashTag.swift | 79 +++++++++---------- 9 files changed, 104 insertions(+), 101 deletions(-) create mode 100644 Sources/Flash/Extensions/Container+Flash.swift delete mode 100644 Sources/Flash/Extensions/Response+Flash.swift delete mode 100644 Sources/Flash/Extensions/SubContainer+Flash.swift diff --git a/Sources/Flash/Extensions/Container+Flash.swift b/Sources/Flash/Extensions/Container+Flash.swift new file mode 100644 index 0000000..b472ed2 --- /dev/null +++ b/Sources/Flash/Extensions/Container+Flash.swift @@ -0,0 +1,16 @@ +import Vapor + +extension Container { + /// Add a flash message to the `Response` of this `Future`. + /// + /// - Parameters: + /// - type: The type of the flash message (e.g. `.success` or `.error`). + /// - message: The message to display. + /// - Returns: The `Response` with the flash message added. + public func flash(_ type: Flash.Kind, _ message: String) -> Self { + if let container: FlashContainer = try? make() { + container.flashes.append(.init(type, message)) + } + return self + } +} diff --git a/Sources/Flash/Extensions/Future+Flash.swift b/Sources/Flash/Extensions/Future+Flash.swift index 14251bc..35a7245 100644 --- a/Sources/Flash/Extensions/Future+Flash.swift +++ b/Sources/Flash/Extensions/Future+Flash.swift @@ -1,9 +1,15 @@ import Vapor -public extension Future where T: Response { +public extension Future where T == Response { + /// Add a flash message to the `Response` of this `Future`. + /// + /// - Parameters: + /// - type: The type of the flash message (e.g. `.success` or `.error`). + /// - message: The message to display. + /// - Returns: A `Future` containing the `Response` with the added flash message. public func flash(_ type: Flash.Kind, _ message: String) -> Future { - return self.map(to: Response.self) { res in - return res.flash(type, message) + return map { response in + response.flash(type, message) } } } diff --git a/Sources/Flash/Extensions/Response+Flash.swift b/Sources/Flash/Extensions/Response+Flash.swift deleted file mode 100644 index dc1eafd..0000000 --- a/Sources/Flash/Extensions/Response+Flash.swift +++ /dev/null @@ -1,10 +0,0 @@ -import Vapor - -public extension Response { - public func flash(_ type: Flash.Kind, _ message: String) -> Response { - if let container = try? privateContainer.make(FlashContainer.self) { - container.flashes.append(.init(type, message)) - } - return self - } -} diff --git a/Sources/Flash/Extensions/SubContainer+Flash.swift b/Sources/Flash/Extensions/SubContainer+Flash.swift deleted file mode 100644 index 60b72fa..0000000 --- a/Sources/Flash/Extensions/SubContainer+Flash.swift +++ /dev/null @@ -1,10 +0,0 @@ -import Vapor - -extension SubContainer { - public func flash(_ type: Flash.Kind, _ message: String) -> SubContainer { - if let container = try? self.make(FlashContainer.self) { - container.flashes.append(.init(type, message)) - } - return self - } -} diff --git a/Sources/Flash/Middlewares/FlashMiddleware.swift b/Sources/Flash/Middlewares/FlashMiddleware.swift index 2f25293..bc29c54 100644 --- a/Sources/Flash/Middlewares/FlashMiddleware.swift +++ b/Sources/Flash/Middlewares/FlashMiddleware.swift @@ -1,41 +1,49 @@ import Vapor +/// Middleware that: +/// - decodes flashes from the `Session` and puts them on the `Request`s container. +/// - encodes flashes from the `Response` to the `Session`. public struct FlashMiddleware: Middleware, ServiceType { - private static let sessionKey = "_flash" - + /// See `ServiceType.makeService`. public static func makeService(for container: Container) throws -> FlashMiddleware { return .init() } + /// Create a new `FlashMiddleware`. public init() {} /// See Middleware.respond public func respond(to req: Request, chainingTo next: Responder) throws -> Future { - try FlashMiddleware.handle(req: req) - return try next.respond(to: req) - .map(to: Response.self) { resp in - try FlashMiddleware.handle(req: req, resp: resp) - return resp + try req.decodeFlashDataFromSession() + return try next + .respond(to: req) + .try { resp in + try req.encodeFlashDataToSession(from: resp) } } +} - public static func handle(req: Request) throws { - let session = try req.session() +private let sessionKey = "_flash" - if let data = session[sessionKey]?.data(using: .utf8) { - let flash = try JSONDecoder().decode(FlashContainer.self, from: data) - let container = try req.privateContainer.make(FlashContainer.self) - container.new = flash.new - container.old = flash.old +extension Request { + func decodeFlashDataFromSession() throws { + let container = try privateContainer.make(FlashContainer.self) + + guard let data = try session()[sessionKey]?.data(using: .utf8) else { + container.flashes = [] + return } + + let decoded = try JSONDecoder().decode(FlashContainer.self, from: data) + container.flashes = decoded.flashes } - public static func handle(req: Request, resp: Response) throws { + func encodeFlashDataToSession(from resp: Response) throws { let container = try resp.privateContainer.make(FlashContainer.self) let flash = try String( data: JSONEncoder().encode(container), encoding: .utf8 ) - try req.session()[sessionKey] = flash + try session()[sessionKey] = flash } } diff --git a/Sources/Flash/Models/Flash.swift b/Sources/Flash/Models/Flash.swift index b6a1fd0..ca66845 100644 --- a/Sources/Flash/Models/Flash.swift +++ b/Sources/Flash/Models/Flash.swift @@ -1,6 +1,7 @@ import Vapor -public final class Flash: Codable { +/// A message that can be displayed on a web page. The message is displayed +public struct Flash: Codable { public enum Kind: String, Codable { case error case success @@ -8,14 +9,18 @@ public final class Flash: Codable { case warning } - public var kind: Kind - public var message: String + /// The kind of message. + /// - See: `Flash.Kind`. + public let kind: Kind - public init(kind: Kind, message: String) { - self.kind = kind - self.message = message - } + /// The message to be displayed. + public let message: String + /// Create a new `Flash` message. + /// + /// - Parameters: + /// - kind: The kind of message. + /// - message: The text to be displayed. public init(_ kind: Kind, _ message: String) { self.kind = kind self.message = message diff --git a/Sources/Flash/Models/FlashContainer.swift b/Sources/Flash/Models/FlashContainer.swift index d9ffbcd..ab89690 100644 --- a/Sources/Flash/Models/FlashContainer.swift +++ b/Sources/Flash/Models/FlashContainer.swift @@ -1,16 +1,6 @@ import Vapor +/// Container that contains all `Flash`es. public final class FlashContainer: Codable, Service { - public var new: [Flash] = [] - public var old: [Flash] = [] - - public var flashes: [Flash] { - get { - return new - } - - set { - new = newValue - } - } + public var flashes: [Flash] = [] } diff --git a/Sources/Flash/Providers/FlashProvider.swift b/Sources/Flash/Providers/FlashProvider.swift index a836fb0..2404e84 100644 --- a/Sources/Flash/Providers/FlashProvider.swift +++ b/Sources/Flash/Providers/FlashProvider.swift @@ -2,9 +2,13 @@ import Leaf import Sugar import Vapor +/// Provider that registers FlashMiddleware and FlashContainer, and FlashTag. public final class FlashProvider: Provider { + + /// Create a new `FlashProvider`. public init() {} + /// See `Provider.register`. public func register(_ services: inout Services) throws { try services.register(MutableLeafTagConfigProvider()) services.register(FlashMiddleware.self) @@ -13,6 +17,7 @@ public final class FlashProvider: Provider { } } + /// See `Provider.didBoot` public func didBoot(_ container: Container) throws -> EventLoopFuture { let tags: MutableLeafTagConfig = try container.make() tags.use(FlashTag(), as: "flash") diff --git a/Sources/Flash/Tags/FlashTag.swift b/Sources/Flash/Tags/FlashTag.swift index e2c3cb1..0e4ccbb 100644 --- a/Sources/Flash/Tags/FlashTag.swift +++ b/Sources/Flash/Tags/FlashTag.swift @@ -1,70 +1,63 @@ import Leaf import TemplateKit +/// Tag that public final class FlashTag: TagRenderer { - public func render(tag: TagContext) throws -> EventLoopFuture { + /// Create a new `FlashTag`. + public init() {} + + /// See `TagRenderer.render`. + public func render(tag: TagContext) throws -> Future { let body = try tag.requireBody() - let flash = try tag.container.make(FlashContainer.self) + let flash: FlashContainer = try tag.container.make() guard !flash.flashes.isEmpty else { - return Future.map(on: tag) { - .string("") - } + return tag.future(.null) } - var dict = tag.context.data.dictionary ?? [:] - dict["all"] = try .array(flash.flashes.map { - try $0.convertToTemplateData() - }) - - dict["errors"] = try .array(flash.flashes.compactMap { flash in - guard flash.kind == .error else { return nil } - return try flash.convertToTemplateData() - }) - - dict["warnings"] = try .array(flash.flashes.compactMap { flash in - guard flash.kind == .warning else { return nil } - return try flash.convertToTemplateData() - }) - - dict["successes"] = try .array(flash.flashes.compactMap { flash in - guard flash.kind == .success else { return nil } - return try flash.convertToTemplateData() - }) + var flashes = + try Dictionary(grouping: flash.flashes) { flash in + flash.kind.groupingKey + } + .mapValues { flashes -> TemplateData in + .array(try flashes.map { flash in + try flash.convertToTemplateData() + }) + } - dict["information"] = try .array(flash.flashes.compactMap { flash in - guard flash.kind == .info else { return nil } - return try flash.convertToTemplateData() - }) + flashes["all"] = .array(flashes.values.compactMap { $0 }) - tag.context.data = .dictionary(dict) + let existing = (tag.context.data.dictionary ?? [:]) + tag.context.data = .dictionary( + existing.merging(flashes) { (existing, fromFlash) -> TemplateData in + return fromFlash + } + ) - return tag.serializer.serialize(ast: body).map(to: TemplateData.self) { er in - let body = String(data: er.data, encoding: .utf8) ?? "" - return .string(body) + return tag.serializer.serialize(ast: body).map { view in + .string(String(data: view.data, encoding: .utf8) ?? "") } } - - public init() {} } extension Flash: TemplateDataRepresentable { + /// See `TemplateDataRepresentable`. public func convertToTemplateData() throws -> TemplateData { - return TemplateData.dictionary([ - "kind": .string(self.kind.rawValue), - "bootstrapClass": .string(self.kind.bootstrapClass), - "message": .string(self.message) + return .dictionary([ + "kind": .string(kind.rawValue), + "bootstrapClass": .string(kind.rawValue), + "message": .string(message) ]) } } extension Flash.Kind { - var bootstrapClass: String { + var groupingKey: String { switch self { - case .error: return "danger" - case .warning: return "warning" - case .success: return "success" - case .info: return "info" + case .error: return "errors" + case .info: return "information" + case .success: return "successes" + case .warning: return "warnings" } } } From ba248ae0af3ac5f9d2cde26333e9673761ddea1b Mon Sep 17 00:00:00 2001 From: Simon Kempendorf Date: Tue, 23 Oct 2018 23:52:47 +0200 Subject: [PATCH 2/3] Fix array for all flashes --- Sources/Flash/Tags/FlashTag.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/Flash/Tags/FlashTag.swift b/Sources/Flash/Tags/FlashTag.swift index 0e4ccbb..d9db3cd 100644 --- a/Sources/Flash/Tags/FlashTag.swift +++ b/Sources/Flash/Tags/FlashTag.swift @@ -24,8 +24,7 @@ public final class FlashTag: TagRenderer { try flash.convertToTemplateData() }) } - - flashes["all"] = .array(flashes.values.compactMap { $0 }) + flashes["all"] = .array(try flash.flashes.map { flash in try flash.convertToTemplateData() }) let existing = (tag.context.data.dictionary ?? [:]) tag.context.data = .dictionary( From 1124b4aad37560827765620b274e47d4704d9362 Mon Sep 17 00:00:00 2001 From: Simon Kempendorf Date: Tue, 23 Oct 2018 23:53:03 +0200 Subject: [PATCH 3/3] Remove flashes after showing --- Sources/Flash/Tags/FlashTag.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/Flash/Tags/FlashTag.swift b/Sources/Flash/Tags/FlashTag.swift index d9db3cd..2d52e9c 100644 --- a/Sources/Flash/Tags/FlashTag.swift +++ b/Sources/Flash/Tags/FlashTag.swift @@ -25,6 +25,7 @@ public final class FlashTag: TagRenderer { }) } flashes["all"] = .array(try flash.flashes.map { flash in try flash.convertToTemplateData() }) + flash.flashes.removeAll() let existing = (tag.context.data.dictionary ?? [:]) tag.context.data = .dictionary(