From ff3051c32829d5718f81c2473c6904f03f135241 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 19 Feb 2016 19:26:36 +0200 Subject: [PATCH 01/88] moved to TidyJSON --- Cartfile | 2 +- Express.xcodeproj/project.pbxproj | 16 ++++---- Express/AnyContent+JSON.swift | 9 +++-- Express/JsonView.swift | 66 +++++++++++++++++++++++++------ 4 files changed, 67 insertions(+), 26 deletions(-) diff --git a/Cartfile b/Cartfile index 93ce337..e8cebe2 100644 --- a/Cartfile +++ b/Cartfile @@ -1,5 +1,5 @@ github "crossroadlabs/BrightFutures" "master" -github "SwiftyJSON/SwiftyJSON" +github "crossroadlabs/TidyJSON" "master" github "groue/GRMustache.swift" "Swift2.1" github "crossroadlabs/Regex" ~> 0.4 github "crossroadlabs/PathToRegex" ~> 0.1 diff --git a/Express.xcodeproj/project.pbxproj b/Express.xcodeproj/project.pbxproj index 907934c..0c0e4e9 100644 --- a/Express.xcodeproj/project.pbxproj +++ b/Express.xcodeproj/project.pbxproj @@ -44,17 +44,17 @@ 657CDBCF1C2A07C5005261BA /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657CDBCE1C2A07C5005261BA /* Errors.swift */; }; 658DBDA31C221A2A00BE8AA9 /* BrightFutures.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA01C221A2A00BE8AA9 /* BrightFutures.framework */; }; 658DBDA41C221A2A00BE8AA9 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA11C221A2A00BE8AA9 /* Result.framework */; }; - 658DBDA51C221A2A00BE8AA9 /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA21C221A2A00BE8AA9 /* SwiftyJSON.framework */; }; 658DBDA71C221B0D00BE8AA9 /* BrightFutures.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA01C221A2A00BE8AA9 /* BrightFutures.framework */; }; 658DBDA81C221B0D00BE8AA9 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA11C221A2A00BE8AA9 /* Result.framework */; }; - 658DBDA91C221B0D00BE8AA9 /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA21C221A2A00BE8AA9 /* SwiftyJSON.framework */; }; 658DBDAB1C221B3400BE8AA9 /* BrightFutures.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA01C221A2A00BE8AA9 /* BrightFutures.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 658DBDAC1C221B3400BE8AA9 /* Result.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA11C221A2A00BE8AA9 /* Result.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 658DBDAD1C221B3400BE8AA9 /* SwiftyJSON.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA21C221A2A00BE8AA9 /* SwiftyJSON.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 658DBDAF1C22218100BE8AA9 /* Headers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658DBDAE1C22218100BE8AA9 /* Headers.swift */; }; 658DBDB11C2228F000BE8AA9 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658DBDB01C2228F000BE8AA9 /* Response.swift */; }; 658F1CC81C3BF017004968B9 /* UrlMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658F1CC71C3BF017004968B9 /* UrlMatcher.swift */; }; 659157FF1C285AFA00672785 /* colored.mustache in Resources */ = {isa = PBXBuildFile; fileRef = 659157FE1C285AFA00672785 /* colored.mustache */; }; + 659E80AB1C7756C200DE85B1 /* TidyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 659E80AA1C7756C200DE85B1 /* TidyJSON.framework */; }; + 659E80AC1C7756D000DE85B1 /* TidyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 659E80AA1C7756C200DE85B1 /* TidyJSON.framework */; }; + 659E80AD1C7756DD00DE85B1 /* TidyJSON.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 659E80AA1C7756C200DE85B1 /* TidyJSON.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 65BA1E981C25D38200BCE076 /* JsonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65BA1E971C25D38200BCE076 /* JsonView.swift */; }; 65BB8DAB1C2A226E00F8B52C /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65BB8DAA1C2A226E00F8B52C /* ErrorHandler.swift */; }; 65D922211C25BBFF00EEA056 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65D922201C25BBFF00EEA056 /* View.swift */; }; @@ -81,12 +81,12 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 659E80AD1C7756DD00DE85B1 /* TidyJSON.framework in Copy Frameworks */, 65DEAFF61C6A8F3900EBA47F /* PathToRegex.framework in Copy Frameworks */, 65DEAFF71C6A8F3900EBA47F /* Regex.framework in Copy Frameworks */, 6525AC561C24E3E700131F58 /* Mustache.framework in Copy Frameworks */, 658DBDAB1C221B3400BE8AA9 /* BrightFutures.framework in Copy Frameworks */, 658DBDAC1C221B3400BE8AA9 /* Result.framework in Copy Frameworks */, - 658DBDAD1C221B3400BE8AA9 /* SwiftyJSON.framework in Copy Frameworks */, ); name = "Copy Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -132,11 +132,11 @@ 657CDBCE1C2A07C5005261BA /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; 658DBDA01C221A2A00BE8AA9 /* BrightFutures.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BrightFutures.framework; path = Carthage/Build/Mac/BrightFutures.framework; sourceTree = ""; }; 658DBDA11C221A2A00BE8AA9 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/Mac/Result.framework; sourceTree = ""; }; - 658DBDA21C221A2A00BE8AA9 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/Mac/SwiftyJSON.framework; sourceTree = ""; }; 658DBDAE1C22218100BE8AA9 /* Headers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Headers.swift; sourceTree = ""; }; 658DBDB01C2228F000BE8AA9 /* Response.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = ""; }; 658F1CC71C3BF017004968B9 /* UrlMatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UrlMatcher.swift; sourceTree = ""; }; 659157FE1C285AFA00672785 /* colored.mustache */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = colored.mustache; sourceTree = ""; }; + 659E80AA1C7756C200DE85B1 /* TidyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TidyJSON.framework; path = Carthage/Build/Mac/TidyJSON.framework; sourceTree = ""; }; 65BA1E971C25D38200BCE076 /* JsonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JsonView.swift; sourceTree = ""; }; 65BB8DAA1C2A226E00F8B52C /* ErrorHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = ""; }; 65D922201C25BBFF00EEA056 /* View.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; @@ -157,12 +157,12 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 659E80AB1C7756C200DE85B1 /* TidyJSON.framework in Frameworks */, 65DEAFF21C6A8EA700EBA47F /* PathToRegex.framework in Frameworks */, 65DEAFF31C6A8EA700EBA47F /* Regex.framework in Frameworks */, 658DBDA31C221A2A00BE8AA9 /* BrightFutures.framework in Frameworks */, 6525AC541C24E3C600131F58 /* Mustache.framework in Frameworks */, 658DBDA41C221A2A00BE8AA9 /* Result.framework in Frameworks */, - 658DBDA51C221A2A00BE8AA9 /* SwiftyJSON.framework in Frameworks */, 96E0C0541C6D4A9600CA926F /* libevhtp.dylib in Frameworks */, 968A3BAE1C63983700E4AFCC /* libssl.dylib in Frameworks */, 968A3BB01C63984400E4AFCC /* libcrypto.dylib in Frameworks */, @@ -176,12 +176,12 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 659E80AC1C7756D000DE85B1 /* TidyJSON.framework in Frameworks */, 658DBDA71C221B0D00BE8AA9 /* BrightFutures.framework in Frameworks */, 6525AC551C24E3E000131F58 /* Mustache.framework in Frameworks */, 65DEAFF41C6A8F1900EBA47F /* PathToRegex.framework in Frameworks */, 65DEAFF51C6A8F1900EBA47F /* Regex.framework in Frameworks */, 658DBDA81C221B0D00BE8AA9 /* Result.framework in Frameworks */, - 658DBDA91C221B0D00BE8AA9 /* SwiftyJSON.framework in Frameworks */, 651826801C2208A800E49AC5 /* Express.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -283,9 +283,9 @@ 658DBDA61C221AEB00BE8AA9 /* Frameworks */ = { isa = PBXGroup; children = ( + 659E80AA1C7756C200DE85B1 /* TidyJSON.framework */, 65DEAFF01C6A8EA700EBA47F /* PathToRegex.framework */, 65DEAFF11C6A8EA700EBA47F /* Regex.framework */, - 658DBDA21C221A2A00BE8AA9 /* SwiftyJSON.framework */, 658DBDA11C221A2A00BE8AA9 /* Result.framework */, 658DBDA01C221A2A00BE8AA9 /* BrightFutures.framework */, 6525AC531C24E3C600131F58 /* Mustache.framework */, diff --git a/Express/AnyContent+JSON.swift b/Express/AnyContent+JSON.swift index 360bf6a..7bdff85 100644 --- a/Express/AnyContent+JSON.swift +++ b/Express/AnyContent+JSON.swift @@ -20,16 +20,17 @@ //===----------------------------------------------------------------------===// import Foundation -import SwiftyJSON +import TidyJSON public extension AnyContent { func asJSON() -> JSON? { for ct in contentType { //TODO: move to constants if "application/json" == ct { - let json = JSON(data: NSData(bytes: data, length: data.count)) - for e in json.error { - //TODO: better log + guard let (json, error) = self.asText().map(JSON.parse) else { + return Optional.None + } + if let e = error { print(e) return Optional.None } diff --git a/Express/JsonView.swift b/Express/JsonView.swift index ec22409..f8eb0b0 100644 --- a/Express/JsonView.swift +++ b/Express/JsonView.swift @@ -20,15 +20,57 @@ //===----------------------------------------------------------------------===// import Foundation -import SwiftyJSON +import TidyJSON -//quick fix -private func anyDescription(any:Any) -> String? { - guard let anyObject = any as? AnyObject else { - return nil +public protocol JSONConvertible { + func toJSON() -> JSON? +} + +extension Bool : JSONConvertible { + public func toJSON() -> JSON? { + return JSON(self) + } +} + +extension Double : JSONConvertible { + public func toJSON() -> JSON? { + return JSON(self) + } +} + +extension Int : JSONConvertible { + public func toJSON() -> JSON? { + return JSON(self) + } +} + +extension String : JSONConvertible { + public func toJSON() -> JSON? { + return JSON(self) + } +} + +extension Array : JSONConvertible { + public func toJSON() -> JSON? { + return JSON(self.flatMap { $0 as? JSONConvertible }.flatMap {$0.toJSON()}) + } +} + +extension Dictionary : JSONConvertible { + public func toJSON() -> JSON? { + let normalized = self.map {(String($0), $1)}.flatMap { (k, v) in + (v as? JSONConvertible).map {(k, $0)} + } + return JSON(toMap(normalized.flatMap { (k, v) in + v.toJSON().map {(k, $0)} + })) + } +} + +extension Optional { + public func toJSON() -> JSON? { + return self.flatMap{$0 as? JSONConvertible}.flatMap{$0.toJSON()}.getOrElse(JSON.Null) } - - return anyObject.description } public class JsonView : NamedViewType { @@ -38,14 +80,12 @@ public class JsonView : NamedViewType { public init() { } - public func render(context:Any?) throws -> AbstractActionType { + public func render(context:T?) throws -> AbstractActionType { //TODO: implement reflection - let json = context.flatMap { $0 as? AnyObject } .map { context in - JSON(context) - }.getOrElse(JSON(Dictionary())) + let json = context.flatMap{$0 as? JSONConvertible}.flatMap { $0.toJSON() } //TODO: avoid string path - guard let render = json.rawString() else { - throw ExpressError.Render(description: "unable to render json: " + context.flatMap(anyDescription).getOrElse("None"), line: nil, cause: nil) + guard let render = json?.dump() else { + throw ExpressError.Render(description: "unable to render json: " + context.flatMap{String($0)}.getOrElse("None"), line: nil, cause: nil) } return Action.ok(AnyContent(str:render, contentType: "application/json")) } From a0368dae94c8faa380eb78f62271c4a527c94653 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 07:51:36 +0200 Subject: [PATCH 02/88] Adapted linux version of BrightFutures --- Cartfile | 8 ++++---- Express.xcodeproj/project.pbxproj | 8 ++++---- Express/EVHTP.swift | 1 + Express/ExecutionContext.swift | 13 +++++++------ Express/Express.swift | 1 + Express/ExpressServer.swift | 2 ++ Express/HttpServer.swift | 1 + Express/Queue.swift | 32 ------------------------------- Express/Server.swift | 1 + Express/Transaction.swift | 2 ++ Express/Views.swift | 1 + 11 files changed, 24 insertions(+), 46 deletions(-) delete mode 100644 Express/Queue.swift diff --git a/Cartfile b/Cartfile index e8cebe2..73e0ccb 100644 --- a/Cartfile +++ b/Cartfile @@ -1,6 +1,6 @@ -github "crossroadlabs/BrightFutures" "master" +github "crossroadlabs/BrightFutures" "feature-express-linux" github "crossroadlabs/TidyJSON" "master" github "groue/GRMustache.swift" "Swift2.1" -github "crossroadlabs/Regex" ~> 0.4 -github "crossroadlabs/PathToRegex" ~> 0.1 -github "crossroadlabs/CEVHTP" ~> 0.1.0 +github "crossroadlabs/Regex" ~> 0.5 +github "crossroadlabs/PathToRegex" ~> 0.2 +github "crossroadlabs/CEVHTP" ~> 0.1 diff --git a/Express.xcodeproj/project.pbxproj b/Express.xcodeproj/project.pbxproj index 0c0e4e9..23815be 100644 --- a/Express.xcodeproj/project.pbxproj +++ b/Express.xcodeproj/project.pbxproj @@ -16,7 +16,6 @@ 6504936D1C22199300616444 /* Functional.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6504935C1C22199300616444 /* Functional.swift */; }; 6504936E1C22199300616444 /* HttpMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6504935D1C22199300616444 /* HttpMethod.swift */; }; 6504936F1C22199300616444 /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6504935E1C22199300616444 /* HttpServer.swift */; }; - 650493701C22199300616444 /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6504935F1C22199300616444 /* Queue.swift */; }; 650493711C22199300616444 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650493601C22199300616444 /* Request.swift */; }; 650493721C22199300616444 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650493611C22199300616444 /* Router.swift */; }; 650493731C22199300616444 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650493621C22199300616444 /* Server.swift */; }; @@ -65,6 +64,7 @@ 65DEAFF61C6A8F3900EBA47F /* PathToRegex.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65DEAFF01C6A8EA700EBA47F /* PathToRegex.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 65DEAFF71C6A8F3900EBA47F /* Regex.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65DEAFF11C6A8EA700EBA47F /* Regex.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 65DEAFF91C6AA8F700EBA47F /* RegexUrlMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DEAFF81C6AA8F700EBA47F /* RegexUrlMatcher.swift */; }; + 65E646DB1C79878E0036D028 /* ExecutionContext.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646DA1C79878E0036D028 /* ExecutionContext.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 961B9E8B1C23229A000506D2 /* libevent.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 961B9E8A1C23229A000506D2 /* libevent.dylib */; }; 961B9E8F1C2322CA000506D2 /* libevent_openssl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 961B9E8E1C2322CA000506D2 /* libevent_openssl.dylib */; }; 961B9E911C2324B4000506D2 /* EVHTP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961B9E901C2324B4000506D2 /* EVHTP.swift */; }; @@ -81,6 +81,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 65E646DB1C79878E0036D028 /* ExecutionContext.framework in Copy Frameworks */, 659E80AD1C7756DD00DE85B1 /* TidyJSON.framework in Copy Frameworks */, 65DEAFF61C6A8F3900EBA47F /* PathToRegex.framework in Copy Frameworks */, 65DEAFF71C6A8F3900EBA47F /* Regex.framework in Copy Frameworks */, @@ -103,7 +104,6 @@ 6504935C1C22199300616444 /* Functional.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Functional.swift; sourceTree = ""; }; 6504935D1C22199300616444 /* HttpMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpMethod.swift; sourceTree = ""; }; 6504935E1C22199300616444 /* HttpServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpServer.swift; sourceTree = ""; }; - 6504935F1C22199300616444 /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = ""; }; 650493601C22199300616444 /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; }; 650493611C22199300616444 /* Router.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; 650493621C22199300616444 /* Server.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Server.swift; sourceTree = ""; }; @@ -143,6 +143,7 @@ 65DEAFF01C6A8EA700EBA47F /* PathToRegex.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PathToRegex.framework; path = Carthage/Build/Mac/PathToRegex.framework; sourceTree = ""; }; 65DEAFF11C6A8EA700EBA47F /* Regex.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Regex.framework; path = Carthage/Build/Mac/Regex.framework; sourceTree = ""; }; 65DEAFF81C6AA8F700EBA47F /* RegexUrlMatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegexUrlMatcher.swift; sourceTree = ""; }; + 65E646DA1C79878E0036D028 /* ExecutionContext.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ExecutionContext.framework; path = Carthage/Build/Mac/ExecutionContext.framework; sourceTree = ""; }; 961B9E8A1C23229A000506D2 /* libevent.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libevent.dylib; path = ../../../../../usr/local/lib/libevent.dylib; sourceTree = ""; }; 961B9E8E1C2322CA000506D2 /* libevent_openssl.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libevent_openssl.dylib; path = ../../../../../usr/local/lib/libevent_openssl.dylib; sourceTree = ""; }; 961B9E901C2324B4000506D2 /* EVHTP.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EVHTP.swift; sourceTree = ""; }; @@ -211,7 +212,6 @@ 651475D01C21F1550049825D /* Express */ = { isa = PBXGroup; children = ( - 6504935F1C22199300616444 /* Queue.swift */, 6504935B1C22199300616444 /* ExecutionContext.swift */, 6504935A1C22199300616444 /* Streams.swift */, 650493591C22199300616444 /* Content.swift */, @@ -283,6 +283,7 @@ 658DBDA61C221AEB00BE8AA9 /* Frameworks */ = { isa = PBXGroup; children = ( + 65E646DA1C79878E0036D028 /* ExecutionContext.framework */, 659E80AA1C7756C200DE85B1 /* TidyJSON.framework */, 65DEAFF01C6A8EA700EBA47F /* PathToRegex.framework */, 65DEAFF11C6A8EA700EBA47F /* Regex.framework */, @@ -448,7 +449,6 @@ 650493711C22199300616444 /* Request.swift in Sources */, 6504936E1C22199300616444 /* HttpMethod.swift in Sources */, 657C1A6E1C24C8850037041F /* Views.swift in Sources */, - 650493701C22199300616444 /* Queue.swift in Sources */, 650493751C22199300616444 /* Express.swift in Sources */, 6550774C1C2DC8E500E42309 /* MIME.swift in Sources */, 65BA1E981C25D38200BCE076 /* JsonView.swift in Sources */, diff --git a/Express/EVHTP.swift b/Express/EVHTP.swift index 5c40616..eaad266 100644 --- a/Express/EVHTP.swift +++ b/Express/EVHTP.swift @@ -21,6 +21,7 @@ import Foundation import CEVHTP +import Result import BrightFutures internal typealias EVHTPp = UnsafeMutablePointer diff --git a/Express/ExecutionContext.swift b/Express/ExecutionContext.swift index b7ae4b6..0d6858f 100644 --- a/Express/ExecutionContext.swift +++ b/Express/ExecutionContext.swift @@ -22,13 +22,14 @@ import Foundation import BrightFutures import Dispatch +import ExecutionContext -class ExecutionContext { - static let main = toContext(Queue.main) - static let user = toContext(Queue.user) - static let action = toContext(Queue.action) - static let render = toContext(Queue.render) - static let view = toContext(Queue.view) +extension DefaultExecutionContext { + static let main = mainContext + static let user = globalContext + static let action = toContext(DefaultExecutionContext(kind: .Parallel)) + static let render = toContext(DefaultExecutionContext(kind: .Parallel)) + static let view = toContext(DefaultExecutionContext(kind: .Serial)) @noreturn class func run() { dispatch_main() diff --git a/Express/Express.swift b/Express/Express.swift index 99ffcab..53ab296 100644 --- a/Express/Express.swift +++ b/Express/Express.swift @@ -20,6 +20,7 @@ //===----------------------------------------------------------------------===// import Foundation +import ExecutionContext import BrightFutures public func express() -> Express { diff --git a/Express/ExpressServer.swift b/Express/ExpressServer.swift index 8deaf6a..b675950 100644 --- a/Express/ExpressServer.swift +++ b/Express/ExpressServer.swift @@ -20,6 +20,8 @@ //===----------------------------------------------------------------------===// import Foundation +import Result +import ExecutionContext import BrightFutures public extension Express { diff --git a/Express/HttpServer.swift b/Express/HttpServer.swift index 23e87d6..8b86750 100644 --- a/Express/HttpServer.swift +++ b/Express/HttpServer.swift @@ -20,6 +20,7 @@ //===----------------------------------------------------------------------===// import Foundation +import Result import BrightFutures private class ServerParams { diff --git a/Express/Queue.swift b/Express/Queue.swift deleted file mode 100644 index cf99620..0000000 --- a/Express/Queue.swift +++ /dev/null @@ -1,32 +0,0 @@ -//===--- Queue.swift ------------------------------------------------------===// -// -//Copyright (c) 2015-2016 Daniel Leping (dileping) -// -//This file is part of Swift Express. -// -//Swift Express is free software: you can redistribute it and/or modify -//it under the terms of the GNU Lesser General Public License as published by -//the Free Software Foundation, either version 3 of the License, or -//(at your option) any later version. -// -//Swift Express is distributed in the hope that it will be useful, -//but WITHOUT ANY WARRANTY; without even the implied warranty of -//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -//GNU Lesser General Public License for more details. -// -//You should have received a copy of the GNU Lesser General Public License -//along with Swift Express. If not, see . -// -//===----------------------------------------------------------------------===// - - -import Foundation -import BrightFutures -import Dispatch - -extension Queue { - static let user:Queue = Queue.global - static let action:Queue = Queue(queue: dispatch_queue_create("action", DISPATCH_QUEUE_CONCURRENT)) - static let render:Queue = Queue(queue: dispatch_queue_create("render", DISPATCH_QUEUE_CONCURRENT)) - static let view:Queue = Queue(queue: dispatch_queue_create("view", DISPATCH_QUEUE_SERIAL)) -} \ No newline at end of file diff --git a/Express/Server.swift b/Express/Server.swift index 85780b3..f8bc453 100644 --- a/Express/Server.swift +++ b/Express/Server.swift @@ -21,6 +21,7 @@ import Foundation import BrightFutures +import Result protocol ServerType { var router:RouterType { diff --git a/Express/Transaction.swift b/Express/Transaction.swift index 47d350c..9e375bf 100644 --- a/Express/Transaction.swift +++ b/Express/Transaction.swift @@ -20,7 +20,9 @@ //===----------------------------------------------------------------------===// import Foundation +import ExecutionContext import BrightFutures +import Result public protocol TransactionType : DataConsumerType { func tryConsume(content:ContentType) -> Bool diff --git a/Express/Views.swift b/Express/Views.swift index 6875d30..9a31de8 100644 --- a/Express/Views.swift +++ b/Express/Views.swift @@ -20,6 +20,7 @@ //===----------------------------------------------------------------------===// import Foundation +import ExecutionContext import BrightFutures import Result From b4afee470f34d6498a103be3ebb29dfe330e8ff4 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 09:44:55 +0200 Subject: [PATCH 03/88] Works with new futures --- Express/ExecutionContext.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Express/ExecutionContext.swift b/Express/ExecutionContext.swift index 0d6858f..205911b 100644 --- a/Express/ExecutionContext.swift +++ b/Express/ExecutionContext.swift @@ -24,9 +24,11 @@ import BrightFutures import Dispatch import ExecutionContext -extension DefaultExecutionContext { - static let main = mainContext - static let user = globalContext +private let cmain:ExecutionContextType = ExecutionContext.main + +extension ExecutionContext { + static let main = cmain + static let user = ExecutionContext.global static let action = toContext(DefaultExecutionContext(kind: .Parallel)) static let render = toContext(DefaultExecutionContext(kind: .Parallel)) static let view = toContext(DefaultExecutionContext(kind: .Serial)) From 9247eb0520f4509c8a3795ab0648a13872af4b8c Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 09:46:47 +0200 Subject: [PATCH 04/88] minor changes --- Express/ExecutionContext.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Express/ExecutionContext.swift b/Express/ExecutionContext.swift index 205911b..3e2abad 100644 --- a/Express/ExecutionContext.swift +++ b/Express/ExecutionContext.swift @@ -28,10 +28,10 @@ private let cmain:ExecutionContextType = ExecutionContext.main extension ExecutionContext { static let main = cmain - static let user = ExecutionContext.global - static let action = toContext(DefaultExecutionContext(kind: .Parallel)) - static let render = toContext(DefaultExecutionContext(kind: .Parallel)) - static let view = toContext(DefaultExecutionContext(kind: .Serial)) + static let user = global + static let action = toContext(ExecutionContext(kind: .Parallel)) + static let render = toContext(ExecutionContext(kind: .Parallel)) + static let view = toContext(ExecutionContext(kind: .Serial)) @noreturn class func run() { dispatch_main() From 8755d163780a7ff4e6c841c05c5b438ed91f389f Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 10:35:04 +0200 Subject: [PATCH 05/88] Implemented Stencil view engine --- Cartfile | 1 + Demo/main.swift | 11 +++++ Demo/views/hello2.stencil | 5 ++ Express.xcodeproj/project.pbxproj | 24 ++++++++++ Express/StencilViewEngine.swift | 80 +++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 Demo/views/hello2.stencil create mode 100644 Express/StencilViewEngine.swift diff --git a/Cartfile b/Cartfile index 73e0ccb..c30f68f 100644 --- a/Cartfile +++ b/Cartfile @@ -4,3 +4,4 @@ github "groue/GRMustache.swift" "Swift2.1" github "crossroadlabs/Regex" ~> 0.5 github "crossroadlabs/PathToRegex" ~> 0.2 github "crossroadlabs/CEVHTP" ~> 0.1 +github "crossroadlabs/Stencil" ~> 0.5 diff --git a/Demo/main.swift b/Demo/main.swift index bd6320b..7c41772 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -17,6 +17,7 @@ let app = express() app.views.register(JsonView()) app.views.register(MustacheViewEngine()) +app.views.register(StencilViewEngine()) enum TestError { case Test @@ -69,6 +70,16 @@ app.get("/hello/:user.html") { request in return Action.render("hello", context: context) } +//user as an url param +app.get("/hello2/:user.html") { request in + //get user + let user = request.params["user"] + //if there is a user - create our context. If there is no user, context will remain nil + let context = user.map {["user": $0]} + //render our template named "hello" + return Action.render("hello2", context: context) +} + app.post("/api/user") { request in //check if JSON has arrived guard let json = request.body?.asJSON() else { diff --git a/Demo/views/hello2.stencil b/Demo/views/hello2.stencil new file mode 100644 index 0000000..0e623cc --- /dev/null +++ b/Demo/views/hello2.stencil @@ -0,0 +1,5 @@ + + +

Hello from Stencil: {{user}}

+ + \ No newline at end of file diff --git a/Express.xcodeproj/project.pbxproj b/Express.xcodeproj/project.pbxproj index 23815be..9f81d64 100644 --- a/Express.xcodeproj/project.pbxproj +++ b/Express.xcodeproj/project.pbxproj @@ -65,6 +65,14 @@ 65DEAFF71C6A8F3900EBA47F /* Regex.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65DEAFF11C6A8EA700EBA47F /* Regex.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 65DEAFF91C6AA8F700EBA47F /* RegexUrlMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DEAFF81C6AA8F700EBA47F /* RegexUrlMatcher.swift */; }; 65E646DB1C79878E0036D028 /* ExecutionContext.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646DA1C79878E0036D028 /* ExecutionContext.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 65E646E31C79A4EB0036D028 /* PathKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646E11C79A4EB0036D028 /* PathKit.framework */; }; + 65E646E41C79A4EB0036D028 /* Stencil.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646E21C79A4EB0036D028 /* Stencil.framework */; }; + 65E646E51C79A4FA0036D028 /* PathKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646E11C79A4EB0036D028 /* PathKit.framework */; }; + 65E646E61C79A4FA0036D028 /* Stencil.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646E21C79A4EB0036D028 /* Stencil.framework */; }; + 65E646E71C79A5010036D028 /* PathKit.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646E11C79A4EB0036D028 /* PathKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 65E646E81C79A5010036D028 /* Stencil.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646E21C79A4EB0036D028 /* Stencil.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 65E646EA1C79A5340036D028 /* StencilViewEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E646E91C79A5340036D028 /* StencilViewEngine.swift */; }; + 65E646EC1C79AD280036D028 /* hello2.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646EB1C79AD280036D028 /* hello2.stencil */; }; 961B9E8B1C23229A000506D2 /* libevent.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 961B9E8A1C23229A000506D2 /* libevent.dylib */; }; 961B9E8F1C2322CA000506D2 /* libevent_openssl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 961B9E8E1C2322CA000506D2 /* libevent_openssl.dylib */; }; 961B9E911C2324B4000506D2 /* EVHTP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961B9E901C2324B4000506D2 /* EVHTP.swift */; }; @@ -81,6 +89,8 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 65E646E71C79A5010036D028 /* PathKit.framework in Copy Frameworks */, + 65E646E81C79A5010036D028 /* Stencil.framework in Copy Frameworks */, 65E646DB1C79878E0036D028 /* ExecutionContext.framework in Copy Frameworks */, 659E80AD1C7756DD00DE85B1 /* TidyJSON.framework in Copy Frameworks */, 65DEAFF61C6A8F3900EBA47F /* PathToRegex.framework in Copy Frameworks */, @@ -144,6 +154,10 @@ 65DEAFF11C6A8EA700EBA47F /* Regex.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Regex.framework; path = Carthage/Build/Mac/Regex.framework; sourceTree = ""; }; 65DEAFF81C6AA8F700EBA47F /* RegexUrlMatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegexUrlMatcher.swift; sourceTree = ""; }; 65E646DA1C79878E0036D028 /* ExecutionContext.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ExecutionContext.framework; path = Carthage/Build/Mac/ExecutionContext.framework; sourceTree = ""; }; + 65E646E11C79A4EB0036D028 /* PathKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PathKit.framework; path = Carthage/Build/Mac/PathKit.framework; sourceTree = ""; }; + 65E646E21C79A4EB0036D028 /* Stencil.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Stencil.framework; path = Carthage/Build/Mac/Stencil.framework; sourceTree = ""; }; + 65E646E91C79A5340036D028 /* StencilViewEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StencilViewEngine.swift; sourceTree = ""; }; + 65E646EB1C79AD280036D028 /* hello2.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = hello2.stencil; sourceTree = ""; }; 961B9E8A1C23229A000506D2 /* libevent.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libevent.dylib; path = ../../../../../usr/local/lib/libevent.dylib; sourceTree = ""; }; 961B9E8E1C2322CA000506D2 /* libevent_openssl.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libevent_openssl.dylib; path = ../../../../../usr/local/lib/libevent_openssl.dylib; sourceTree = ""; }; 961B9E901C2324B4000506D2 /* EVHTP.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EVHTP.swift; sourceTree = ""; }; @@ -158,6 +172,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 65E646E31C79A4EB0036D028 /* PathKit.framework in Frameworks */, + 65E646E41C79A4EB0036D028 /* Stencil.framework in Frameworks */, 659E80AB1C7756C200DE85B1 /* TidyJSON.framework in Frameworks */, 65DEAFF21C6A8EA700EBA47F /* PathToRegex.framework in Frameworks */, 65DEAFF31C6A8EA700EBA47F /* Regex.framework in Frameworks */, @@ -184,6 +200,8 @@ 65DEAFF51C6A8F1900EBA47F /* Regex.framework in Frameworks */, 658DBDA81C221B0D00BE8AA9 /* Result.framework in Frameworks */, 651826801C2208A800E49AC5 /* Express.framework in Frameworks */, + 65E646E51C79A4FA0036D028 /* PathKit.framework in Frameworks */, + 65E646E61C79A4FA0036D028 /* Stencil.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -242,6 +260,7 @@ 657C1A6F1C24C8FF0037041F /* ViewEngine.swift */, 657C1A6D1C24C8850037041F /* Views.swift */, 657C1A711C24DD1D0037041F /* MustacheViewEngine.swift */, + 65E646E91C79A5340036D028 /* StencilViewEngine.swift */, 65BA1E971C25D38200BCE076 /* JsonView.swift */, 657CDBCE1C2A07C5005261BA /* Errors.swift */, 65BB8DAA1C2A226E00F8B52C /* ErrorHandler.swift */, @@ -276,6 +295,7 @@ 657C1A741C24E0F40037041F /* test.mustache */, 659157FE1C285AFA00672785 /* colored.mustache */, 652E5E941C6BD79700C11021 /* hello.mustache */, + 65E646EB1C79AD280036D028 /* hello2.stencil */, ); path = views; sourceTree = ""; @@ -283,6 +303,8 @@ 658DBDA61C221AEB00BE8AA9 /* Frameworks */ = { isa = PBXGroup; children = ( + 65E646E11C79A4EB0036D028 /* PathKit.framework */, + 65E646E21C79A4EB0036D028 /* Stencil.framework */, 65E646DA1C79878E0036D028 /* ExecutionContext.framework */, 659E80AA1C7756C200DE85B1 /* TidyJSON.framework */, 65DEAFF01C6A8EA700EBA47F /* PathToRegex.framework */, @@ -399,6 +421,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 65E646EC1C79AD280036D028 /* hello2.stencil in Resources */, 659157FF1C285AFA00672785 /* colored.mustache in Resources */, 657C1A751C24E0F40037041F /* test.mustache in Resources */, 652E5E951C6BD79700C11021 /* hello.mustache in Resources */, @@ -440,6 +463,7 @@ 6504936D1C22199300616444 /* Functional.swift in Sources */, 65662D061C33377200A16177 /* AnyContent+MultipartFormData.swift in Sources */, 658DBDB11C2228F000BE8AA9 /* Response.swift in Sources */, + 65E646EA1C79A5340036D028 /* StencilViewEngine.swift in Sources */, 655CC27E1C2370F9009FE219 /* Streams+Headers.swift in Sources */, 657CDBCF1C2A07C5005261BA /* Errors.swift in Sources */, 650493691C22199300616444 /* AnyContent+JSON.swift in Sources */, diff --git a/Express/StencilViewEngine.swift b/Express/StencilViewEngine.swift new file mode 100644 index 0000000..bafcbed --- /dev/null +++ b/Express/StencilViewEngine.swift @@ -0,0 +1,80 @@ +//===--- StencilViewEngine.swift -----------------------------------------===// +// +//Copyright (c) 2015-2016 Daniel Leping (dileping) +// +//This file is part of Swift Express. +// +//Swift Express is free software: you can redistribute it and/or modify +//it under the terms of the GNU Lesser General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. +// +//Swift Express is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU Lesser General Public License for more details. +// +//You should have received a copy of the GNU Lesser General Public License +//along with Swift Express. If not, see . +// +//===----------------------------------------------------------------------===// + +import Foundation +import PathKit +import Stencil + +typealias StencilEdible = [String: Any] + +private protocol StencilCookable { + func cook() -> StencilEdible +} + +extension Dictionary : StencilCookable { + func cook() -> StencilEdible { + return self.map { (k,v) in + (String(k), v) + } + } +} + +class StencilView : ViewType { + let template:Template + + init(template:Template) { + self.template = template + } + + func render(context:Any?) throws -> AbstractActionType { + do { + let edibleOption = context.flatMap{$0 as? StencilCookable }?.cook() + guard let edible = edibleOption else { + throw ExpressError.Render(description: "Unable to render supplied context", line: nil, cause: nil) + } + + let stencilContext = Context(dictionary: edible) + let render = try template.render(stencilContext) + return Action.ok(AnyContent(str:render, contentType: "text/html")) + } catch let e as TemplateSyntaxError { + throw ExpressError.Render(description: e.description, line: nil, cause: e) + } + } +} + +public class StencilViewEngine : ViewEngineType { + public init() { + } + + public func extensions() -> Array { + return ["stencil"] + } + + public func view(filePath:String) throws -> ViewType { + do { + let path = Path(filePath) + let template = try Template(path: path) + return StencilView(template: template) + } catch let e as TemplateSyntaxError { + throw ExpressError.Render(description: e.description, line: nil, cause: e) + } + } +} From e060e1f374165b4c66dcdd4ac9948687409fbc77 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 11:01:10 +0200 Subject: [PATCH 06/88] better Mustache support --- Express/MustacheViewEngine.swift | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/Express/MustacheViewEngine.swift b/Express/MustacheViewEngine.swift index 7806c23..d5a543e 100644 --- a/Express/MustacheViewEngine.swift +++ b/Express/MustacheViewEngine.swift @@ -22,6 +22,22 @@ import Foundation import Mustache +typealias MustacheEdible = AnyObject + +private protocol MustacheCookable { + func cook() -> MustacheEdible +} + +extension Dictionary : MustacheCookable { + func cook() -> MustacheEdible { + let s = self.map { (k,v) in + (String(k), v as! AnyObject) + } + let dict:NSDictionary = NSDictionary(dictionary: s) + return dict + } +} + class MustacheView : ViewType { let template:Template @@ -31,7 +47,15 @@ class MustacheView : ViewType { func render(context:Any?) throws -> AbstractActionType { do { - let anyContext = context.flatMap { $0 as? AnyObject } + let anyContext = context.flatMap { (i)->AnyObject? in + if let obj = i as? AnyObject { + return obj + } + guard let cookable = i as? MustacheCookable else { + return nil + } + return cookable.cook() + } let box = Box(anyContext as? MustacheBoxable) let render = try template.render(box) return Action.ok(AnyContent(str:render, contentType: "text/html")) From 0a809b686761fff043b691263bc80917e8fee765 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 11:03:04 +0200 Subject: [PATCH 07/88] Stencil collection test --- Demo/main.swift | 13 +++++++++++-- Demo/views/colored2.stencil | 1 + Demo/views/test2.stencil | 11 +++++++++++ Express.xcodeproj/project.pbxproj | 8 ++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 Demo/views/colored2.stencil create mode 100644 Demo/views/test2.stencil diff --git a/Demo/main.swift b/Demo/main.swift index 7c41772..1cef5be 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -142,7 +142,7 @@ app.get("/test") { req in } } -app.get("/test.html") { request in +func testItems(request:Request) throws -> [String: Any] { let newItems = request.query.map { (k, v) in (k, v.first!) } @@ -156,7 +156,16 @@ app.get("/test.html") { request in throw TestError.Test } - return Action.render("test", context: ["test": "ok", "items": viewItems]) + return ["test": "ok", "items": viewItems] +} + +app.get("/test.html") { request in + let items = try testItems(request) + return Action.render("test", context: items) +} + +app.get("/test2.html") { request in + return Action.render("test2", context: try testItems(request)) } app.get("/echo") { request in diff --git a/Demo/views/colored2.stencil b/Demo/views/colored2.stencil new file mode 100644 index 0000000..4f551d2 --- /dev/null +++ b/Demo/views/colored2.stencil @@ -0,0 +1 @@ +
  • Item: {{name}}
  • \ No newline at end of file diff --git a/Demo/views/test2.stencil b/Demo/views/test2.stencil new file mode 100644 index 0000000..aa03831 --- /dev/null +++ b/Demo/views/test2.stencil @@ -0,0 +1,11 @@ + + + +

    Test Stencil: {{test}}

    +

    +{% for item in items %} +
  • Item: {{item.name}}
  • +{% endfor %} +

    + + \ No newline at end of file diff --git a/Express.xcodeproj/project.pbxproj b/Express.xcodeproj/project.pbxproj index 9f81d64..2511bd5 100644 --- a/Express.xcodeproj/project.pbxproj +++ b/Express.xcodeproj/project.pbxproj @@ -73,6 +73,8 @@ 65E646E81C79A5010036D028 /* Stencil.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646E21C79A4EB0036D028 /* Stencil.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 65E646EA1C79A5340036D028 /* StencilViewEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E646E91C79A5340036D028 /* StencilViewEngine.swift */; }; 65E646EC1C79AD280036D028 /* hello2.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646EB1C79AD280036D028 /* hello2.stencil */; }; + 65E646EE1C79AF4E0036D028 /* test2.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646ED1C79AF4E0036D028 /* test2.stencil */; }; + 65E646F01C79AF6C0036D028 /* colored2.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646EF1C79AF6C0036D028 /* colored2.stencil */; }; 961B9E8B1C23229A000506D2 /* libevent.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 961B9E8A1C23229A000506D2 /* libevent.dylib */; }; 961B9E8F1C2322CA000506D2 /* libevent_openssl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 961B9E8E1C2322CA000506D2 /* libevent_openssl.dylib */; }; 961B9E911C2324B4000506D2 /* EVHTP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961B9E901C2324B4000506D2 /* EVHTP.swift */; }; @@ -158,6 +160,8 @@ 65E646E21C79A4EB0036D028 /* Stencil.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Stencil.framework; path = Carthage/Build/Mac/Stencil.framework; sourceTree = ""; }; 65E646E91C79A5340036D028 /* StencilViewEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StencilViewEngine.swift; sourceTree = ""; }; 65E646EB1C79AD280036D028 /* hello2.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = hello2.stencil; sourceTree = ""; }; + 65E646ED1C79AF4E0036D028 /* test2.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = test2.stencil; sourceTree = ""; }; + 65E646EF1C79AF6C0036D028 /* colored2.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = colored2.stencil; sourceTree = ""; }; 961B9E8A1C23229A000506D2 /* libevent.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libevent.dylib; path = ../../../../../usr/local/lib/libevent.dylib; sourceTree = ""; }; 961B9E8E1C2322CA000506D2 /* libevent_openssl.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libevent_openssl.dylib; path = ../../../../../usr/local/lib/libevent_openssl.dylib; sourceTree = ""; }; 961B9E901C2324B4000506D2 /* EVHTP.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EVHTP.swift; sourceTree = ""; }; @@ -296,6 +300,8 @@ 659157FE1C285AFA00672785 /* colored.mustache */, 652E5E941C6BD79700C11021 /* hello.mustache */, 65E646EB1C79AD280036D028 /* hello2.stencil */, + 65E646ED1C79AF4E0036D028 /* test2.stencil */, + 65E646EF1C79AF6C0036D028 /* colored2.stencil */, ); path = views; sourceTree = ""; @@ -422,6 +428,8 @@ buildActionMask = 2147483647; files = ( 65E646EC1C79AD280036D028 /* hello2.stencil in Resources */, + 65E646F01C79AF6C0036D028 /* colored2.stencil in Resources */, + 65E646EE1C79AF4E0036D028 /* test2.stencil in Resources */, 659157FF1C285AFA00672785 /* colored.mustache in Resources */, 657C1A751C24E0F40037041F /* test.mustache in Resources */, 652E5E951C6BD79700C11021 /* hello.mustache in Resources */, From a0b1ba42936d96b80299fed21e1dbf6a48dab14d Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 11:48:18 +0200 Subject: [PATCH 08/88] implemented Stencil include loader --- Demo/views/colored2.stencil | 2 +- Demo/views/test2.stencil | 2 +- Express/StencilViewEngine.swift | 35 ++++++++++++++++++++++++++++----- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Demo/views/colored2.stencil b/Demo/views/colored2.stencil index 4f551d2..01341c0 100644 --- a/Demo/views/colored2.stencil +++ b/Demo/views/colored2.stencil @@ -1 +1 @@ -
  • Item: {{name}}
  • \ No newline at end of file +
  • Item: {{item.name}}
  • \ No newline at end of file diff --git a/Demo/views/test2.stencil b/Demo/views/test2.stencil index aa03831..ea8a9a1 100644 --- a/Demo/views/test2.stencil +++ b/Demo/views/test2.stencil @@ -4,7 +4,7 @@

    Test Stencil: {{test}}

    {% for item in items %} -
  • Item: {{item.name}}
  • +{% include "colored2.stencil" %} {% endfor %}

    diff --git a/Express/StencilViewEngine.swift b/Express/StencilViewEngine.swift index bafcbed..531a8b4 100644 --- a/Express/StencilViewEngine.swift +++ b/Express/StencilViewEngine.swift @@ -23,6 +23,12 @@ import Foundation import PathKit import Stencil +private extension Path { + var containerDir: Path { + return Path(NSString(string: String(self)).stringByDeletingLastPathComponent) + } +} + typealias StencilEdible = [String: Any] private protocol StencilCookable { @@ -37,21 +43,38 @@ extension Dictionary : StencilCookable { } } +private let loaderKey = "loader" + class StencilView : ViewType { let template:Template + let loader:TemplateLoader? - init(template:Template) { + init(template:Template, loader:TemplateLoader? = nil) { self.template = template + self.loader = loader } func render(context:Any?) throws -> AbstractActionType { do { let edibleOption = context.flatMap{$0 as? StencilCookable }?.cook() - guard let edible = edibleOption else { - throw ExpressError.Render(description: "Unable to render supplied context", line: nil, cause: nil) + let contextSupplied:[String:Any] = edibleOption.getOrElse(Dictionary()) + + let loader = contextSupplied.findFirst { (k, v) in + k == loaderKey + }.map{$1} + + if let loader = loader { + guard let loader = loader as? TemplateLoader else { + throw ExpressError.Render(description: "'loader' is a reserved key and can be of TemplateLoader type only", line: nil, cause: nil) + } + print("OK, loader: ", loader) + //TODO: merge loaders } - let stencilContext = Context(dictionary: edible) + let contextLoader:[String:Any] = self.loader.map{["loader": $0]}.getOrElse(Dictionary()) + let finalContext = contextSupplied ++ contextLoader + + let stencilContext = Context(dictionary: finalContext) let render = try template.render(stencilContext) return Action.ok(AnyContent(str:render, contentType: "text/html")) } catch let e as TemplateSyntaxError { @@ -71,8 +94,10 @@ public class StencilViewEngine : ViewEngineType { public func view(filePath:String) throws -> ViewType { do { let path = Path(filePath) + let dir = path.containerDir + let loader = TemplateLoader(paths: [dir]) let template = try Template(path: path) - return StencilView(template: template) + return StencilView(template: template, loader: loader) } catch let e as TemplateSyntaxError { throw ExpressError.Render(description: e.description, line: nil, cause: e) } From 606d85cee5e1f43a1b2feca48569b4b23fce566b Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 11:56:01 +0200 Subject: [PATCH 09/88] Some Linux related wrapping --- Express/MustacheViewEngine.swift | 125 ++++++++++++++++++------------- 1 file changed, 73 insertions(+), 52 deletions(-) diff --git a/Express/MustacheViewEngine.swift b/Express/MustacheViewEngine.swift index d5a543e..06389bb 100644 --- a/Express/MustacheViewEngine.swift +++ b/Express/MustacheViewEngine.swift @@ -20,72 +20,93 @@ //===----------------------------------------------------------------------===// import Foundation -import Mustache -typealias MustacheEdible = AnyObject +private let message = "Mustache rendering engine is not supported on Linux (probably yet). Use Stencil engine if you want to run Express on Linux" +private let warning = "Warning: " + message -private protocol MustacheCookable { - func cook() -> MustacheEdible -} - -extension Dictionary : MustacheCookable { - func cook() -> MustacheEdible { - let s = self.map { (k,v) in - (String(k), v as! AnyObject) - } - let dict:NSDictionary = NSDictionary(dictionary: s) - return dict +#if !os(Linux) + import Mustache + + typealias MustacheEdible = AnyObject + + private protocol MustacheCookable { + func cook() -> MustacheEdible } -} - -class MustacheView : ViewType { - let template:Template - init(template:Template) { - self.template = template + extension Dictionary : MustacheCookable { + func cook() -> MustacheEdible { + let s = self.map { (k,v) in + (String(k), v as! AnyObject) + } + let dict:NSDictionary = NSDictionary(dictionary: s) + return dict + } } - func render(context:Any?) throws -> AbstractActionType { - do { - let anyContext = context.flatMap { (i)->AnyObject? in - if let obj = i as? AnyObject { - return obj - } - guard let cookable = i as? MustacheCookable else { - return nil + class MustacheView : ViewType { + let template:Template + + init(template:Template) { + self.template = template + } + + func render(context:Any?) throws -> AbstractActionType { + do { + let anyContext = context.flatMap { (i)->AnyObject? in + if let obj = i as? AnyObject { + return obj + } + guard let cookable = i as? MustacheCookable else { + return nil + } + return cookable.cook() } - return cookable.cook() - } - let box = Box(anyContext as? MustacheBoxable) - let render = try template.render(box) - return Action.ok(AnyContent(str:render, contentType: "text/html")) - } catch let e as MustacheError { - switch e.kind { - //TODO: double check no such error can be found at this place - //case MustacheError.Kind.TemplateNotFound: throw ExpressError.FileNotFound(filename: <#T##String#>) + let box = Box(anyContext as? MustacheBoxable) + let render = try template.render(box) + return Action.ok(AnyContent(str:render, contentType: "text/html")) + } catch let e as MustacheError { + switch e.kind { + //TODO: double check no such error can be found at this place + //case MustacheError.Kind.TemplateNotFound: throw ExpressError.FileNotFound(filename: <#T##String#>) default: throw ExpressError.Render(description: e.description, line: e.lineNumber, cause: e) + } } } } -} - -public class MustacheViewEngine : ViewEngineType { - public init() { - } - - public func extensions() -> Array { - return ["mustache"] - } - public func view(filePath:String) throws -> ViewType { - do { - let template = try Template(path: filePath) - return MustacheView(template: template) - } catch let e as MustacheError { - switch e.kind { + public class MustacheViewEngine : ViewEngineType { + public init() { + print(warning) + } + + public func extensions() -> Array { + return ["mustache"] + } + + public func view(filePath:String) throws -> ViewType { + do { + let template = try Template(path: filePath) + return MustacheView(template: template) + } catch let e as MustacheError { + switch e.kind { case MustacheError.Kind.TemplateNotFound: throw ExpressError.FileNotFound(filename: filePath) default: throw ExpressError.Render(description: e.description, line: e.lineNumber, cause: e) + } } } } -} \ No newline at end of file +#else + public class MustacheViewEngine : ViewEngineType { + public init() { + print(warning) + } + + public func extensions() -> Array { + return ["mustache"] + } + + public func view(filePath:String) throws -> ViewType { + throw ExpressError.NotImplemented(description: message) + } + } +#endif \ No newline at end of file From 490f04d55af82e0ad6f65864b7bd38e668324732 Mon Sep 17 00:00:00 2001 From: Yehor Popovych Date: Sun, 21 Feb 2016 15:26:54 +0200 Subject: [PATCH 10/88] Package.swift --- .gitignore | 5 +++++ Package.swift | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 Package.swift diff --git a/.gitignore b/.gitignore index 0a40066..17cd41a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,11 @@ +# Xcode *.xcworkspace xcuserdata +# Carthage Cartfile.resolved Carthage +# SPM +.build +Packages diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..8cb1129 --- /dev/null +++ b/Package.swift @@ -0,0 +1,48 @@ +//===--- Package.swift -----------------------------------------------------===// +// +//Copyright (c) 2015-2016 Daniel Leping (dileping) +// +//This file is part of Swift Express. +// +//Swift Express is free software: you can redistribute it and/or modify +//it under the terms of the GNU Lesser General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. +// +//Swift Express is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU Lesser General Public License for more details. +// +//You should have received a copy of the GNU Lesser General Public License +//along with Swift Express. If not, see . +// +//===----------------------------------------------------------------------===// + +import PackageDescription + +let package = Package( + name: "Express", + targets: [ + Target( + name: "Express" + ), + Target( + name:"Demo", + dependencies:[.Target(name:"Express")] + ) + ], + dependencies: [ + .Package(url: "https://github.com/crossroadlabs/BrightFutures.git", majorVersion: 3), + .Package(url: "https://github.com/crossroadlabs/TidyJSON.git", majorVersion: 1), + .Package(url: "https://github.com/crossroadlabs/PathToRegex.git", majorVersion: 0), + .Package(url: "https://github.com/crossroadlabs/Regex.git", majorVersion: 0), + .Package(url: "https://github.com/crossroadlabs/Stencil.git", majorVersion: 0), + ] +) + +#if os(Linux) +package.dependencies.append(.Package(url: "https://github.com/crossroadlabs/CEVHTP.git", majorVersion: 0, minor: 2)) +#else +package.dependencies.append(.Package(url: "https://github.com/crossroadlabs/CEVHTP.git", majorVersion: 0, minor: 1)) +#endif From d1d6452e2b27bca7c0d3899c7d0cc6746acbe9ea Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 15:43:22 +0200 Subject: [PATCH 11/88] removed Dispatch imports --- Express/ExecutionContext.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Express/ExecutionContext.swift b/Express/ExecutionContext.swift index 3e2abad..75ea727 100644 --- a/Express/ExecutionContext.swift +++ b/Express/ExecutionContext.swift @@ -21,7 +21,6 @@ import Foundation import BrightFutures -import Dispatch import ExecutionContext private let cmain:ExecutionContextType = ExecutionContext.main From 64c33e90b3b50fd749028a6682c99577d19db9fa Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 16:18:56 +0200 Subject: [PATCH 12/88] fixed dispatch and NSString conversion --- Express/ExecutionContext.swift | 12 +++++++++++- Express/Static.swift | 4 ++-- Express/Views.swift | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Express/ExecutionContext.swift b/Express/ExecutionContext.swift index 75ea727..bcd47c2 100644 --- a/Express/ExecutionContext.swift +++ b/Express/ExecutionContext.swift @@ -33,6 +33,16 @@ extension ExecutionContext { static let view = toContext(ExecutionContext(kind: .Serial)) @noreturn class func run() { - dispatch_main() + executionContextMain() + } +} + +extension String { + func toNSString() -> NSString { + #if !os(Linux) + return self as NSString + #else + return self.bridge() + #endif } } \ No newline at end of file diff --git a/Express/Static.swift b/Express/Static.swift index ea323d6..11acd25 100644 --- a/Express/Static.swift +++ b/Express/Static.swift @@ -49,8 +49,8 @@ public class StaticAction : Action, IntermediateActionType { } //TODO: get rid of NSs - let file = (self.path as NSString).stringByAppendingPathComponent(fileFromURI) - let ext = (file as NSString).pathExtension + let file = self.path.toNSString().stringByAppendingPathComponent(fileFromURI) + let ext = file.toNSString().pathExtension let fm = NSFileManager.defaultManager() diff --git a/Express/Views.swift b/Express/Views.swift index 9a31de8..f2225ab 100644 --- a/Express/Views.swift +++ b/Express/Views.swift @@ -90,7 +90,7 @@ public class Views { let combinedData = self.paths.map { path in exts.map { ext in - (ext, (path as NSString).stringByAppendingPathComponent(viewName) + "." + ext) + (ext, path.toNSString().stringByAppendingPathComponent(viewName) + "." + ext) } }.flatten() From b5b1304a740e4266996888a3ac293747aa18fd7b Mon Sep 17 00:00:00 2001 From: Yehor Popovych Date: Sun, 21 Feb 2016 16:28:50 +0200 Subject: [PATCH 13/88] Fixed sa_len error --- Express/EVHTP.swift | 23 ++++++++++++++++++++++- Express/HttpServer.swift | 3 +++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Express/EVHTP.swift b/Express/EVHTP.swift index eaad266..7fa7d09 100644 --- a/Express/EVHTP.swift +++ b/Express/EVHTP.swift @@ -23,6 +23,9 @@ import Foundation import CEVHTP import Result import BrightFutures +#if os(Linux) + import Glibc +#endif internal typealias EVHTPp = UnsafeMutablePointer internal typealias EVHTPRequest = UnsafeMutablePointer @@ -185,9 +188,27 @@ private func request_callback(req: EVHTPRequest, callbk: UnsafeMutablePointer) -> Int { + #if os(Linux) + switch Int32(saddr.memory.sa_family) { + case AF_INET: + return strideof(sockaddr_in) + case AF_INET6: + return strideof(sockaddr_in6) + case AF_LOCAL: + return strideof(sockaddr_un) + default: + return 0 + } + #else + return Int(saddr.memory.sa_len) + #endif +} internal class EVHTPRequestInfo { private let req:EVHTPRequest + + var headers: Dictionary { get { @@ -315,7 +336,7 @@ internal class EVHTPRequestInfo { var remoteIp: String { get { let mbuf = UnsafeMutablePointer.alloc(Int(INET6_ADDRSTRLEN)) - let err = getnameinfo(req.memory.conn.memory.saddr, UInt32(req.memory.conn.memory.saddr.memory.sa_len), mbuf, UInt32(INET6_ADDRSTRLEN), nil, 0, NI_NUMERICHOST) + let err = getnameinfo(req.memory.conn.memory.saddr, UInt32(sockaddr_size(req.memory.conn.memory.saddr)), mbuf, UInt32(INET6_ADDRSTRLEN), nil, 0, NI_NUMERICHOST) var res = "" if err == 0 { let t = String.fromCString(mbuf) diff --git a/Express/HttpServer.swift b/Express/HttpServer.swift index 8b86750..0eebeff 100644 --- a/Express/HttpServer.swift +++ b/Express/HttpServer.swift @@ -22,6 +22,9 @@ import Foundation import Result import BrightFutures +#if os(Linux) + import Glibc +#endif private class ServerParams { let promise: Promise From 05b05674da41af42e52a17ca3357289c3514ceee Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 16:56:04 +0200 Subject: [PATCH 14/88] attempt to fix error bringing --- Express/ErrorHandler.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Express/ErrorHandler.swift b/Express/ErrorHandler.swift index 17af1be..eb93a00 100644 --- a/Express/ErrorHandler.swift +++ b/Express/ErrorHandler.swift @@ -57,9 +57,12 @@ public class AggregateErrorHandler : ErrorHandlerType { register { e in //this is the only way to check. Otherwise it will just always tall-free bridge to NSError if e.dynamicType == NSError.self { - let e = e as NSError - //TODO: generalize this shit - return Action.internalServerError(e.description) + //stupid autobridging + switch e { + case let e as NSError: + return Action.internalServerError(e.description) + default: return nil + } } else { return nil } From d04450ac3d4706688220b2896c6a55bdb3997a07 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 19:27:31 +0200 Subject: [PATCH 15/88] Extracted utils --- Express.xcodeproj/project.pbxproj | 4 ++++ Express/ExecutionContext.swift | 10 ---------- Express/Static.swift | 4 ++-- Express/Utils.swift | 30 ++++++++++++++++++++++++++++++ Express/Views.swift | 2 +- 5 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 Express/Utils.swift diff --git a/Express.xcodeproj/project.pbxproj b/Express.xcodeproj/project.pbxproj index 2511bd5..3950d86 100644 --- a/Express.xcodeproj/project.pbxproj +++ b/Express.xcodeproj/project.pbxproj @@ -41,6 +41,7 @@ 657C1A721C24DD1D0037041F /* MustacheViewEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657C1A711C24DD1D0037041F /* MustacheViewEngine.swift */; }; 657C1A751C24E0F40037041F /* test.mustache in Resources */ = {isa = PBXBuildFile; fileRef = 657C1A741C24E0F40037041F /* test.mustache */; }; 657CDBCF1C2A07C5005261BA /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657CDBCE1C2A07C5005261BA /* Errors.swift */; }; + 6589201B1C7A2982008EEE37 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589201A1C7A2982008EEE37 /* Utils.swift */; }; 658DBDA31C221A2A00BE8AA9 /* BrightFutures.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA01C221A2A00BE8AA9 /* BrightFutures.framework */; }; 658DBDA41C221A2A00BE8AA9 /* Result.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA11C221A2A00BE8AA9 /* Result.framework */; }; 658DBDA71C221B0D00BE8AA9 /* BrightFutures.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA01C221A2A00BE8AA9 /* BrightFutures.framework */; }; @@ -142,6 +143,7 @@ 657C1A711C24DD1D0037041F /* MustacheViewEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MustacheViewEngine.swift; sourceTree = ""; }; 657C1A741C24E0F40037041F /* test.mustache */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = test.mustache; sourceTree = ""; }; 657CDBCE1C2A07C5005261BA /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; + 6589201A1C7A2982008EEE37 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 658DBDA01C221A2A00BE8AA9 /* BrightFutures.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BrightFutures.framework; path = Carthage/Build/Mac/BrightFutures.framework; sourceTree = ""; }; 658DBDA11C221A2A00BE8AA9 /* Result.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Result.framework; path = Carthage/Build/Mac/Result.framework; sourceTree = ""; }; 658DBDAE1C22218100BE8AA9 /* Headers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Headers.swift; sourceTree = ""; }; @@ -270,6 +272,7 @@ 65BB8DAA1C2A226E00F8B52C /* ErrorHandler.swift */, 655077451C2D936100E42309 /* Static.swift */, 6550774B1C2DC8E500E42309 /* MIME.swift */, + 6589201A1C7A2982008EEE37 /* Utils.swift */, ); path = Express; sourceTree = ""; @@ -473,6 +476,7 @@ 658DBDB11C2228F000BE8AA9 /* Response.swift in Sources */, 65E646EA1C79A5340036D028 /* StencilViewEngine.swift in Sources */, 655CC27E1C2370F9009FE219 /* Streams+Headers.swift in Sources */, + 6589201B1C7A2982008EEE37 /* Utils.swift in Sources */, 657CDBCF1C2A07C5005261BA /* Errors.swift in Sources */, 650493691C22199300616444 /* AnyContent+JSON.swift in Sources */, 657C1A701C24C8FF0037041F /* ViewEngine.swift in Sources */, diff --git a/Express/ExecutionContext.swift b/Express/ExecutionContext.swift index bcd47c2..45eb35d 100644 --- a/Express/ExecutionContext.swift +++ b/Express/ExecutionContext.swift @@ -36,13 +36,3 @@ extension ExecutionContext { executionContextMain() } } - -extension String { - func toNSString() -> NSString { - #if !os(Linux) - return self as NSString - #else - return self.bridge() - #endif - } -} \ No newline at end of file diff --git a/Express/Static.swift b/Express/Static.swift index 11acd25..73df554 100644 --- a/Express/Static.swift +++ b/Express/Static.swift @@ -49,8 +49,8 @@ public class StaticAction : Action, IntermediateActionType { } //TODO: get rid of NSs - let file = self.path.toNSString().stringByAppendingPathComponent(fileFromURI) - let ext = file.toNSString().pathExtension + let file = self.path.bridge().stringByAppendingPathComponent(fileFromURI) + let ext = file.bridge().pathExtension let fm = NSFileManager.defaultManager() diff --git a/Express/Utils.swift b/Express/Utils.swift new file mode 100644 index 0000000..0d90da5 --- /dev/null +++ b/Express/Utils.swift @@ -0,0 +1,30 @@ +//===--- Utils.swift -------------------------------------------===// +// +//Copyright (c) 2015-2016 Daniel Leping (dileping) +// +//This file is part of Swift Express. +// +//Swift Express is free software: you can redistribute it and/or modify +//it under the terms of the GNU Lesser General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. +// +//Swift Express is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU Lesser General Public License for more details. +// +//You should have received a copy of the GNU Lesser General Public License +//along with Swift Express. If not, see . +// +//===----------------------------------------------------------------------===// + +import Foundation + +#if !os(Linux) + extension String { + func bridge() -> NSString { + return self as NSString + } + } +#endif \ No newline at end of file diff --git a/Express/Views.swift b/Express/Views.swift index f2225ab..cccf15a 100644 --- a/Express/Views.swift +++ b/Express/Views.swift @@ -90,7 +90,7 @@ public class Views { let combinedData = self.paths.map { path in exts.map { ext in - (ext, path.toNSString().stringByAppendingPathComponent(viewName) + "." + ext) + (ext, path.bridge().stringByAppendingPathComponent(viewName) + "." + ext) } }.flatten() From bd97c89d5fb2d37f6f55dfe855c1513e0eaecfbd Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Sun, 21 Feb 2016 19:33:58 +0200 Subject: [PATCH 16/88] made rendering context generic --- Express/Action.swift | 8 ++++---- Express/JsonView.swift | 2 +- Express/MustacheViewEngine.swift | 2 +- Express/StencilViewEngine.swift | 2 +- Express/View.swift | 2 +- Express/Views.swift | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Express/Action.swift b/Express/Action.swift index e645a1a..4c9580b 100644 --- a/Express/Action.swift +++ b/Express/Action.swift @@ -52,11 +52,11 @@ class ResponseAction : Action, FlushableAction { } } -class RenderAction : Action, IntermediateActionType { +class RenderAction : Action, IntermediateActionType { let view:String - let context:Any? + let context:Context? - init(view:String, context:Any?) { + init(view:String, context:Context?) { self.view = view self.context = context } @@ -127,7 +127,7 @@ public extension Action { return chain(nilRequest()) } - public class func render(view:String, context:Any? = nil) -> Action { + public class func render(view:String, context:Context? = nil) -> Action { return RenderAction(view: view, context: context) } } diff --git a/Express/JsonView.swift b/Express/JsonView.swift index f8eb0b0..cbc277c 100644 --- a/Express/JsonView.swift +++ b/Express/JsonView.swift @@ -80,7 +80,7 @@ public class JsonView : NamedViewType { public init() { } - public func render(context:T?) throws -> AbstractActionType { + public func render(context:Context?) throws -> AbstractActionType { //TODO: implement reflection let json = context.flatMap{$0 as? JSONConvertible}.flatMap { $0.toJSON() } //TODO: avoid string path diff --git a/Express/MustacheViewEngine.swift b/Express/MustacheViewEngine.swift index 06389bb..90c179a 100644 --- a/Express/MustacheViewEngine.swift +++ b/Express/MustacheViewEngine.swift @@ -50,7 +50,7 @@ private let warning = "Warning: " + message self.template = template } - func render(context:Any?) throws -> AbstractActionType { + func render(context:Context?) throws -> AbstractActionType { do { let anyContext = context.flatMap { (i)->AnyObject? in if let obj = i as? AnyObject { diff --git a/Express/StencilViewEngine.swift b/Express/StencilViewEngine.swift index 531a8b4..a9b8df6 100644 --- a/Express/StencilViewEngine.swift +++ b/Express/StencilViewEngine.swift @@ -54,7 +54,7 @@ class StencilView : ViewType { self.loader = loader } - func render(context:Any?) throws -> AbstractActionType { + func render(context:C?) throws -> AbstractActionType { do { let edibleOption = context.flatMap{$0 as? StencilCookable }?.cook() let contextSupplied:[String:Any] = edibleOption.getOrElse(Dictionary()) diff --git a/Express/View.swift b/Express/View.swift index d0484b0..4028cd2 100644 --- a/Express/View.swift +++ b/Express/View.swift @@ -22,7 +22,7 @@ import Foundation public protocol ViewType { - func render(context:Any?) throws -> AbstractActionType + func render(context:Context?) throws -> AbstractActionType } public protocol NamedViewType : ViewType { diff --git a/Express/Views.swift b/Express/Views.swift index cccf15a..4104b07 100644 --- a/Express/Views.swift +++ b/Express/Views.swift @@ -121,7 +121,7 @@ public class Views { } } - func render(view:String, context:Any?) -> Future { + func render(view:String, context:Context?) -> Future { return self.view(view).map(renderContext) { view in try view.render(context) } From 1fdf97b86b6ba67dcb47ed12ded5e0aa1ef55b22 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 08:30:49 +0200 Subject: [PATCH 17/88] linux travis --- .travis.yml | 91 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/.travis.yml b/.travis.yml index efaddf3..141cc03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,36 +1,61 @@ -language: objective-c -osx_image: xcode7.2 env: - global: - - FRAMEWORK_NAME=Express -before_install: - - brew update - - brew unlink carthage - - brew install carthage - - brew link carthage - - brew tap crossroadlabs/tap - - brew install libevhtp --without-oniguruma --with-shared -before_script: - # bootstrap the dependencies for the project - # you can remove if you don't have dependencies - - carthage bootstrap --platform osx -before_deploy: - - carthage build --no-skip-current - - carthage archive $FRAMEWORK_NAME - # - pod trunk push PathToRegex.podspec -script: -- xcodebuild build -project $FRAMEWORK_NAME.xcodeproj -scheme $FRAMEWORK_NAME -#- xcodebuild test -project Regex.xcodeproj -scheme Regex-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6' -#- xcodebuild test -project Regex.xcodeproj -scheme Regex-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV 1080p' -#- xcodebuild build -project Regex.xcodeproj -scheme Regex-watchOS -sdk watchsimulator -destination 'platform=watchOS Simulator,name=Apple Watch - 42mm' + global: + - MODULE_NAME=Express +matrix: + include: + - script: + - xcodebuild test -project $MODULE_NAME.xcodeproj -scheme $MODULE_NAME + os: osx + osx_image: xcode7.2 + language: objective-c + before_install: + - brew update + - brew unlink carthage + - brew install carthage + - brew link carthage + - brew tap crossroadlabs/tap + - brew install libevhtp --without-oniguruma --with-shared + before_script: + # bootstrap the dependencies for the project + # you can remove if you don't have dependencies + - carthage bootstrap --platform osx + before_deploy: + - carthage build --no-skip-current + - carthage archive $MODULE_NAME + deploy: + provider: releases + api_key: + secure: NxWvEe4nqcI99oKbtcFtmpz3AVkQiC+4kFu4OcCusAoKimMxx3WJ4+B6anDLG5dj/5rhsl4rQdKr/WIneHodyPrIFQPsRLUV2KFxgd+vbKrXLRRw+sZb//r0j89E7tCEUPdModIKWN4OCR7UWHvZ5syVQFfsP4MH1r+OJcBiewVVVPhgXzvndkxVlgXonyrJ++NAW/g+jO+CYNU9Y3KkyK7BaPMw6/oSC2Ly3A3zU1El++MFz+ew6YDGxiEiw7y20q7/qTT4pdl2HWem1P04UwPyJ2fUw/nD0C+fvX6tnFYX0s9p9VPSnrWXkhSHKT9IGlr8sveRPuQIaJcXcbkMHF8rnCmMpJJ63+YeXV88krW0q2ZyVpSodh/NaJ76PqbXij9WBSZ8AmNJsBlBsjhAgPCfZZkJ3vjrSSH2XP4k0ICLI8sxYmELXzR0MU1LaexE/4KKMDUcl4zuHWQBTJTMvtDyl+AJk+GdPBSGEpCQvHjXAwuW9OAz/sqZrLQy8eQJmji8DVhMJYlCK+krBQFZIekA6Wun5VTmATrtcwd6qwXyMOYeoblh3308zhzgyjBQwfJVpWxLLqB+dyQNLEOYhMcCcTwWNMvz7RYbEMcplkIu5azuUL400pPk2g2wp6U522vsAmH7bra3+VyA0qYG0ckLiwBaBKVCxD7EzkAy5CA= + file: $MODULE_NAME.framework.zip + skip_cleanup: true + on: + repo: crossroadlabs/Express + tags: true + - script: + ./build test + sudo: required + dist: trusty + language: generic + before_install: + # install original swift distribution + - wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import - + - cd .. + - export SWIFT_VERSION=swift-DEVELOPMENT-SNAPSHOT-2016-02-08-a + - wget https://swift.org/builds/development/ubuntu1404/$SWIFT_VERSION/$SWIFT_VERSION-ubuntu14.04.tar.gz + - tar xzf $SWIFT_VERSION-ubuntu14.04.tar.gz + - export PATH="${PWD}/${SWIFT_VERSION}-ubuntu14.04/usr/bin:${PATH}" + # install latest package manager with `swift-test` + - git clone https://github.com/apple/swift-package-manager + - cd swift-package-manager + - Utilities/bootstrap + - export SWIFT_BUILD_PATH=`pwd` + - export PATH=$SWIFT_BUILD_PATH/.build/debug:$PATH + - cd .. + # get back home + - cd $MODULE_NAME + # get crossroad build script + - wget https://raw.githubusercontent.com/crossroadlabs/utils/master/build + - chmod +x build notifications: email: false -deploy: - provider: releases - api_key: - secure: NxWvEe4nqcI99oKbtcFtmpz3AVkQiC+4kFu4OcCusAoKimMxx3WJ4+B6anDLG5dj/5rhsl4rQdKr/WIneHodyPrIFQPsRLUV2KFxgd+vbKrXLRRw+sZb//r0j89E7tCEUPdModIKWN4OCR7UWHvZ5syVQFfsP4MH1r+OJcBiewVVVPhgXzvndkxVlgXonyrJ++NAW/g+jO+CYNU9Y3KkyK7BaPMw6/oSC2Ly3A3zU1El++MFz+ew6YDGxiEiw7y20q7/qTT4pdl2HWem1P04UwPyJ2fUw/nD0C+fvX6tnFYX0s9p9VPSnrWXkhSHKT9IGlr8sveRPuQIaJcXcbkMHF8rnCmMpJJ63+YeXV88krW0q2ZyVpSodh/NaJ76PqbXij9WBSZ8AmNJsBlBsjhAgPCfZZkJ3vjrSSH2XP4k0ICLI8sxYmELXzR0MU1LaexE/4KKMDUcl4zuHWQBTJTMvtDyl+AJk+GdPBSGEpCQvHjXAwuW9OAz/sqZrLQy8eQJmji8DVhMJYlCK+krBQFZIekA6Wun5VTmATrtcwd6qwXyMOYeoblh3308zhzgyjBQwfJVpWxLLqB+dyQNLEOYhMcCcTwWNMvz7RYbEMcplkIu5azuUL400pPk2g2wp6U522vsAmH7bra3+VyA0qYG0ckLiwBaBKVCxD7EzkAy5CA= - file: $FRAMEWORK_NAME.framework.zip - skip_cleanup: true - on: - repo: crossroadlabs/$FRAMEWORK_NAME - tags: true + From 0be9927810490b99577b95e963d06a5a5460491d Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 08:35:24 +0200 Subject: [PATCH 18/88] travis linux fix --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 141cc03..f7aab78 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,6 +56,8 @@ matrix: # get crossroad build script - wget https://raw.githubusercontent.com/crossroadlabs/utils/master/build - chmod +x build + # express dependencies + - sudo apt-get install libevhtp-dev libevent-dev libssl-dev notifications: email: false From 7a7e60ff70aa85bb05067b930405f4d72407c637 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 09:07:46 +0200 Subject: [PATCH 19/88] trusty fix --- .travis.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f7aab78..4906d91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,13 +51,21 @@ matrix: - export SWIFT_BUILD_PATH=`pwd` - export PATH=$SWIFT_BUILD_PATH/.build/debug:$PATH - cd .. + # express dependencies + # - sudo apt-get install libevhtp-dev libevent-dev libssl-dev //libevhtp-dev is not available on trusty + - sudo apt-get install libevent-dev libssl-dev + - git clone https://github.com/ellzey/libevhtp.git + - cd libevhtp + - cd build + - cmake -DEVHTP_DISABLE_REGEX:STRING=ON -DEVHTP_BUILD_SHARED:STRING=ON .. + - make + - sudo make install + - cd ../.. # get back home - cd $MODULE_NAME # get crossroad build script - wget https://raw.githubusercontent.com/crossroadlabs/utils/master/build - chmod +x build - # express dependencies - - sudo apt-get install libevhtp-dev libevent-dev libssl-dev notifications: email: false From f04928d3e559ae27ee5fa2eb5d3ecfd5378eed81 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 09:14:31 +0200 Subject: [PATCH 20/88] linux build fix --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4906d91..95f64b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -57,7 +57,7 @@ matrix: - git clone https://github.com/ellzey/libevhtp.git - cd libevhtp - cd build - - cmake -DEVHTP_DISABLE_REGEX:STRING=ON -DEVHTP_BUILD_SHARED:STRING=ON .. + - cmake -DEVHTP_DISABLE_REGEX:STRING=ON -DEVHTP_BUILD_SHARED:STRING=ON -DCMAKE_INSTALL_PREFIX:STRING=/usr .. - make - sudo make install - cd ../.. From b9e4ac745f1c52afdde98858ebdfdb3f55430254 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 09:21:28 +0200 Subject: [PATCH 21/88] evhtp headers location fix --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 95f64b1..3bbb9d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,6 +60,8 @@ matrix: - cmake -DEVHTP_DISABLE_REGEX:STRING=ON -DEVHTP_BUILD_SHARED:STRING=ON -DCMAKE_INSTALL_PREFIX:STRING=/usr .. - make - sudo make install + - sudo mv -f /usr/include/evhtp/* /usr/include/ + - sudo rm -rf /usr/include/evhtp - cd ../.. # get back home - cd $MODULE_NAME From 04adf2994f8f3e74cb6eb24482593795c5aab967 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 09:41:39 +0200 Subject: [PATCH 22/88] test newer libevent --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 3bbb9d9..a1895c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,6 +53,7 @@ matrix: - cd .. # express dependencies # - sudo apt-get install libevhtp-dev libevent-dev libssl-dev //libevhtp-dev is not available on trusty + - sudo apt-get update - sudo apt-get install libevent-dev libssl-dev - git clone https://github.com/ellzey/libevhtp.git - cd libevhtp From 8cc433cc9da9e45a4c9170f2cab6501c9f75cd54 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 10:26:41 +0200 Subject: [PATCH 23/88] travis debug --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a1895c5..d4e0c25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,7 +32,8 @@ matrix: repo: crossroadlabs/Express tags: true - script: - ./build test + - cat /usr/include/evhtp-config.h + - ./build test sudo: required dist: trusty language: generic From 5c0e2edd213db43754cab34395eacffb4eddb7e5 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 10:30:54 +0200 Subject: [PATCH 24/88] TidyJSON original source --- Cartfile | 2 +- Package.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile b/Cartfile index c30f68f..cd91235 100644 --- a/Cartfile +++ b/Cartfile @@ -1,5 +1,5 @@ github "crossroadlabs/BrightFutures" "feature-express-linux" -github "crossroadlabs/TidyJSON" "master" +github "benloong/TidyJSON" ~> 1.0 github "groue/GRMustache.swift" "Swift2.1" github "crossroadlabs/Regex" ~> 0.5 github "crossroadlabs/PathToRegex" ~> 0.2 diff --git a/Package.swift b/Package.swift index 8cb1129..7cdcba8 100644 --- a/Package.swift +++ b/Package.swift @@ -34,7 +34,7 @@ let package = Package( ], dependencies: [ .Package(url: "https://github.com/crossroadlabs/BrightFutures.git", majorVersion: 3), - .Package(url: "https://github.com/crossroadlabs/TidyJSON.git", majorVersion: 1), + .Package(url: "https://github.com/benloong/TidyJSON.git", majorVersion: 1), .Package(url: "https://github.com/crossroadlabs/PathToRegex.git", majorVersion: 0), .Package(url: "https://github.com/crossroadlabs/Regex.git", majorVersion: 0), .Package(url: "https://github.com/crossroadlabs/Stencil.git", majorVersion: 0), From a6a4b5de5c2ba03e38dd1701d0a90de89e219723 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 10:41:25 +0200 Subject: [PATCH 25/88] event config debug --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index d4e0c25..e539eb3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ matrix: tags: true - script: - cat /usr/include/evhtp-config.h + - cat /usr/include/event2/event-config.h - ./build test sudo: required dist: trusty From 566ad8261481106955b2571f0de1d0861024fb6b Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 10:43:29 +0200 Subject: [PATCH 26/88] xcodebuild fix --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e539eb3..d98f6ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ env: matrix: include: - script: - - xcodebuild test -project $MODULE_NAME.xcodeproj -scheme $MODULE_NAME + - xcodebuild build -project $MODULE_NAME.xcodeproj -scheme $MODULE_NAME os: osx osx_image: xcode7.2 language: objective-c From bbac0e91c10bbd5dcd5a475a62ccb6128c94b488 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 10:56:38 +0200 Subject: [PATCH 27/88] more debug --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index d98f6ca..fd8177c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,6 +34,7 @@ matrix: - script: - cat /usr/include/evhtp-config.h - cat /usr/include/event2/event-config.h + - cat /usr/include/event2/thread.h - ./build test sudo: required dist: trusty From 560c6443f4b2f891396401591ab8b9828091e267 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 13:44:31 +0200 Subject: [PATCH 28/88] libevhtp version --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index fd8177c..68b6b45 100644 --- a/.travis.yml +++ b/.travis.yml @@ -60,6 +60,7 @@ matrix: - sudo apt-get install libevent-dev libssl-dev - git clone https://github.com/ellzey/libevhtp.git - cd libevhtp + - git checkout tags/1.2.10 - cd build - cmake -DEVHTP_DISABLE_REGEX:STRING=ON -DEVHTP_BUILD_SHARED:STRING=ON -DCMAKE_INSTALL_PREFIX:STRING=/usr .. - make From 7e194bf02b2e05828f607eba27d23e9a8f51d15d Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 13:57:51 +0200 Subject: [PATCH 29/88] fixed travis build --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 68b6b45..b6932ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,8 +61,7 @@ matrix: - git clone https://github.com/ellzey/libevhtp.git - cd libevhtp - git checkout tags/1.2.10 - - cd build - - cmake -DEVHTP_DISABLE_REGEX:STRING=ON -DEVHTP_BUILD_SHARED:STRING=ON -DCMAKE_INSTALL_PREFIX:STRING=/usr .. + - cmake -DEVHTP_DISABLE_REGEX:STRING=ON -DEVHTP_BUILD_SHARED:STRING=ON -DCMAKE_INSTALL_PREFIX:STRING=/usr - make - sudo make install - sudo mv -f /usr/include/evhtp/* /usr/include/ From 3d05a25b44be7e9a544d3b34d84314891c580efd Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 14:05:20 +0200 Subject: [PATCH 30/88] travis fix --- .travis.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index b6932ba..708120e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,9 +64,7 @@ matrix: - cmake -DEVHTP_DISABLE_REGEX:STRING=ON -DEVHTP_BUILD_SHARED:STRING=ON -DCMAKE_INSTALL_PREFIX:STRING=/usr - make - sudo make install - - sudo mv -f /usr/include/evhtp/* /usr/include/ - - sudo rm -rf /usr/include/evhtp - - cd ../.. + - cd .. # get back home - cd $MODULE_NAME # get crossroad build script From 081eeb0c0f62998878dd684d5c75e6fba949d468 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 14:20:27 +0200 Subject: [PATCH 31/88] fixed demo --- Demo/main.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Demo/main.swift b/Demo/main.swift index 1cef5be..ed6acfc 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -49,7 +49,9 @@ app.errorHandler.register { e in ["name": k, "color": v] } - return Action.render("test", context: ["test": "error", "items": viewItems]) + let context:[String: Any] = ["test": "error", "items": viewItems] + + return Action.render("test", context: context) } /// StaticAction is just a predefined configurable handler for serving static files. From 6162e6b6246f070c279c088fea5ea254af47f691 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 14:23:42 +0200 Subject: [PATCH 32/88] travis cleanup --- .travis.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 708120e..76a47bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,10 +32,7 @@ matrix: repo: crossroadlabs/Express tags: true - script: - - cat /usr/include/evhtp-config.h - - cat /usr/include/event2/event-config.h - - cat /usr/include/event2/thread.h - - ./build test + - ./build sudo: required dist: trusty language: generic From 62f6e0e8c527d16c28822f08d5b83e07ebb6ed70 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Mon, 22 Feb 2016 14:37:23 +0200 Subject: [PATCH 33/88] back to our fork --- Cartfile | 2 +- Package.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile b/Cartfile index cd91235..e05e960 100644 --- a/Cartfile +++ b/Cartfile @@ -1,5 +1,5 @@ github "crossroadlabs/BrightFutures" "feature-express-linux" -github "benloong/TidyJSON" ~> 1.0 +github "crossroadlabs/TidyJSON" ~> 1.0 github "groue/GRMustache.swift" "Swift2.1" github "crossroadlabs/Regex" ~> 0.5 github "crossroadlabs/PathToRegex" ~> 0.2 diff --git a/Package.swift b/Package.swift index 7cdcba8..8cb1129 100644 --- a/Package.swift +++ b/Package.swift @@ -34,7 +34,7 @@ let package = Package( ], dependencies: [ .Package(url: "https://github.com/crossroadlabs/BrightFutures.git", majorVersion: 3), - .Package(url: "https://github.com/benloong/TidyJSON.git", majorVersion: 1), + .Package(url: "https://github.com/crossroadlabs/TidyJSON.git", majorVersion: 1), .Package(url: "https://github.com/crossroadlabs/PathToRegex.git", majorVersion: 0), .Package(url: "https://github.com/crossroadlabs/Regex.git", majorVersion: 0), .Package(url: "https://github.com/crossroadlabs/Stencil.git", majorVersion: 0), From 1e72590b12671a4a5e2f039e801a1df791a4bfa7 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Thu, 25 Feb 2016 11:06:39 +0200 Subject: [PATCH 34/88] updated TidyJSON --- Cartfile | 2 +- Express/AnyContent+JSON.swift | 19 ++++++++++++------- Package.swift | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Cartfile b/Cartfile index e05e960..eb31f1f 100644 --- a/Cartfile +++ b/Cartfile @@ -1,5 +1,5 @@ github "crossroadlabs/BrightFutures" "feature-express-linux" -github "crossroadlabs/TidyJSON" ~> 1.0 +github "crossroadlabs/TidyJSON" ~> 1.1 github "groue/GRMustache.swift" "Swift2.1" github "crossroadlabs/Regex" ~> 0.5 github "crossroadlabs/PathToRegex" ~> 0.2 diff --git a/Express/AnyContent+JSON.swift b/Express/AnyContent+JSON.swift index 7bdff85..fe51000 100644 --- a/Express/AnyContent+JSON.swift +++ b/Express/AnyContent+JSON.swift @@ -27,16 +27,21 @@ public extension AnyContent { for ct in contentType { //TODO: move to constants if "application/json" == ct { - guard let (json, error) = self.asText().map(JSON.parse) else { - return Optional.None - } - if let e = error { + do { + guard let text = self.asText() else { + return nil + } + let json = try JSON.parse(text) + return json + } catch let e as TidyJSON.ParseError { + print(e) + return nil + } catch let e { print(e) - return Optional.None + return nil } - return json } } - return Optional.None + return nil } } \ No newline at end of file diff --git a/Package.swift b/Package.swift index 8cb1129..b784886 100644 --- a/Package.swift +++ b/Package.swift @@ -34,7 +34,7 @@ let package = Package( ], dependencies: [ .Package(url: "https://github.com/crossroadlabs/BrightFutures.git", majorVersion: 3), - .Package(url: "https://github.com/crossroadlabs/TidyJSON.git", majorVersion: 1), + .Package(url: "https://github.com/crossroadlabs/TidyJSON.git", majorVersion: 1, minor: 1), .Package(url: "https://github.com/crossroadlabs/PathToRegex.git", majorVersion: 0), .Package(url: "https://github.com/crossroadlabs/Regex.git", majorVersion: 0), .Package(url: "https://github.com/crossroadlabs/Stencil.git", majorVersion: 0), From 32534b84b88c94f98d8456f4990c5d82590c17de Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Thu, 25 Feb 2016 12:44:19 +0200 Subject: [PATCH 35/88] proper futures versions --- Cartfile | 2 +- Package.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cartfile b/Cartfile index eb31f1f..57e377d 100644 --- a/Cartfile +++ b/Cartfile @@ -1,4 +1,4 @@ -github "crossroadlabs/BrightFutures" "feature-express-linux" +github "crossroadlabs/BrightFutures" ~> 0.4 github "crossroadlabs/TidyJSON" ~> 1.1 github "groue/GRMustache.swift" "Swift2.1" github "crossroadlabs/Regex" ~> 0.5 diff --git a/Package.swift b/Package.swift index b784886..07b0194 100644 --- a/Package.swift +++ b/Package.swift @@ -33,7 +33,7 @@ let package = Package( ) ], dependencies: [ - .Package(url: "https://github.com/crossroadlabs/BrightFutures.git", majorVersion: 3), + .Package(url: "https://github.com/crossroadlabs/BrightFutures.git", majorVersion: 0, minor: 4), .Package(url: "https://github.com/crossroadlabs/TidyJSON.git", majorVersion: 1, minor: 1), .Package(url: "https://github.com/crossroadlabs/PathToRegex.git", majorVersion: 0), .Package(url: "https://github.com/crossroadlabs/Regex.git", majorVersion: 0), From d9e4900cb0aeadfa70b00a9bb95437e73b3a86f7 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Thu, 25 Feb 2016 13:38:45 +0200 Subject: [PATCH 36/88] Added page not found on route not found --- Express/ExpressServer.swift | 2 +- Express/HttpServer.swift | 59 ++++++++++++++++++++++--------------- Express/Server.swift | 4 +-- Express/Transaction.swift | 20 +++++++------ 4 files changed, 50 insertions(+), 35 deletions(-) diff --git a/Express/ExpressServer.swift b/Express/ExpressServer.swift index b675950..5661c85 100644 --- a/Express/ExpressServer.swift +++ b/Express/ExpressServer.swift @@ -26,7 +26,7 @@ import BrightFutures public extension Express { func listen(port:UInt16) -> Future { - let server = HttpServer(router: self) + let server = HttpServer(app: self) return server.start(port) } diff --git a/Express/HttpServer.swift b/Express/HttpServer.swift index 0eebeff..249be49 100644 --- a/Express/HttpServer.swift +++ b/Express/HttpServer.swift @@ -25,16 +25,17 @@ import BrightFutures #if os(Linux) import Glibc #endif +import ExecutionContext private class ServerParams { let promise: Promise let port: UInt16 - let router:RouterType + let app:Express - init(promise: Promise, port: UInt16, router: RouterType) { + init(promise: Promise, port: UInt16, app: Express) { self.promise = promise self.port = port - self.router = router + self.app = app } } @@ -77,27 +78,39 @@ private func handle_request(req: EVHTPRequest, serv:ServerParams) { let head = RequestHead(method: info.method, version: info.version, remoteAddress: info.remoteIp, secure: info.scheme == "HTTPS", uri: info.uri, path: info.path, query: info.query, headers: info.headers, params: Dictionary()) let os = ResponseDataConsumer(sock: req) - guard let routeTuple = serv.router.firstRoute(head) else { - //TODO: implement page not found (throw exception?, see other places) - print("Route not found for request: ", head.path) - return + let routeTuple = serv.app.firstRoute(head) + let transaction = routeTuple.map { + ($0.0, head.withParams($0.1)) + }.map { (let route, let header) in + route.factory(header, os) } + + + /* .getOrElse(Transaction(app: serv.app, routeId: "", head: head, out: os)) let route = routeTuple.0 - let header = head.withParams(routeTuple.1) + let header = head.withParams(routeTuple.1)*/ - let transaction = route.factory(header, os) - transaction.selfProcess() - EVHTP.read_data(req, cb: { data in - if data.count > 0 { - //TODO: handle consumption success or error - transaction.consume(data) - } else { - //TODO: handle errors (for now silencing it with try!) - try! transaction.dataEnd() + if let transaction = transaction { + transaction.selfProcess() + EVHTP.read_data(req, cb: { data in + if data.count > 0 { + //TODO: handle consumption success or error + transaction.consume(data) + } else { + //TODO: handle errors (for now silencing it with try!) + try! transaction.dataEnd() + } + return true + }) + } else { + let transaction = Transaction(app: serv.app, routeId: "", head: head, out: os) + let action = future(immediate) { () throws -> AbstractActionType in + throw ExpressError.PageNotFound(path: head.path) } - return true - }) + transaction.handleAction(action) + try! transaction.dataEnd() + } } private func server_thread(pm: UnsafeMutablePointer) -> UnsafeMutablePointer { @@ -119,18 +132,18 @@ private func server_thread(pm: UnsafeMutablePointer) -> UnsafeMutablePoint } class HttpServer : ServerType { - let router:RouterType + let app:Express let thread: UnsafeMutablePointer func start(port:UInt16) -> Future { - let params = ServerParams(promise: Promise(), port: port, router: router) + let params = ServerParams(promise: Promise(), port: port, app: app) pthread_create(thread, nil, server_thread, UnsafeMutablePointer(Unmanaged.passRetained(params).toOpaque())) return params.promise.future } - required init(router:RouterType) { - self.router = router + required init(app:Express) { + self.app = app self.thread = UnsafeMutablePointer.alloc(1) } deinit { diff --git a/Express/Server.swift b/Express/Server.swift index f8bc453..bc9cb4d 100644 --- a/Express/Server.swift +++ b/Express/Server.swift @@ -24,12 +24,12 @@ import BrightFutures import Result protocol ServerType { - var router:RouterType { + var app:Express { get } func start(port:UInt16) -> Future - init(router:RouterType) + init(app:Express) } diff --git a/Express/Transaction.swift b/Express/Transaction.swift index 9e375bf..dad0fb6 100644 --- a/Express/Transaction.swift +++ b/Express/Transaction.swift @@ -53,27 +53,29 @@ class Transaction, NoError>() + content.onSuccess(ExecutionContext.user) { content in + let request = Request(head: head, body: content as? RequestContent) + self.request.success(request) + } + content.onFailure { e in + self.actionPromise.failure(AnyError(cause: e)) + } } convenience init(app:Express, routeId:String, head:RequestHeadType, out:DataConsumerType, handler:Request -> Future, E>) { self.init(app: app, routeId: routeId, head: head, out: out) - content.onSuccess(ExecutionContext.user) { content in - let request = Request(head: head, body: content as? RequestContent) - self.request.success(request) + request.future.onSuccess { request in let action = handler(request) action.onSuccess { action in self.actionPromise.success(action) } action.onFailure { e in switch e { - case let e as AnyError: self.failAction(e.cause) - default: self.failAction(e) + case let e as AnyError: self.failAction(e.cause) + default: self.failAction(e) } } } - content.onFailure { e in - self.actionPromise.failure(AnyError(cause: e)) - } } func failAction(e:ErrorType) { @@ -92,7 +94,7 @@ class Transaction Date: Thu, 25 Feb 2016 13:44:32 +0200 Subject: [PATCH 37/88] More informative error on path not found --- Express/Action.swift | 5 +++++ Express/Errors.swift | 2 ++ Express/HttpServer.swift | 2 +- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Express/Action.swift b/Express/Action.swift index 4c9580b..cbac061 100644 --- a/Express/Action.swift +++ b/Express/Action.swift @@ -110,6 +110,11 @@ public extension Action { return ResponseAction(response: response) } + internal class func routeNotFound(path:String) -> Action { + let response = Response(status: 404, content: AnyContent(str: "404 Route Not Found\n\n\tpath: " + path), headers: Dictionary()) + return ResponseAction(response: response) + } + internal class func internalServerError(description:String) -> Action { let response = Response(status: 500, content: AnyContent(str: "500 Internal Server Error\n\n" + description), headers: Dictionary()) return ResponseAction(response: response) diff --git a/Express/Errors.swift b/Express/Errors.swift index b6da9e9..02244d1 100644 --- a/Express/Errors.swift +++ b/Express/Errors.swift @@ -28,6 +28,7 @@ public enum ExpressError : ErrorType { case NotImplemented(description:String) case FileNotFound(filename:String) case PageNotFound(path:String) + case RouteNotFound(path:String) case NoSuchView(name:String) case Render(description:String, line:Int?, cause:ErrorType?) } @@ -42,6 +43,7 @@ func ExpressErrorHandler(e:ErrorType) -> AbstractActionType? { case .FileNotFound(let filename): return Action.internalServerError("File not found: " + filename) case .NoSuchView(let name): return Action.internalServerError("View not found: " + name) case .PageNotFound(let path): return Action.notFound(path) + case .RouteNotFound(let path): return Action.routeNotFound(path) case .Render(var description, line: let line, cause: let e): description += "\n\n" if (line != nil) { diff --git a/Express/HttpServer.swift b/Express/HttpServer.swift index 249be49..578b9de 100644 --- a/Express/HttpServer.swift +++ b/Express/HttpServer.swift @@ -106,7 +106,7 @@ private func handle_request(req: EVHTPRequest, serv:ServerParams) { } else { let transaction = Transaction(app: serv.app, routeId: "", head: head, out: os) let action = future(immediate) { () throws -> AbstractActionType in - throw ExpressError.PageNotFound(path: head.path) + throw ExpressError.RouteNotFound(path: head.path) } transaction.handleAction(action) try! transaction.dataEnd() From 0eed1ea16d484e005e72413a6b3bdf8447fa02a6 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Thu, 25 Feb 2016 16:48:48 +0200 Subject: [PATCH 38/88] Implemented form url encoded + mergedQuery --- Demo/main.swift | 4 +++ Express/AnyContent+FormUrlEncoded.swift | 36 +++++++++++++++++++++++-- Express/EVHTP.swift | 10 ++++++- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/Demo/main.swift b/Demo/main.swift index ed6acfc..9d04674 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -243,6 +243,10 @@ app.all("/async/echo") { request in } } +app.post("/merged/query") { request in + Action.render(JsonView.name, context: request.mergedQuery()) +} + app.listen(9999).onSuccess { print("Successfully launched server") } diff --git a/Express/AnyContent+FormUrlEncoded.swift b/Express/AnyContent+FormUrlEncoded.swift index 2e45817..8e4b83c 100644 --- a/Express/AnyContent+FormUrlEncoded.swift +++ b/Express/AnyContent+FormUrlEncoded.swift @@ -22,7 +22,39 @@ import Foundation public extension AnyContent { - func asFormUrlEncoded() throws -> Dictionary>? { - throw ExpressError.NotImplemented(description: "form url encoded parsing is not implemented yet") + func asFormUrlEncoded() -> Dictionary>? { + for ct in contentType { + //TODO: move to constants + if "application/x-www-form-urlencoded" == ct { + return asText().map(evhtp_parse_query) + } + } + return nil + } +} + +public extension Request where C : AnyContent { + public func mergedQuery() -> Dictionary> { + guard let bodyQuery = body?.asFormUrlEncoded() else { + return query + } + + let urlKeys = Set(query.keys) + let keys = Array(urlKeys.union(bodyQuery.keys)) + + let merged = keys.map { key -> (String, Array) in + let allValues = query[key].map { urlValues in + bodyQuery[key].flatMap { bodyValues in + return urlValues + bodyValues + }.getOrElse { + urlValues + } + }.getOrElse { + bodyQuery[key]! + } + + return (key, allValues) + } + return toMap(merged) } } \ No newline at end of file diff --git a/Express/EVHTP.swift b/Express/EVHTP.swift index 7fa7d09..b71223f 100644 --- a/Express/EVHTP.swift +++ b/Express/EVHTP.swift @@ -446,4 +446,12 @@ internal class _evhtp { } } -internal let EVHTP = _evhtp() \ No newline at end of file +internal let EVHTP = _evhtp() + +func evhtp_parse_query(query:String) -> [String: [String]] { + let parsed = evhtp_parse_query_wflags(query, query.utf8.count,EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX | EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS | EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS | EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP) + defer { + evhtp_kvs_free(parsed) + } + return RepeatingHeaderDict.fromHeaders(parsed).dict +} \ No newline at end of file From 14773abd28bdbb7d6cdda355f6e0d9d9e70aa2a9 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Thu, 25 Feb 2016 18:48:51 +0200 Subject: [PATCH 39/88] Added status codes --- Express/StatusCode.swift | 62 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Express/StatusCode.swift diff --git a/Express/StatusCode.swift b/Express/StatusCode.swift new file mode 100644 index 0000000..83a690a --- /dev/null +++ b/Express/StatusCode.swift @@ -0,0 +1,62 @@ +//===--- StatusCode.swift -------------------------------------------------===// +// +//Copyright (c) 2015-2016 Daniel Leping (dileping) +// +//This file is part of Swift Express. +// +//Swift Express is free software: you can redistribute it and/or modify +//it under the terms of the GNU Lesser General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. +// +//Swift Express is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU Lesser General Public License for more details. +// +//You should have received a copy of the GNU Lesser General Public License +//along with Swift Express. If not, see . +// +//===----------------------------------------------------------------------===// + +import Foundation + +public enum StatusCode : UInt16 { + case Accepted = 202 + case BadRequest = 400 + case Conflict = 409 + case Created = 201 + case EntityTooLarge = 413 + case ExpectationFailed = 417 + case Forbidden = 403 + case Found = 302 + case Gone = 410 + case InternalServerError = 500 + case MethodNotAllowed = 405 + case MovedPermanently = 301 + case NoContent = 204 + case NonAuthoritativeInformation = 203 + case NotAcceptable = 406 + case NotFound = 404 + case NotImplemented = 501 + case NotModified = 304 + case Ok = 200 + case PartialContent = 206 + case PreconditionFailed = 412 + case RequestTimeout = 408 + case ResetContent = 205 + case SeeOther = 303 + case ServiceUnavailable = 503 + case TemporaryRedirect = 307 + case TooManyRequest = 429 + case Unauthorized = 401 + case UnsupportedMediaType = 415 + case UriTooLong = 414 +} + +public enum RedirectStatusCode : UInt16 { + case MovedPermanently = 301 + case Found = 302 + case SeeOther = 303 + case TemporaryRedirect = 307 +} \ No newline at end of file From f587ffb5c038521bbd51f5db7558b18f3f7ebd00 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Thu, 25 Feb 2016 18:49:53 +0200 Subject: [PATCH 40/88] implemented status and redirect --- Demo/main.swift | 7 +++++++ Express.xcodeproj/project.pbxproj | 4 ++++ Express/Action.swift | 30 ++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/Demo/main.swift b/Demo/main.swift index 9d04674..0fb1d67 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -243,6 +243,13 @@ app.all("/async/echo") { request in } } +app.get("/test/redirect") { request in + return future { + let to = request.query["to"].flatMap{$0.first}.getOrElse("../test.html") + return Action.redirect(to) + } +} + app.post("/merged/query") { request in Action.render(JsonView.name, context: request.mergedQuery()) } diff --git a/Express.xcodeproj/project.pbxproj b/Express.xcodeproj/project.pbxproj index 3950d86..723cc66 100644 --- a/Express.xcodeproj/project.pbxproj +++ b/Express.xcodeproj/project.pbxproj @@ -65,6 +65,7 @@ 65DEAFF61C6A8F3900EBA47F /* PathToRegex.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65DEAFF01C6A8EA700EBA47F /* PathToRegex.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 65DEAFF71C6A8F3900EBA47F /* Regex.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65DEAFF11C6A8EA700EBA47F /* Regex.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 65DEAFF91C6AA8F700EBA47F /* RegexUrlMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DEAFF81C6AA8F700EBA47F /* RegexUrlMatcher.swift */; }; + 65DFE10F1C7F5E4100B5F93D /* StatusCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65DFE10E1C7F5E4100B5F93D /* StatusCode.swift */; }; 65E646DB1C79878E0036D028 /* ExecutionContext.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646DA1C79878E0036D028 /* ExecutionContext.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 65E646E31C79A4EB0036D028 /* PathKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646E11C79A4EB0036D028 /* PathKit.framework */; }; 65E646E41C79A4EB0036D028 /* Stencil.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646E21C79A4EB0036D028 /* Stencil.framework */; }; @@ -157,6 +158,7 @@ 65DEAFF01C6A8EA700EBA47F /* PathToRegex.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PathToRegex.framework; path = Carthage/Build/Mac/PathToRegex.framework; sourceTree = ""; }; 65DEAFF11C6A8EA700EBA47F /* Regex.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Regex.framework; path = Carthage/Build/Mac/Regex.framework; sourceTree = ""; }; 65DEAFF81C6AA8F700EBA47F /* RegexUrlMatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RegexUrlMatcher.swift; sourceTree = ""; }; + 65DFE10E1C7F5E4100B5F93D /* StatusCode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusCode.swift; sourceTree = ""; }; 65E646DA1C79878E0036D028 /* ExecutionContext.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ExecutionContext.framework; path = Carthage/Build/Mac/ExecutionContext.framework; sourceTree = ""; }; 65E646E11C79A4EB0036D028 /* PathKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PathKit.framework; path = Carthage/Build/Mac/PathKit.framework; sourceTree = ""; }; 65E646E21C79A4EB0036D028 /* Stencil.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Stencil.framework; path = Carthage/Build/Mac/Stencil.framework; sourceTree = ""; }; @@ -273,6 +275,7 @@ 655077451C2D936100E42309 /* Static.swift */, 6550774B1C2DC8E500E42309 /* MIME.swift */, 6589201A1C7A2982008EEE37 /* Utils.swift */, + 65DFE10E1C7F5E4100B5F93D /* StatusCode.swift */, ); path = Express; sourceTree = ""; @@ -488,6 +491,7 @@ 650493751C22199300616444 /* Express.swift in Sources */, 6550774C1C2DC8E500E42309 /* MIME.swift in Sources */, 65BA1E981C25D38200BCE076 /* JsonView.swift in Sources */, + 65DFE10F1C7F5E4100B5F93D /* StatusCode.swift in Sources */, 65662D081C3337C300A16177 /* AnyContent+XML.swift in Sources */, 961B9E911C2324B4000506D2 /* EVHTP.swift in Sources */, 6504936F1C22199300616444 /* HttpServer.swift in Sources */, diff --git a/Express/Action.swift b/Express/Action.swift index cbac061..1638f9b 100644 --- a/Express/Action.swift +++ b/Express/Action.swift @@ -135,6 +135,36 @@ public extension Action { public class func render(view:String, context:Context? = nil) -> Action { return RenderAction(view: view, context: context) } + + public class func response(response:Response) -> Action { + return ResponseAction(response: response) + } + + public class func response(status:UInt16, content:C? = nil, headers:Dictionary = Dictionary()) -> Action { + return response(Response(status: status, content: content, headers: headers)) + } + + public class func response(status:StatusCode, content:C? = nil, headers:Dictionary = Dictionary()) -> Action { + return response(status.rawValue, content: content, headers: headers) + } + + public class func status(status:UInt16) -> Action { + return response(status) + } + + public class func status(status:StatusCode) -> Action { + return self.status(status.rawValue) + } + + public class func redirect(url:String, status:RedirectStatusCode) -> Action { + let headers = ["Location": url] + return response(status.rawValue, headers: headers) + } + + public class func redirect(url:String, permanent:Bool = false) -> Action { + let code:RedirectStatusCode = permanent ? .MovedPermanently : .TemporaryRedirect + return redirect(url, status: code) + } } public extension Action where C : AnyContent { From 061572cd93b3f8be6c81b1e333868394b3523435 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Thu, 25 Feb 2016 18:59:58 +0200 Subject: [PATCH 41/88] more redirect types --- Express/Action.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Express/Action.swift b/Express/Action.swift index 1638f9b..b516af2 100644 --- a/Express/Action.swift +++ b/Express/Action.swift @@ -165,6 +165,22 @@ public extension Action { let code:RedirectStatusCode = permanent ? .MovedPermanently : .TemporaryRedirect return redirect(url, status: code) } + + public class func found(url:String) -> Action { + return redirect(url, status: .Found) + } + + public class func movedPermanently(url:String) -> Action { + return redirect(url, status: .MovedPermanently) + } + + public class func seeOther(url:String) -> Action { + return redirect(url, status: .SeeOther) + } + + public class func temporaryRedirect(url:String) -> Action { + return redirect(url, status: .TemporaryRedirect) + } } public extension Action where C : AnyContent { From db041522e193b2fa124aa57543c5d1d8919efe86 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Thu, 25 Feb 2016 19:09:22 +0200 Subject: [PATCH 42/88] Added fahrenheit status code --- Express/StatusCode.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Express/StatusCode.swift b/Express/StatusCode.swift index 83a690a..68f76de 100644 --- a/Express/StatusCode.swift +++ b/Express/StatusCode.swift @@ -51,6 +51,7 @@ public enum StatusCode : UInt16 { case TooManyRequest = 429 case Unauthorized = 401 case UnsupportedMediaType = 415 + case UnavailableForLegalReasons = 451 //farenheit case UriTooLong = 414 } From 5921dc6ab27d75d5e3ceac3ce0c6f255692e9a68 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Thu, 25 Feb 2016 19:20:11 +0200 Subject: [PATCH 43/88] added dispatch note --- Express/ExpressServer.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Express/ExpressServer.swift b/Express/ExpressServer.swift index 5661c85..93ff13a 100644 --- a/Express/ExpressServer.swift +++ b/Express/ExpressServer.swift @@ -31,6 +31,9 @@ public extension Express { } @noreturn func run() { + #if os(Linux) && !dispatch + print("Note: Express is currently running without dispatch support. We have implemented this mode to support Linux developers while Dispatch for Linux is not available out of the box. Consider it to be development mode only and not suitable for production as it might cause occasional hanging and crashes. Still, there is a possibility to build Express with dispatch support (recommended for production use). Follow this link for more info: https://github.com/crossroadlabs/Express") + #endif ExecutionContext.run() } } \ No newline at end of file From 2973daa2c7a98437e36b2971568e7077be1d7d1c Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 10:43:18 +0200 Subject: [PATCH 44/88] Fixed stencil --- Express/StencilViewEngine.swift | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Express/StencilViewEngine.swift b/Express/StencilViewEngine.swift index a9b8df6..9b27ca2 100644 --- a/Express/StencilViewEngine.swift +++ b/Express/StencilViewEngine.swift @@ -35,10 +35,37 @@ private protocol StencilCookable { func cook() -> StencilEdible } +private protocol StencilNormalizable { + func normalizeValue(value:Any) -> Any + func normalize() -> Any +} + +extension StencilNormalizable { + func normalizeValue(value:Any) -> Any { + let normalizable = value as? StencilNormalizable + return normalizable.map {$0.normalize()} .getOrElse(value) + } +} + +extension Array : StencilNormalizable { + func normalize() -> Any { + return self.map(normalizeValue) + } +} + +extension Dictionary : StencilNormalizable { + func normalize() -> Any { + let normalized = self.map { (k, v) in + (k, v as Any) + } + return toMap(normalized) + } +} + extension Dictionary : StencilCookable { func cook() -> StencilEdible { return self.map { (k,v) in - (String(k), v) + return (String(k), normalizeValue(v)) } } } From 2391e19e35497600e42a245ea47b0b1cbb6044fa Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 10:52:33 +0200 Subject: [PATCH 45/88] better stencil fix --- Express/StencilViewEngine.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Express/StencilViewEngine.swift b/Express/StencilViewEngine.swift index 9b27ca2..28635a4 100644 --- a/Express/StencilViewEngine.swift +++ b/Express/StencilViewEngine.swift @@ -56,7 +56,7 @@ extension Array : StencilNormalizable { extension Dictionary : StencilNormalizable { func normalize() -> Any { let normalized = self.map { (k, v) in - (k, v as Any) + (k, normalizeValue(v)) } return toMap(normalized) } From 4f8a0183d398c7757ae6b1fceee3781b9f630f38 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 11:49:00 +0200 Subject: [PATCH 46/88] type safe error handlers --- Demo/main.swift | 11 +++++++++++ Demo/views/404.stencil | 7 +++++++ Express.xcodeproj/project.pbxproj | 4 ++++ Express/ErrorHandler.swift | 9 +++++++++ 4 files changed, 31 insertions(+) create mode 100644 Demo/views/404.stencil diff --git a/Demo/main.swift b/Demo/main.swift index 0fb1d67..001d507 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -54,6 +54,17 @@ app.errorHandler.register { e in return Action.render("test", context: context) } + +/// Custom page not found error handler +app.errorHandler.register { (e:ExpressError) in + switch e { + case .PageNotFound(let path): + return Action.render("404", context: ["path": path]) + default: + return nil + } +} + /// StaticAction is just a predefined configurable handler for serving static files. /// It's important to pass exactly the same param name to it from the url pattern. app.get("/:file+", action: StaticAction(path: "public", param:"file")) diff --git a/Demo/views/404.stencil b/Demo/views/404.stencil new file mode 100644 index 0000000..5f63434 --- /dev/null +++ b/Demo/views/404.stencil @@ -0,0 +1,7 @@ + + +

    +

    404: Page not found

    +

    {{path}}

    + + \ No newline at end of file diff --git a/Express.xcodeproj/project.pbxproj b/Express.xcodeproj/project.pbxproj index 723cc66..1775534 100644 --- a/Express.xcodeproj/project.pbxproj +++ b/Express.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 650493771C22199300616444 /* ExpressSugar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 650493661C22199300616444 /* ExpressSugar.swift */; }; 6518267F1C22081600E49AC5 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6518267E1C22081600E49AC5 /* main.swift */; }; 651826801C2208A800E49AC5 /* Express.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 651475CE1C21F1550049825D /* Express.framework */; }; + 651A23451C80552100A8480E /* 404.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 651A23441C80552100A8480E /* 404.stencil */; }; 6525AC541C24E3C600131F58 /* Mustache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; }; 6525AC551C24E3E000131F58 /* Mustache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; }; 6525AC561C24E3E700131F58 /* Mustache.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -130,6 +131,7 @@ 651826711C2207D200E49AC5 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6518267A1C2207D200E49AC5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6518267E1C22081600E49AC5 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; + 651A23441C80552100A8480E /* 404.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = 404.stencil; sourceTree = ""; }; 6525AC531C24E3C600131F58 /* Mustache.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Mustache.framework; path = Carthage/Build/Mac/Mustache.framework; sourceTree = ""; }; 652E5E941C6BD79700C11021 /* hello.mustache */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = hello.mustache; sourceTree = ""; }; 655077451C2D936100E42309 /* Static.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Static.swift; sourceTree = ""; }; @@ -308,6 +310,7 @@ 65E646EB1C79AD280036D028 /* hello2.stencil */, 65E646ED1C79AF4E0036D028 /* test2.stencil */, 65E646EF1C79AF6C0036D028 /* colored2.stencil */, + 651A23441C80552100A8480E /* 404.stencil */, ); path = views; sourceTree = ""; @@ -436,6 +439,7 @@ 65E646EC1C79AD280036D028 /* hello2.stencil in Resources */, 65E646F01C79AF6C0036D028 /* colored2.stencil in Resources */, 65E646EE1C79AF4E0036D028 /* test2.stencil in Resources */, + 651A23451C80552100A8480E /* 404.stencil in Resources */, 659157FF1C285AFA00672785 /* colored.mustache in Resources */, 657C1A751C24E0F40037041F /* test.mustache in Resources */, 652E5E951C6BD79700C11021 /* hello.mustache in Resources */, diff --git a/Express/ErrorHandler.swift b/Express/ErrorHandler.swift index eb93a00..09469e4 100644 --- a/Express/ErrorHandler.swift +++ b/Express/ErrorHandler.swift @@ -78,6 +78,15 @@ public class AggregateErrorHandler : ErrorHandlerType { register(FunctionErrorHandler(fun: f)) } + public func register(f:E -> AbstractActionType?) { + register { e in + guard let e = e as? E else { + return nil + } + return f(e) + } + } + public func handle(e:ErrorType) -> AbstractActionType? { for handler in handlers { if let action = handler.handle(e) { From 058c2fa554e009f72a0b5f11b513bdfccef475a2 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 12:41:49 +0200 Subject: [PATCH 47/88] views refactoring --- Express/Action.swift | 14 +++++++++++++- Express/Content.swift | 12 ++++++++++++ Express/JsonView.swift | 4 ++-- Express/MustacheViewEngine.swift | 4 ++-- Express/Response.swift | 19 +++++++++++++++---- Express/StencilViewEngine.swift | 4 ++-- Express/View.swift | 2 +- Express/Views.swift | 2 +- 8 files changed, 48 insertions(+), 13 deletions(-) diff --git a/Express/Action.swift b/Express/Action.swift index b516af2..7952f7e 100644 --- a/Express/Action.swift +++ b/Express/Action.swift @@ -52,6 +52,16 @@ class ResponseAction : Action, FlushableAction { } } +extension ResponseAction where C : FlushableContent { + convenience init(response:ResponseType) { + let content = response.content.map { content in + C(content: content) + } + let mappedResponse = Response(status: response.status, content: content, headers: response.headers) + self.init(response: mappedResponse) + } +} + class RenderAction : Action, IntermediateActionType { let view:String let context:Context? @@ -62,7 +72,9 @@ class RenderAction : Action, IntermediateA } func nextAction(app:Express, routeId:String, request:Request, out:DataConsumerType) -> Future { - return app.views.render(view, context: context) + return app.views.render(view, context: context).map { response in + ResponseAction(response: response) + } } } diff --git a/Express/Content.swift b/Express/Content.swift index bb93dbb..c7640da 100644 --- a/Express/Content.swift +++ b/Express/Content.swift @@ -49,6 +49,18 @@ public class ContentFactoryBase { public protocol FlushableContentType : ContentType, FlushableType { } +public class FlushableContent : FlushableContentType { + let content:FlushableContentType + + public required init(content:FlushableContentType) { + self.content = content + } + + public func flushTo(out:DataConsumerType) -> Future { + return content.flushTo(out) + } +} + public class AbstractContentFactory : ContentFactoryBase, ContentFactoryType { public typealias Content = T var promise:Promise diff --git a/Express/JsonView.swift b/Express/JsonView.swift index cbc277c..d30382d 100644 --- a/Express/JsonView.swift +++ b/Express/JsonView.swift @@ -80,13 +80,13 @@ public class JsonView : NamedViewType { public init() { } - public func render(context:Context?) throws -> AbstractActionType { + public func render(context:Context?) throws -> ResponseType { //TODO: implement reflection let json = context.flatMap{$0 as? JSONConvertible}.flatMap { $0.toJSON() } //TODO: avoid string path guard let render = json?.dump() else { throw ExpressError.Render(description: "unable to render json: " + context.flatMap{String($0)}.getOrElse("None"), line: nil, cause: nil) } - return Action.ok(AnyContent(str:render, contentType: "application/json")) + return Response(status: .Ok, content: AnyContent(str:render, contentType: "application/json")) } } \ No newline at end of file diff --git a/Express/MustacheViewEngine.swift b/Express/MustacheViewEngine.swift index 90c179a..d492462 100644 --- a/Express/MustacheViewEngine.swift +++ b/Express/MustacheViewEngine.swift @@ -50,7 +50,7 @@ private let warning = "Warning: " + message self.template = template } - func render(context:Context?) throws -> AbstractActionType { + func render(context:Context?) throws -> ResponseType { do { let anyContext = context.flatMap { (i)->AnyObject? in if let obj = i as? AnyObject { @@ -63,7 +63,7 @@ private let warning = "Warning: " + message } let box = Box(anyContext as? MustacheBoxable) let render = try template.render(box) - return Action.ok(AnyContent(str:render, contentType: "text/html")) + return Response(status: .Ok, content: AnyContent(str:render, contentType: "text/html")) } catch let e as MustacheError { switch e.kind { //TODO: double check no such error can be found at this place diff --git a/Express/Response.swift b/Express/Response.swift index 1e25f16..6f4addf 100644 --- a/Express/Response.swift +++ b/Express/Response.swift @@ -28,14 +28,25 @@ protocol HeadersAdjuster { static func adjustHeaders(headers:Dictionary, c:Content?) -> Dictionary } -public class Response : HttpResponseHead, HeadersAdjuster { +public protocol ResponseType : HttpResponseHeadType { + var content:FlushableContentType? {get} +} + +public class Response : HttpResponseHead, HeadersAdjuster, ResponseType { typealias Content = C - let content:C? + /// swift limitation + public let typesafeContent:C? + public let content:FlushableContentType? + + public convenience init(status:StatusCode, content:C? = nil, headers:Dictionary = Dictionary()) { + self.init(status: status.rawValue, content: content, headers: headers) + } - public init(status:UInt16, content:C? = nil, headers h:Dictionary = Dictionary()) { + public init(status:UInt16, content:C? = nil, headers:Dictionary = Dictionary()) { + self.typesafeContent = content self.content = content - super.init(status: status, headers:Response.adjustHeaders(h, c: content)) + super.init(status: status, headers:Response.adjustHeaders(headers, c: typesafeContent)) } public override func flushTo(out:DataConsumerType) -> Future { diff --git a/Express/StencilViewEngine.swift b/Express/StencilViewEngine.swift index 28635a4..e59e216 100644 --- a/Express/StencilViewEngine.swift +++ b/Express/StencilViewEngine.swift @@ -81,7 +81,7 @@ class StencilView : ViewType { self.loader = loader } - func render(context:C?) throws -> AbstractActionType { + func render(context:C?) throws -> ResponseType { do { let edibleOption = context.flatMap{$0 as? StencilCookable }?.cook() let contextSupplied:[String:Any] = edibleOption.getOrElse(Dictionary()) @@ -103,7 +103,7 @@ class StencilView : ViewType { let stencilContext = Context(dictionary: finalContext) let render = try template.render(stencilContext) - return Action.ok(AnyContent(str:render, contentType: "text/html")) + return Response(status: .Ok, content: AnyContent(str:render, contentType: "text/html")) } catch let e as TemplateSyntaxError { throw ExpressError.Render(description: e.description, line: nil, cause: e) } diff --git a/Express/View.swift b/Express/View.swift index 4028cd2..55ee5df 100644 --- a/Express/View.swift +++ b/Express/View.swift @@ -22,7 +22,7 @@ import Foundation public protocol ViewType { - func render(context:Context?) throws -> AbstractActionType + func render(context:Context?) throws -> ResponseType } public protocol NamedViewType : ViewType { diff --git a/Express/Views.swift b/Express/Views.swift index 4104b07..4286f27 100644 --- a/Express/Views.swift +++ b/Express/Views.swift @@ -121,7 +121,7 @@ public class Views { } } - func render(view:String, context:Context?) -> Future { + func render(view:String, context:Context?) -> Future { return self.view(view).map(renderContext) { view in try view.render(context) } From 7eff37cea6fb6a09d65512f93cba65a4db8f80fb Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 13:08:25 +0200 Subject: [PATCH 48/88] Added contentType to ContentType --- Express/AnyContent.swift | 2 +- Express/Content.swift | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Express/AnyContent.swift b/Express/AnyContent.swift index 34d49f3..794df2a 100644 --- a/Express/AnyContent.swift +++ b/Express/AnyContent.swift @@ -52,7 +52,7 @@ public class AnyContentFactory : AbstractContentFactory { public class AnyContent : ConstructableContentType, FlushableContentType { public typealias Factory = AnyContentFactory let data:Array - let contentType:String? + public let contentType:String? public init?(data:Array?, contentType:String?) { guard let data = data else { diff --git a/Express/Content.swift b/Express/Content.swift index c7640da..9cb0b30 100644 --- a/Express/Content.swift +++ b/Express/Content.swift @@ -32,6 +32,7 @@ public protocol ContentFactoryType : DataConsumerType { } public protocol ContentType { + var contentType:String? {get} } public protocol ConstructableContentType : ContentType { @@ -51,6 +52,11 @@ public protocol FlushableContentType : ContentType, FlushableType { public class FlushableContent : FlushableContentType { let content:FlushableContentType + public var contentType:String? { + get { + return content.contentType + } + } public required init(content:FlushableContentType) { self.content = content From 17762aa4718dd2676dd1331febe15e6d063532fd Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 13:33:38 +0200 Subject: [PATCH 49/88] Proper rendering APIs --- Demo/main.swift | 2 +- Express/Action.swift | 19 ++++++++++++++----- Express/JsonView.swift | 4 ++-- Express/MustacheViewEngine.swift | 4 ++-- Express/Response.swift | 9 +++++++++ Express/StencilViewEngine.swift | 4 ++-- Express/View.swift | 2 +- Express/Views.swift | 2 +- 8 files changed, 32 insertions(+), 14 deletions(-) diff --git a/Demo/main.swift b/Demo/main.swift index 001d507..2c28040 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -59,7 +59,7 @@ app.errorHandler.register { e in app.errorHandler.register { (e:ExpressError) in switch e { case .PageNotFound(let path): - return Action.render("404", context: ["path": path]) + return Action.render("404", context: ["path": path], status: .NotFound) default: return nil } diff --git a/Express/Action.swift b/Express/Action.swift index 7952f7e..14649ec 100644 --- a/Express/Action.swift +++ b/Express/Action.swift @@ -65,15 +65,24 @@ extension ResponseAction where C : FlushableContent { class RenderAction : Action, IntermediateActionType { let view:String let context:Context? + let status:StatusCode + let headers:Dictionary - init(view:String, context:Context?) { + init(view:String, context:Context?, status:StatusCode = .Ok, headers:Dictionary = Dictionary()) { self.view = view self.context = context + self.status = status + self.headers = headers + } + + private func response(status:StatusCode, content:RC?, headers:Dictionary) -> Response { + return Response(status: status, content: content, headers: headers) } func nextAction(app:Express, routeId:String, request:Request, out:DataConsumerType) -> Future { - return app.views.render(view, context: context).map { response in - ResponseAction(response: response) + return app.views.render(view, context: context).map { content in + let response = Response(status: self.status, content: content, headers: self.headers) + return ResponseAction(response: response) } } } @@ -144,8 +153,8 @@ public extension Action { return chain(nilRequest()) } - public class func render(view:String, context:Context? = nil) -> Action { - return RenderAction(view: view, context: context) + public class func render(view:String, context:Context? = nil, status:StatusCode = .Ok, headers:Dictionary = Dictionary()) -> Action { + return RenderAction(view: view, context: context, status: status, headers: headers) } public class func response(response:Response) -> Action { diff --git a/Express/JsonView.swift b/Express/JsonView.swift index d30382d..7187b39 100644 --- a/Express/JsonView.swift +++ b/Express/JsonView.swift @@ -80,13 +80,13 @@ public class JsonView : NamedViewType { public init() { } - public func render(context:Context?) throws -> ResponseType { + public func render(context:Context?) throws -> FlushableContentType { //TODO: implement reflection let json = context.flatMap{$0 as? JSONConvertible}.flatMap { $0.toJSON() } //TODO: avoid string path guard let render = json?.dump() else { throw ExpressError.Render(description: "unable to render json: " + context.flatMap{String($0)}.getOrElse("None"), line: nil, cause: nil) } - return Response(status: .Ok, content: AnyContent(str:render, contentType: "application/json")) + return AnyContent(str:render, contentType: "application/json")! } } \ No newline at end of file diff --git a/Express/MustacheViewEngine.swift b/Express/MustacheViewEngine.swift index d492462..2837198 100644 --- a/Express/MustacheViewEngine.swift +++ b/Express/MustacheViewEngine.swift @@ -50,7 +50,7 @@ private let warning = "Warning: " + message self.template = template } - func render(context:Context?) throws -> ResponseType { + func render(context:Context?) throws -> FlushableContentType { do { let anyContext = context.flatMap { (i)->AnyObject? in if let obj = i as? AnyObject { @@ -63,7 +63,7 @@ private let warning = "Warning: " + message } let box = Box(anyContext as? MustacheBoxable) let render = try template.render(box) - return Response(status: .Ok, content: AnyContent(str:render, contentType: "text/html")) + return AnyContent(str:render, contentType: "text/html")! } catch let e as MustacheError { switch e.kind { //TODO: double check no such error can be found at this place diff --git a/Express/Response.swift b/Express/Response.swift index 6f4addf..da661cd 100644 --- a/Express/Response.swift +++ b/Express/Response.swift @@ -77,4 +77,13 @@ public class Response : HttpResponseHead, HeadersAdjus } return h.getOrElse(headers) } +} + +extension Response where C : FlushableContent { + convenience init(status:StatusCode, content:FlushableContentType?, headers:Dictionary = Dictionary()) { + let content = content.map { content in + C(content: content) + } + self.init(status: status, content: content, headers: headers) + } } \ No newline at end of file diff --git a/Express/StencilViewEngine.swift b/Express/StencilViewEngine.swift index e59e216..a85715d 100644 --- a/Express/StencilViewEngine.swift +++ b/Express/StencilViewEngine.swift @@ -81,7 +81,7 @@ class StencilView : ViewType { self.loader = loader } - func render(context:C?) throws -> ResponseType { + func render(context:C?) throws -> FlushableContentType { do { let edibleOption = context.flatMap{$0 as? StencilCookable }?.cook() let contextSupplied:[String:Any] = edibleOption.getOrElse(Dictionary()) @@ -103,7 +103,7 @@ class StencilView : ViewType { let stencilContext = Context(dictionary: finalContext) let render = try template.render(stencilContext) - return Response(status: .Ok, content: AnyContent(str:render, contentType: "text/html")) + return AnyContent(str:render, contentType: "text/html")! } catch let e as TemplateSyntaxError { throw ExpressError.Render(description: e.description, line: nil, cause: e) } diff --git a/Express/View.swift b/Express/View.swift index 55ee5df..e47f669 100644 --- a/Express/View.swift +++ b/Express/View.swift @@ -22,7 +22,7 @@ import Foundation public protocol ViewType { - func render(context:Context?) throws -> ResponseType + func render(context:Context?) throws -> FlushableContentType } public protocol NamedViewType : ViewType { diff --git a/Express/Views.swift b/Express/Views.swift index 4286f27..8c330d0 100644 --- a/Express/Views.swift +++ b/Express/Views.swift @@ -121,7 +121,7 @@ public class Views { } } - func render(view:String, context:Context?) -> Future { + func render(view:String, context:Context?) -> Future { return self.view(view).map(renderContext) { view in try view.render(context) } From 8ac7364987ab69fc3883b70d2b212defb6ef98ff Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 13:47:08 +0200 Subject: [PATCH 50/88] echo request --- Demo/main.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Demo/main.swift b/Demo/main.swift index 2c28040..e8efe8c 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -73,6 +73,10 @@ app.get("/hello") { request in return Action.ok(AnyContent(str: "

    Hello Express!!!

    ", contentType: "text/html")) } +app.get("/echo") { request in + return Action.ok(request.query["call"]?.first) +} + //user as an url param app.get("/hello/:user.html") { request in //get user From 354f8ec01b17e2659a4698f7c518df8fe9739493 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 13:47:35 +0200 Subject: [PATCH 51/88] echo to the top of the chain --- Demo/main.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Demo/main.swift b/Demo/main.swift index e8efe8c..4a06100 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -19,6 +19,10 @@ app.views.register(JsonView()) app.views.register(MustacheViewEngine()) app.views.register(StencilViewEngine()) +app.get("/echo") { request in + return Action.ok(request.query["call"]?.first) +} + enum TestError { case Test case Test2 @@ -73,10 +77,6 @@ app.get("/hello") { request in return Action.ok(AnyContent(str: "

    Hello Express!!!

    ", contentType: "text/html")) } -app.get("/echo") { request in - return Action.ok(request.query["call"]?.first) -} - //user as an url param app.get("/hello/:user.html") { request in //get user From 230258203336167b577b49a82ef80ceab7ba9205 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 13:50:30 +0200 Subject: [PATCH 52/88] Better message for linux users --- Express/ExpressServer.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Express/ExpressServer.swift b/Express/ExpressServer.swift index 93ff13a..26c5b16 100644 --- a/Express/ExpressServer.swift +++ b/Express/ExpressServer.swift @@ -32,7 +32,7 @@ public extension Express { @noreturn func run() { #if os(Linux) && !dispatch - print("Note: Express is currently running without dispatch support. We have implemented this mode to support Linux developers while Dispatch for Linux is not available out of the box. Consider it to be development mode only and not suitable for production as it might cause occasional hanging and crashes. Still, there is a possibility to build Express with dispatch support (recommended for production use). Follow this link for more info: https://github.com/crossroadlabs/Express") + print("Note: You have built Express without dispatch support. We have implemented this mode to support Linux developers while Dispatch for Linux is not available out of the box. Consider it to be development mode only and not suitable for production as it might cause occasional hanging and crashes. Still, there is a possibility to build Express with dispatch support (recommended for production use). Follow this link for more info: https://github.com/crossroadlabs/Express") #endif ExecutionContext.run() } From 305b08413daa6fcf41d3d626d0e62179809784ef Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 13:51:15 +0200 Subject: [PATCH 53/88] getting rid of mustache examples --- Demo/main.swift | 10 ---------- Demo/views/hello.mustache | 5 ----- Demo/views/{hello2.stencil => hello.stencil} | 0 Express.xcodeproj/project.pbxproj | 12 ++++-------- 4 files changed, 4 insertions(+), 23 deletions(-) delete mode 100644 Demo/views/hello.mustache rename Demo/views/{hello2.stencil => hello.stencil} (100%) diff --git a/Demo/main.swift b/Demo/main.swift index 4a06100..6e07064 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -87,16 +87,6 @@ app.get("/hello/:user.html") { request in return Action.render("hello", context: context) } -//user as an url param -app.get("/hello2/:user.html") { request in - //get user - let user = request.params["user"] - //if there is a user - create our context. If there is no user, context will remain nil - let context = user.map {["user": $0]} - //render our template named "hello" - return Action.render("hello2", context: context) -} - app.post("/api/user") { request in //check if JSON has arrived guard let json = request.body?.asJSON() else { diff --git a/Demo/views/hello.mustache b/Demo/views/hello.mustache deleted file mode 100644 index 6dd53d0..0000000 --- a/Demo/views/hello.mustache +++ /dev/null @@ -1,5 +0,0 @@ - - -

    Hello: {{user}}

    - - \ No newline at end of file diff --git a/Demo/views/hello2.stencil b/Demo/views/hello.stencil similarity index 100% rename from Demo/views/hello2.stencil rename to Demo/views/hello.stencil diff --git a/Express.xcodeproj/project.pbxproj b/Express.xcodeproj/project.pbxproj index 1775534..fa7cdca 100644 --- a/Express.xcodeproj/project.pbxproj +++ b/Express.xcodeproj/project.pbxproj @@ -29,7 +29,6 @@ 6525AC541C24E3C600131F58 /* Mustache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; }; 6525AC551C24E3E000131F58 /* Mustache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; }; 6525AC561C24E3E700131F58 /* Mustache.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 652E5E951C6BD79700C11021 /* hello.mustache in Resources */ = {isa = PBXBuildFile; fileRef = 652E5E941C6BD79700C11021 /* hello.mustache */; }; 655077461C2D936100E42309 /* Static.swift in Sources */ = {isa = PBXBuildFile; fileRef = 655077451C2D936100E42309 /* Static.swift */; }; 6550774A1C2DC31500E42309 /* logo.png in Resources */ = {isa = PBXBuildFile; fileRef = 655077491C2DC31500E42309 /* logo.png */; }; 6550774C1C2DC8E500E42309 /* MIME.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6550774B1C2DC8E500E42309 /* MIME.swift */; }; @@ -75,7 +74,7 @@ 65E646E71C79A5010036D028 /* PathKit.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646E11C79A4EB0036D028 /* PathKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 65E646E81C79A5010036D028 /* Stencil.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646E21C79A4EB0036D028 /* Stencil.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 65E646EA1C79A5340036D028 /* StencilViewEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E646E91C79A5340036D028 /* StencilViewEngine.swift */; }; - 65E646EC1C79AD280036D028 /* hello2.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646EB1C79AD280036D028 /* hello2.stencil */; }; + 65E646EC1C79AD280036D028 /* hello.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646EB1C79AD280036D028 /* hello.stencil */; }; 65E646EE1C79AF4E0036D028 /* test2.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646ED1C79AF4E0036D028 /* test2.stencil */; }; 65E646F01C79AF6C0036D028 /* colored2.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646EF1C79AF6C0036D028 /* colored2.stencil */; }; 961B9E8B1C23229A000506D2 /* libevent.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 961B9E8A1C23229A000506D2 /* libevent.dylib */; }; @@ -133,7 +132,6 @@ 6518267E1C22081600E49AC5 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 651A23441C80552100A8480E /* 404.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = 404.stencil; sourceTree = ""; }; 6525AC531C24E3C600131F58 /* Mustache.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Mustache.framework; path = Carthage/Build/Mac/Mustache.framework; sourceTree = ""; }; - 652E5E941C6BD79700C11021 /* hello.mustache */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = hello.mustache; sourceTree = ""; }; 655077451C2D936100E42309 /* Static.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Static.swift; sourceTree = ""; }; 655077491C2DC31500E42309 /* logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo.png; sourceTree = ""; }; 6550774B1C2DC8E500E42309 /* MIME.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MIME.swift; sourceTree = ""; }; @@ -165,7 +163,7 @@ 65E646E11C79A4EB0036D028 /* PathKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PathKit.framework; path = Carthage/Build/Mac/PathKit.framework; sourceTree = ""; }; 65E646E21C79A4EB0036D028 /* Stencil.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Stencil.framework; path = Carthage/Build/Mac/Stencil.framework; sourceTree = ""; }; 65E646E91C79A5340036D028 /* StencilViewEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StencilViewEngine.swift; sourceTree = ""; }; - 65E646EB1C79AD280036D028 /* hello2.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = hello2.stencil; sourceTree = ""; }; + 65E646EB1C79AD280036D028 /* hello.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = hello.stencil; sourceTree = ""; }; 65E646ED1C79AF4E0036D028 /* test2.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = test2.stencil; sourceTree = ""; }; 65E646EF1C79AF6C0036D028 /* colored2.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = colored2.stencil; sourceTree = ""; }; 961B9E8A1C23229A000506D2 /* libevent.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libevent.dylib; path = ../../../../../usr/local/lib/libevent.dylib; sourceTree = ""; }; @@ -306,8 +304,7 @@ children = ( 657C1A741C24E0F40037041F /* test.mustache */, 659157FE1C285AFA00672785 /* colored.mustache */, - 652E5E941C6BD79700C11021 /* hello.mustache */, - 65E646EB1C79AD280036D028 /* hello2.stencil */, + 65E646EB1C79AD280036D028 /* hello.stencil */, 65E646ED1C79AF4E0036D028 /* test2.stencil */, 65E646EF1C79AF6C0036D028 /* colored2.stencil */, 651A23441C80552100A8480E /* 404.stencil */, @@ -436,13 +433,12 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 65E646EC1C79AD280036D028 /* hello2.stencil in Resources */, + 65E646EC1C79AD280036D028 /* hello.stencil in Resources */, 65E646F01C79AF6C0036D028 /* colored2.stencil in Resources */, 65E646EE1C79AF4E0036D028 /* test2.stencil in Resources */, 651A23451C80552100A8480E /* 404.stencil in Resources */, 659157FF1C285AFA00672785 /* colored.mustache in Resources */, 657C1A751C24E0F40037041F /* test.mustache in Resources */, - 652E5E951C6BD79700C11021 /* hello.mustache in Resources */, 6550774A1C2DC31500E42309 /* logo.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; From 55d090a9d4e10c9145b5c363839cf10297e042ea Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 13:59:08 +0200 Subject: [PATCH 54/88] some cleanup --- Demo/main.swift | 18 ++++-------------- README.md | 6 +++--- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/Demo/main.swift b/Demo/main.swift index 6e07064..1192baf 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -105,21 +105,17 @@ app.post("/api/user") { request in return Action.render(JsonView.name, context: response) } -app.get("/myecho") { request in - return Action.ok(request.query["message"]?.first) -} - //:param - this is how you define a part of URL you want to receive through request object -app.get("/myecho/:param") { request in +app.get("/echo/:param") { request in //here you get the param from request: request.params["param"] return Action.ok(request.params["param"]) } -func factorial(n: Int) -> Int { +func factorial(n: Double) -> Double { return n == 0 ? 1 : n * factorial(n - 1) } -func calcFactorial(num:Int) -> Future { +func calcFactorial(num:Double) -> Future { return future { return factorial(num) } @@ -129,7 +125,7 @@ func calcFactorial(num:Int) -> Future { // hopefully inference in swift will get better eventually and just "request in" will be enough app.get("/factorial/:num(\\d+)") { request -> Future, AnyError> in // get the number from the url - let num = request.params["num"].flatMap{Int($0)}.getOrElse(0) + let num = request.params["num"].flatMap{Double($0)}.getOrElse(0) // get the factorial Future. Returns immediately - non-blocking let factorial = calcFactorial(num) @@ -143,12 +139,6 @@ app.get("/factorial/:num(\\d+)") { request -> Future, AnyErro return future } -app.get("/test") { req in - return future { - return try test() - } -} - func testItems(request:Request) throws -> [String: Any] { let newItems = request.query.map { (k, v) in (k, v.first!) diff --git a/README.md b/README.md index 5158427..fbdeffe 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ Still if you want to benefit from asynchronicity, we provide a very powerful API Let's assume you have following function somewhere: ```swift -func calcFactorial(num:Int) -> Future +func calcFactorial(num:Double) -> Future ``` it's a purely asyncronous function that returns future. It would be really nice if it could be handled asynchronously as well in a nice functional way. Here is an example of how it could be done. @@ -105,7 +105,7 @@ it's a purely asyncronous function that returns future. It would be really nice // hopefully inference in swift will get better eventually and just "request in" will be enough app.get("/factorial/:num(\\d+)") { request -> Future, AnyError> in // get the number from the url - let num = request.params["num"].flatMap{Int($0)}.getOrElse(0) + let num = request.params["num"].flatMap{Double($0)}.getOrElse(0) // get the factorial Future. Returns immediately - non-blocking let factorial = calcFactorial(num) @@ -126,7 +126,7 @@ Let's get our echo example from [Getting Started](#getting-started) a bit furthe ```swift //:param - this is how you define a part of URL you want to receive through request object -app.get("/myecho/:param") { request in +app.get("/echo/:param") { request in //here you get the param from request: request.params["param"] return Action.ok(request.params["param"]) } From 92dc684956ce7b4938f188282eb82757d7ddbb4c Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 14:02:56 +0200 Subject: [PATCH 55/88] some demo cleanup --- Demo/main.swift | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) diff --git a/Demo/main.swift b/Demo/main.swift index 1192baf..193c052 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -165,18 +165,6 @@ app.get("/test2.html") { request in return Action.render("test2", context: try testItems(request)) } -app.get("/echo") { request in - return Action.chain() -} - -app.get("/myecho") { request in - return Action.ok(AnyContent(str: request.query["message"]?.first)) -} - -app.get("/hello") { request in - return Action.ok(AnyContent(str: "

    Hello Express!!!

    ", contentType: "text/html")) -} - app.get("/") { request in for me in request.body?.asJSON().map({$0["test"]}) { print(me) @@ -204,40 +192,6 @@ func echoRender(request:Request) -> Action { return Action.render(JsonView.name, context: data) } -app.post("/echo/inline") { request in - let call = request.body?.asJSON().map({$0["say"]})?.string - let response = call.getOrElse("I don't hear you!") - - return Action.ok(AnyContent(str:"{\"said\": \"" + response + "\"}", contentType: "application/json")) -} - -app.get("/echo") { request in - return echo(request) -} - -app.get("/echo/render", handler: echoRender) -app.post("/echo/render", handler: echoRender) - -app.post("/echo") { request in - return echo(request) -} - -app.post("/echo2") { request in - return Action.ok(AnyContent(str: request.body?.asText().map {"Text echo: " + $0}, - contentType: request.contentType)) -} - -app.post("/echo3") { request in - return Action.ok(AnyContent(data: request.body?.asRaw(), - contentType: request.contentType)) -} - -app.all("/async/echo") { request in - return future { - return echo(request) - } -} - app.get("/test/redirect") { request in return future { let to = request.query["to"].flatMap{$0.first}.getOrElse("../test.html") From 387fe7eac0a60fed5e856ab0aca5f0b65d257d20 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 14:13:28 +0200 Subject: [PATCH 56/88] Better app.listen API --- Demo/main.swift | 4 ++-- Express/ExpressServer.swift | 6 +++--- Express/HttpServer.swift | 11 ++++++++--- Express/Server.swift | 8 +++++--- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Demo/main.swift b/Demo/main.swift index 193c052..e4e5eeb 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -203,8 +203,8 @@ app.post("/merged/query") { request in Action.render(JsonView.name, context: request.mergedQuery()) } -app.listen(9999).onSuccess { - print("Successfully launched server") +app.listen(9999).onSuccess { server in + print("Express was successfully launched on port", server.port) } app.run() diff --git a/Express/ExpressServer.swift b/Express/ExpressServer.swift index 26c5b16..6215d5d 100644 --- a/Express/ExpressServer.swift +++ b/Express/ExpressServer.swift @@ -25,9 +25,9 @@ import ExecutionContext import BrightFutures public extension Express { - func listen(port:UInt16) -> Future { - let server = HttpServer(app: self) - return server.start(port) + func listen(port:UInt16) -> Future { + let server = HttpServer(app: self, port: port) + return server.start() } @noreturn func run() { diff --git a/Express/HttpServer.swift b/Express/HttpServer.swift index 578b9de..a9d3951 100644 --- a/Express/HttpServer.swift +++ b/Express/HttpServer.swift @@ -132,20 +132,25 @@ private func server_thread(pm: UnsafeMutablePointer) -> UnsafeMutablePoint } class HttpServer : ServerType { + let port:UInt16 let app:Express let thread: UnsafeMutablePointer - func start(port:UInt16) -> Future { + func start() -> Future { let params = ServerParams(promise: Promise(), port: port, app: app) pthread_create(thread, nil, server_thread, UnsafeMutablePointer(Unmanaged.passRetained(params).toOpaque())) - return params.promise.future + return params.promise.future.map { + self + } } - required init(app:Express) { + required init(app:Express, port:UInt16) { + self.port = port self.app = app self.thread = UnsafeMutablePointer.alloc(1) } + deinit { self.thread.destroy() self.thread.dealloc(1) diff --git a/Express/Server.swift b/Express/Server.swift index bc9cb4d..8405ef9 100644 --- a/Express/Server.swift +++ b/Express/Server.swift @@ -23,13 +23,15 @@ import Foundation import BrightFutures import Result -protocol ServerType { +public protocol ServerType { + var port:UInt16 {get} + var app:Express { get } - func start(port:UInt16) -> Future + func start() -> Future - init(app:Express) + init(app:Express, port:UInt16) } From eaba2f0b7980fc5cd9e4d521c2dd600c02a071bf Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 16:07:20 +0200 Subject: [PATCH 57/88] Some docs --- doc/gettingstarted/installing.md | 89 ++++++++++++++++++++++++++++++++ doc/index.md | 10 ++++ 2 files changed, 99 insertions(+) create mode 100644 doc/gettingstarted/installing.md create mode 100644 doc/index.md diff --git a/doc/gettingstarted/installing.md b/doc/gettingstarted/installing.md new file mode 100644 index 0000000..ded1108 --- /dev/null +++ b/doc/gettingstarted/installing.md @@ -0,0 +1,89 @@ +#Installing + +## [OS X ![OS X](https://cdn1.iconfinder.com/data/icons/system-shade-circles/512/mac_os_X-16.png)](http://www.apple.com/osx/) + +##### First install the following components (if you have not yet): + +* [XCode](https://developer.apple.com/xcode/download/) 7.2 or higher +* [Homebrew](http://brew.sh/) the latest available version +* Command Line tools: run ```xcode-select --install``` in terminal + +##### Run the following in terminal: + +```sh +brew tap crossroadlabs/tap +brew install swift-express +``` + +## [Linux ![Linux](https://cdn1.iconfinder.com/data/icons/system-shade-circles/512/linux_tox-16.png)](http://www.linux.org/) + +##### First install the following components (if you have not yet): + +* [Linux](http://www.linux.org/), one of the following distributions will work: + * [Ubuntu 15.10 (Wily Werewolf)](http://releases.ubuntu.com/15.10/) + * [Ubuntu 14.04 (Trusty Tahr)](http://releases.ubuntu.com/14.04/) +* [Swift](https://swift.org/), the latest development snapshot from [here](https://swift.org/download/#latest-development-snapshots) + * _You should have swift at least of 25.02.2016_ + * Installation instructions are [here](https://swift.org/getting-started/#on-linux) +* Dependency libraries: + * If you are using Ubuntu 15.10, you are lucky. Just install the dependencies from `apt`: + +```sh +sudo apt-get install libevhtp-dev libevent-dev libssl-dev +``` + + * + +* [Dispatch](https://swift.org/core-libraries/#libdispatch) _(optional)_ + * For more information see dedicated [Dispatch installation section](#), please. + + +### Installing [Dispatch](https://swift.org/core-libraries/#libdispatch) on Linux + +Dispatch is not available as a prebuilt package yet, so we have to build it from sources: + +* Install prerequisites: + +```sh +sudo apt-get install autoconf libtool pkg-config systemtap-sdt-dev libblocksruntime-dev libkqueue-dev libbsd-dev git make +``` + +* Clone dispatch repository and get into it: + +```sh +git clone https://github.com/apple/swift-corelibs-libdispatch.git +cd swift-corelibs-libdispatch +``` + +* Initialize submodules: + +```sh +git submodule init +git submodule update +``` + +* Generate build toolset: + +```sh +sh ./autogen.sh +``` + +* Configure build: + +```sh +./configure --with-swift-toolchain=/usr --prefix=/usr +``` + +`path-to-swift` whould point exactly to your swift distribution. Pay attension that you have to put `/usr` after it. + +* Build: + +```sh +make +``` + +* Install (*Don't worry, it will not install system wide*) + +```sh +make install +``` \ No newline at end of file diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 0000000..06ca256 --- /dev/null +++ b/doc/index.md @@ -0,0 +1,10 @@ +# Express documentation + +## Getting started + +### Installing +### Hello world +### Express command line +### Basic routing +### Static files +### Basic error handling \ No newline at end of file From fef08381dbd9f7104e68816d40804b3c1f9814db Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 16:21:24 +0200 Subject: [PATCH 58/88] installation doc --- doc/gettingstarted/installing.md | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/doc/gettingstarted/installing.md b/doc/gettingstarted/installing.md index ded1108..9ef00be 100644 --- a/doc/gettingstarted/installing.md +++ b/doc/gettingstarted/installing.md @@ -26,18 +26,37 @@ brew install swift-express * _You should have swift at least of 25.02.2016_ * Installation instructions are [here](https://swift.org/getting-started/#on-linux) * Dependency libraries: - * If you are using Ubuntu 15.10, you are lucky. Just install the dependencies from `apt`: + +If you are using Ubuntu 15.10, you are lucky. Just install the dependencies from `apt`: ```sh sudo apt-get install libevhtp-dev libevent-dev libssl-dev ``` - * +Ubuntu 14.4 does not have `libevhtp-dev` package. So you have to install everything else: + +```sh +sudo apt-get install libevent-dev libssl-dev +``` + +and install `libevhtp-dev` from sources from this repo [ellzey/libevhtp](https://github.com/ellzey/libevhtp) or use our package `apt` package: + +```sh +TBD +``` * [Dispatch](https://swift.org/core-libraries/#libdispatch) _(optional)_ * For more information see dedicated [Dispatch installation section](#), please. +##### We have not ported our command line tools to linux yet, so either [generate project on OS X](#) or use [this](#) temporary script: + +```sh +TBD +# download the script +``` + + ### Installing [Dispatch](https://swift.org/core-libraries/#libdispatch) on Linux Dispatch is not available as a prebuilt package yet, so we have to build it from sources: @@ -82,7 +101,7 @@ sh ./autogen.sh make ``` -* Install (*Don't worry, it will not install system wide*) +* Install (*Don't worry, it will NOT install system wide*) ```sh make install From e1834ffaada868ae3df1359d617ae711e59287f4 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 16:31:02 +0200 Subject: [PATCH 59/88] better installing section --- doc/gettingstarted/installing.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/gettingstarted/installing.md b/doc/gettingstarted/installing.md index 9ef00be..e47f228 100644 --- a/doc/gettingstarted/installing.md +++ b/doc/gettingstarted/installing.md @@ -39,23 +39,22 @@ Ubuntu 14.4 does not have `libevhtp-dev` package. So you have to install everyth sudo apt-get install libevent-dev libssl-dev ``` -and install `libevhtp-dev` from sources from this repo [ellzey/libevhtp](https://github.com/ellzey/libevhtp) or use our package `apt` package: +and then install `libevhtp-dev` from sources from this repo [ellzey/libevhtp](https://github.com/ellzey/libevhtp) or use our pre-built `apt` package: ```sh TBD ``` * [Dispatch](https://swift.org/core-libraries/#libdispatch) _(optional)_ - * For more information see dedicated [Dispatch installation section](#), please. + * For more information refer to the dedicated [Dispatch installation section](#installing-dispatch-on-linux), please. -##### We have not ported our command line tools to linux yet, so either [generate project on OS X](#) or use [this](#) temporary script: +##### We have not ported our command line tools to Linux yet, so either [generate project on OS X](#) and then use it or use [this](#) temporary script: ```sh TBD # download the script ``` - ### Installing [Dispatch](https://swift.org/core-libraries/#libdispatch) on Linux From c02ecf0812760513c0fa4c82dc26c04d2455297e Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 16:54:30 +0200 Subject: [PATCH 60/88] link to documentation --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fbdeffe..f656000 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Build Status](https://travis-ci.org/crossroadlabs/Express.svg?branch=master)](https://travis-ci.org/crossroadlabs/Express) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) ![Platform OS X | Linux](https://img.shields.io/badge/platform-OS%20X%20%7C%20Linux-orange.svg) -![Swift version](https://img.shields.io/badge/Swift-2.1-blue.svg) +![Swift version](https://img.shields.io/badge/Swift-2.1 | 2.2-blue.svg) [![GitHub release](https://img.shields.io/github/release/crossroadlabs/Express.svg)](https://github.com/crossroadlabs/Express/releases) ### Being [perfectionists](http://www.crossroadlabs.xyz), we took the best from what we think is the best: power of [Play Framework](https://www.playframework.com/) and simplicity of [Express.js](http://expressjs.com/) @@ -214,6 +214,7 @@ Now create a file called `hello.mustache` in the `views` directory: ``` Add a new request handler: + ```swift //user as an url param app.get("/hello/:user.html") { request in @@ -228,6 +229,9 @@ app.get("/hello/:user.html") { request in Now follow the link to see the result: [http://localhost:9999/hello/express.html](http://localhost:9999/hello/express.html) + +### If you want more, please, visit our [documentation](./doc/index.md) page + ## Ideology behind ### Taking the best of Swift From a260bd29ad0d869bcf4044f0f337a1ca44fe6be0 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 16:58:16 +0200 Subject: [PATCH 61/88] Installing link --- doc/index.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/index.md b/doc/index.md index 06ca256..caa4701 100644 --- a/doc/index.md +++ b/doc/index.md @@ -1,10 +1,9 @@ # Express documentation -## Getting started - -### Installing -### Hello world -### Express command line -### Basic routing -### Static files -### Basic error handling \ No newline at end of file +* Getting started + * [Installing](./gettingstarted/installing.md) + * Hello world + * Express command line + * Basic routing + * Static files + * Basic error handling \ No newline at end of file From 1dc14cc9d3bf5404eddd8398fb21959c4e5a37fe Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 17:08:56 +0200 Subject: [PATCH 62/88] doc link --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f656000..3335e06 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -

    +[//]: https://www.iconfinder.com/icons/383207/doc_tag_icon#size=64 +

    Documentation

    # Swift Express From 111f0c174f00923d7cb509875c3451a9e5b2c6e6 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 17:14:55 +0200 Subject: [PATCH 63/88] float right --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3335e06..2c48986 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [//]: https://www.iconfinder.com/icons/383207/doc_tag_icon#size=64 -

    Documentation

    +

    Documentation

    # Swift Express From 871e963da2caae93973ae6c88910cce714689239 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 17:18:49 +0200 Subject: [PATCH 64/88] style test --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2c48986..b581a08 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [//]: https://www.iconfinder.com/icons/383207/doc_tag_icon#size=64 -

    Documentation

    +

    Documentation

    -# Swift Express +# Swift Express {:style="float:right"} [![GitHub license](https://img.shields.io/badge/license-LGPL v3-green.svg)](https://raw.githubusercontent.com/crossroadlabs/Express/master/LICENSE) [![Build Status](https://travis-ci.org/crossroadlabs/Express.svg?branch=master)](https://travis-ci.org/crossroadlabs/Express) From 0e6ab90139e792b4401f8fecdff7db7692deb0d3 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 17:21:28 +0200 Subject: [PATCH 65/88] back to align right --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b581a08..3335e06 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [//]: https://www.iconfinder.com/icons/383207/doc_tag_icon#size=64 -

    Documentation

    +

    Documentation

    -# Swift Express {:style="float:right"} +# Swift Express [![GitHub license](https://img.shields.io/badge/license-LGPL v3-green.svg)](https://raw.githubusercontent.com/crossroadlabs/Express/master/LICENSE) [![Build Status](https://travis-ci.org/crossroadlabs/Express.svg?branch=master)](https://travis-ci.org/crossroadlabs/Express) From 205cfdfcc1b3683bc31506e3b359cda1ce684e5c Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 17:29:05 +0200 Subject: [PATCH 66/88] link to demo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3335e06..160a009 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [//]: https://www.iconfinder.com/icons/383207/doc_tag_icon#size=64 -

    Documentation

    +

    Documentation
    Live 🐧 server running Demo

    # Swift Express From 92692727d4d5ec401cb18df535edba3c4ff2fbb6 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 17:33:16 +0200 Subject: [PATCH 67/88] added live demo link --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 160a009..b73912d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,15 @@ [//]: https://www.iconfinder.com/icons/383207/doc_tag_icon#size=64 -

    Documentation
    Live 🐧 server running Demo

    +

    + + + + +

    Documentation +
    + +

    +[
    Live 🐧 server running Demo +
    ](http://demo.swiftexpress.io/) # Swift Express From e39d57ddc52775959b98b42095dd84ac514aae35 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 18:17:25 +0200 Subject: [PATCH 68/88] proper installation of dependencies --- doc/gettingstarted/installing.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/doc/gettingstarted/installing.md b/doc/gettingstarted/installing.md index e47f228..acefcc4 100644 --- a/doc/gettingstarted/installing.md +++ b/doc/gettingstarted/installing.md @@ -27,22 +27,17 @@ brew install swift-express * Installation instructions are [here](https://swift.org/getting-started/#on-linux) * Dependency libraries: -If you are using Ubuntu 15.10, you are lucky. Just install the dependencies from `apt`: +If you are using Ubuntu 15.10, you are lucky. Skip the following step. If you are on Ubuntu 14.04 you need to add our repo to your `apt`: ```sh -sudo apt-get install libevhtp-dev libevent-dev libssl-dev +sudo add-apt-repository ppa:swiftexpress/swiftexpress +sudo apt-get update ``` -Ubuntu 14.4 does not have `libevhtp-dev` package. So you have to install everything else: +Following is common for both Ubuntu 14.04 and Ubuntu 15.10: ```sh -sudo apt-get install libevent-dev libssl-dev -``` - -and then install `libevhtp-dev` from sources from this repo [ellzey/libevhtp](https://github.com/ellzey/libevhtp) or use our pre-built `apt` package: - -```sh -TBD +sudo apt-get install libevhtp-dev libevent-dev libssl-dev git ``` * [Dispatch](https://swift.org/core-libraries/#libdispatch) _(optional)_ From 56310bec812876bdce67c646c3eeff15e90af02c Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 18:21:05 +0200 Subject: [PATCH 69/88] fixed SPM build --- Package.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Package.swift b/Package.swift index 07b0194..92fdfdb 100644 --- a/Package.swift +++ b/Package.swift @@ -23,6 +23,7 @@ import PackageDescription let package = Package( name: "Express", + exclude: ["doc"], targets: [ Target( name: "Express" From df267db00931b9304a3277c8b2c336e177cfe870 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 18:21:57 +0200 Subject: [PATCH 70/88] Added hello express --- doc/gettingstarted/helloexpress.md | 2 ++ doc/index.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 doc/gettingstarted/helloexpress.md diff --git a/doc/gettingstarted/helloexpress.md b/doc/gettingstarted/helloexpress.md new file mode 100644 index 0000000..0855006 --- /dev/null +++ b/doc/gettingstarted/helloexpress.md @@ -0,0 +1,2 @@ +# Hello Express + diff --git a/doc/index.md b/doc/index.md index caa4701..7d22c8f 100644 --- a/doc/index.md +++ b/doc/index.md @@ -2,7 +2,7 @@ * Getting started * [Installing](./gettingstarted/installing.md) - * Hello world + * [Hello Express](./gettingstarted/helloexpress.md) * Express command line * Basic routing * Static files From 629d8e0d8748e4f4454596d6a7180348fcb100f1 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 18:44:11 +0200 Subject: [PATCH 71/88] missing swift dependencies --- doc/gettingstarted/installing.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/gettingstarted/installing.md b/doc/gettingstarted/installing.md index acefcc4..51c3e6d 100644 --- a/doc/gettingstarted/installing.md +++ b/doc/gettingstarted/installing.md @@ -22,6 +22,12 @@ brew install swift-express * [Linux](http://www.linux.org/), one of the following distributions will work: * [Ubuntu 15.10 (Wily Werewolf)](http://releases.ubuntu.com/15.10/) * [Ubuntu 14.04 (Trusty Tahr)](http://releases.ubuntu.com/14.04/) + * We have exprerienced some dependencies missing if installing by original instructions from [swift.org](http://swift.org/). Install these dependencies first, please: + +```sh +apt-get install clang binutils libicu-dev +``` + * [Swift](https://swift.org/), the latest development snapshot from [here](https://swift.org/download/#latest-development-snapshots) * _You should have swift at least of 25.02.2016_ * Installation instructions are [here](https://swift.org/getting-started/#on-linux) From d80f4ddd82baa3f8aa596dc16eb9f4446273bf93 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 19:09:32 +0200 Subject: [PATCH 72/88] Hello express tutorial --- doc/gettingstarted/helloexpress.md | 92 ++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/doc/gettingstarted/helloexpress.md b/doc/gettingstarted/helloexpress.md index 0855006..526ae80 100644 --- a/doc/gettingstarted/helloexpress.md +++ b/doc/gettingstarted/helloexpress.md @@ -1,2 +1,94 @@ # Hello Express +This is a very basic application. Much simpler than one created with [Express Command Line](#). Still it's good to understand the basic concepts by creating an app by hands. Keep in mind that you have to follow [Installation instructions](./installing.md) first. If you are doing it on a Mac, please, install [Swift 2.2 latest development](https://swift.org/download/#latest-development-snapshots) snapshot in addition. + +##### If you don't want to create an app manually, just skip this tutorial and use [Express Command Line](#) tool instead. + +## Create a folder + +Simple as that. We need a separate folder for the [Hello Express](#) app. Just run: + +```sh +mkdir HelloExpress +cd HelloExpress +``` + +## Package.swift + +[Package.swift](https://github.com/apple/swift-package-manager/blob/master/Documentation/Package.swift.md) is your project descriptor. Contains the name of the app and dependencies. Check [reference](https://github.com/apple/swift-package-manager/blob/master/Documentation/Package.swift.md) for more info. + +Create one right in the current dirctory and put the following text inside (we have just one dependency to [Express](http://www.swiftexpress.io/)): + +```swift +import PackageDescription + +let package = Package( + name: "HelloExpress", + dependencies: [ + .Package(url: "https://github.com/crossroadlabs/Express.git", majorVersion: 0, minor: 3), + ] +) +``` + +## The App + +Create a folder `app` in current directory and put there a file named `main.swift`. The contents of the file should look like: + +```swift +import Express + +let app = express() + +app.get("/") { request in + return Action.ok("Hello Express!") +} + +app.listen(9999).onSuccess { server in + print("Express was successfully launched on port", server.port) +} + +app.run() +``` + +This means that all the requests coming to the root will be responded with _Hello Express!_ string. All other requests will get `404`. + +## Build tool + +Swift on Linux and [Package Manager](https://github.com/apple/swift-package-manager) are early tools and are a bit complicated to use. We created a simple tool to build Swift on Linux. Get it using following commands: + +```sh +wget https://raw.githubusercontent.com/crossroadlabs/utils/master/build +chmod a+x build +``` + +## Build + +Just type in terminal: + +```sh +./build +``` + +## Run + +Run our new app with: + +```sh +./.build/debug/app +``` + +In the console you should see something like this: + +``` +Express was successfully launched on port 9999 +``` + +## Test + +Enter the following url in the browser: [http://localhost:9999/](http://localhost:9999/). You should now see text: + +``` +http://localhost:9999/ +``` + +# Next tutorial: [Express command line](#) \ No newline at end of file From 78c29a6abfe8df99ae918cf138778f7952342568 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 19:14:19 +0200 Subject: [PATCH 73/88] interlinking --- doc/gettingstarted/commandline.md | 2 ++ doc/gettingstarted/helloexpress.md | 6 +++--- doc/gettingstarted/installing.md | 4 +++- doc/index.md | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 doc/gettingstarted/commandline.md diff --git a/doc/gettingstarted/commandline.md b/doc/gettingstarted/commandline.md new file mode 100644 index 0000000..c0e7d06 --- /dev/null +++ b/doc/gettingstarted/commandline.md @@ -0,0 +1,2 @@ +# Express Command Line + diff --git a/doc/gettingstarted/helloexpress.md b/doc/gettingstarted/helloexpress.md index 526ae80..ff14749 100644 --- a/doc/gettingstarted/helloexpress.md +++ b/doc/gettingstarted/helloexpress.md @@ -1,8 +1,8 @@ # Hello Express -This is a very basic application. Much simpler than one created with [Express Command Line](#). Still it's good to understand the basic concepts by creating an app by hands. Keep in mind that you have to follow [Installation instructions](./installing.md) first. If you are doing it on a Mac, please, install [Swift 2.2 latest development](https://swift.org/download/#latest-development-snapshots) snapshot in addition. +This is a very basic application. Much simpler than one created with [Express Command Line](./commandline.md). Still it's good to understand the basic concepts by creating an app by hands. Keep in mind that you have to follow [Installation instructions](./installing.md) first. If you are doing it on a Mac, please, install [Swift 2.2 latest development](https://swift.org/download/#latest-development-snapshots) snapshot in addition. -##### If you don't want to create an app manually, just skip this tutorial and use [Express Command Line](#) tool instead. +##### If you don't want to create an app manually, just skip this tutorial and use [Express Command Line](./commandline.md) tool instead. ## Create a folder @@ -91,4 +91,4 @@ Enter the following url in the browser: [http://localhost:9999/](http://localhos http://localhost:9999/ ``` -# Next tutorial: [Express command line](#) \ No newline at end of file +# Next tutorial: [Express Command Line](./commandline.md) \ No newline at end of file diff --git a/doc/gettingstarted/installing.md b/doc/gettingstarted/installing.md index 51c3e6d..baa9d87 100644 --- a/doc/gettingstarted/installing.md +++ b/doc/gettingstarted/installing.md @@ -105,4 +105,6 @@ make ```sh make install -``` \ No newline at end of file +``` + +# Next tutorial: [Hello Express](./helloexpress.md) \ No newline at end of file diff --git a/doc/index.md b/doc/index.md index 7d22c8f..e66bf1f 100644 --- a/doc/index.md +++ b/doc/index.md @@ -3,7 +3,7 @@ * Getting started * [Installing](./gettingstarted/installing.md) * [Hello Express](./gettingstarted/helloexpress.md) - * Express command line + * [Express Command Line](./gettingstarted/commandline.md) * Basic routing * Static files * Basic error handling \ No newline at end of file From 303699256bf09d6c0526502d036b1b8d65edcb41 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 19:42:58 +0200 Subject: [PATCH 74/88] Added basic routing stub --- doc/gettingstarted/routing.md | 2 ++ doc/index.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 doc/gettingstarted/routing.md diff --git a/doc/gettingstarted/routing.md b/doc/gettingstarted/routing.md new file mode 100644 index 0000000..1fd652f --- /dev/null +++ b/doc/gettingstarted/routing.md @@ -0,0 +1,2 @@ +# Basic Routing + diff --git a/doc/index.md b/doc/index.md index e66bf1f..387132c 100644 --- a/doc/index.md +++ b/doc/index.md @@ -4,6 +4,6 @@ * [Installing](./gettingstarted/installing.md) * [Hello Express](./gettingstarted/helloexpress.md) * [Express Command Line](./gettingstarted/commandline.md) - * Basic routing + * [Basic routing](./gettingstarted/routing.md) * Static files * Basic error handling \ No newline at end of file From 6a4b3b46bc96212ba91d724e102b96e5cc26e262 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 19:43:09 +0200 Subject: [PATCH 75/88] Added command line tutorial --- doc/gettingstarted/commandline.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/doc/gettingstarted/commandline.md b/doc/gettingstarted/commandline.md index c0e7d06..00d3f2b 100644 --- a/doc/gettingstarted/commandline.md +++ b/doc/gettingstarted/commandline.md @@ -1,2 +1,30 @@ # Express Command Line + + +
    +

    + + Note: Express Command Line tool is not yet officially supported on Linux. This tutorial works on OS X only. + +

    +
    + +Using [Express](http://swiftexpress.io) is rather easy. To create a project you just need to type: + +```sh +swift-express init YourProject +``` + +ant it will create you the whole directory structure along with xCode project. + +You can run your project by typing following test while in the project folder: + +```sh +swift-express run +``` + +Full documentation of [Express Command Line](https://github.com/crossroadlabs/ExpressCommandLine) can be found here [crossroadlabs/ExpressCommandLine](https://github.com/crossroadlabs/ExpressCommandLine). + +# Next tutorial: [Basic Routing](./routing.md.md) + From 3752283165da2972266a5ba1da103a46529b9949 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 19:45:16 +0200 Subject: [PATCH 76/88] fixed link --- doc/gettingstarted/commandline.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/gettingstarted/commandline.md b/doc/gettingstarted/commandline.md index 00d3f2b..c46195e 100644 --- a/doc/gettingstarted/commandline.md +++ b/doc/gettingstarted/commandline.md @@ -26,5 +26,5 @@ swift-express run Full documentation of [Express Command Line](https://github.com/crossroadlabs/ExpressCommandLine) can be found here [crossroadlabs/ExpressCommandLine](https://github.com/crossroadlabs/ExpressCommandLine). -# Next tutorial: [Basic Routing](./routing.md.md) +# Next tutorial: [Basic Routing](./routing.md) From 08ae10b8709031d4d91ddbbd3c41c24897a78d49 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 19:48:50 +0200 Subject: [PATCH 77/88] Full getting started stubs --- doc/gettingstarted/errorhandling.md | 1 + doc/gettingstarted/static.md | 1 + doc/index.md | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 doc/gettingstarted/errorhandling.md create mode 100644 doc/gettingstarted/static.md diff --git a/doc/gettingstarted/errorhandling.md b/doc/gettingstarted/errorhandling.md new file mode 100644 index 0000000..f791a0e --- /dev/null +++ b/doc/gettingstarted/errorhandling.md @@ -0,0 +1 @@ +# Basic error handling \ No newline at end of file diff --git a/doc/gettingstarted/static.md b/doc/gettingstarted/static.md new file mode 100644 index 0000000..b977690 --- /dev/null +++ b/doc/gettingstarted/static.md @@ -0,0 +1 @@ +# Static files \ No newline at end of file diff --git a/doc/index.md b/doc/index.md index 387132c..d9a02aa 100644 --- a/doc/index.md +++ b/doc/index.md @@ -5,5 +5,5 @@ * [Hello Express](./gettingstarted/helloexpress.md) * [Express Command Line](./gettingstarted/commandline.md) * [Basic routing](./gettingstarted/routing.md) - * Static files - * Basic error handling \ No newline at end of file + * [Static files](./gettingstarted/static.md) + * [Basic error handling](./gettingstarted/errorhandling.md) \ No newline at end of file From 962a4a695ad423db75fbb9e82d2815d2229175ad Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 19:52:26 +0200 Subject: [PATCH 78/88] Interlinking --- doc/gettingstarted/errorhandling.md | 4 +++- doc/gettingstarted/routing.md | 1 + doc/gettingstarted/static.md | 4 +++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/doc/gettingstarted/errorhandling.md b/doc/gettingstarted/errorhandling.md index f791a0e..da2cf9c 100644 --- a/doc/gettingstarted/errorhandling.md +++ b/doc/gettingstarted/errorhandling.md @@ -1 +1,3 @@ -# Basic error handling \ No newline at end of file +# Basic error handling + +# Next tutorial: [Advanced something](#) \ No newline at end of file diff --git a/doc/gettingstarted/routing.md b/doc/gettingstarted/routing.md index 1fd652f..08affc3 100644 --- a/doc/gettingstarted/routing.md +++ b/doc/gettingstarted/routing.md @@ -1,2 +1,3 @@ # Basic Routing +# Next tutorial: [Static files](./static.md) \ No newline at end of file diff --git a/doc/gettingstarted/static.md b/doc/gettingstarted/static.md index b977690..f87ca9c 100644 --- a/doc/gettingstarted/static.md +++ b/doc/gettingstarted/static.md @@ -1 +1,3 @@ -# Static files \ No newline at end of file +# Static files + +# Next tutorial: [Basic error handling](./errorhandling.md) \ No newline at end of file From 106aa0e9dc7c5f98ce7979ec8ba04e5ea44bd53b Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 20:04:30 +0200 Subject: [PATCH 79/88] basic routing --- doc/gettingstarted/routing.md | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/doc/gettingstarted/routing.md b/doc/gettingstarted/routing.md index 08affc3..2592d66 100644 --- a/doc/gettingstarted/routing.md +++ b/doc/gettingstarted/routing.md @@ -1,3 +1,54 @@ # Basic Routing +[Express](http://swiftexpress.io) routing definitions have the following pattern: + +```swift +app.METHOD(PATH, HANDLER) +``` + +`METHOD` can be one of the `get`, `post`, `put`, `delete`, `patch` or `all`. + +##### Examples: + +Respond with `Hello World!` on the homepage: + +```swift +app.get("/") { request in + return Action.ok("Hello World!") +} +``` + +Respond to `POST` request on the root route `/`, the application’s home page: + +```swift +app.post("/") { request in + return Action.ok("Got a POST request") +} +``` + +Respond to a `PUT` request to the `/user` route: + +```swift +app.put("/user") { request in + return Action.ok("Got a PUT request at /user") +} +``` + +Respond to a `DELETE` request to the `/user` route: + +```swift +app.delete("/user") { request in + return Action.ok("Got a DELETE request at /user") +} +``` + +Respond to all methods requests on the `/user` route: + +```swift +app.all("/user") { request in + return Action.ok("Got a " + request.method + " request at /user") +} +``` + + # Next tutorial: [Static files](./static.md) \ No newline at end of file From 84611163dc25302b8d87ba60adfa9b0fd48dd185 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 20:09:47 +0200 Subject: [PATCH 80/88] static files --- doc/gettingstarted/static.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/gettingstarted/static.md b/doc/gettingstarted/static.md index f87ca9c..644e564 100644 --- a/doc/gettingstarted/static.md +++ b/doc/gettingstarted/static.md @@ -1,3 +1,17 @@ # Static files +Static files are very easy to be served with [Express](http://swiftexpress.io): + +```swift +app.get("/:file+", action: StaticAction(path: "public", param:"file")) +``` + +The code above tells [Express](http://swiftexpress.io/) to serve all static files from the `public` folder recursively (i.e. it will serve both `public/article.html` as well as `public/articles/awesome.html`). If you want to serve just the first level in folder, use: + +```swift +app.get("/:file", action: StaticAction(path: "public", param:"file")) +``` + +The difference is just in the pattern: `/:file` versus `/:file+`. For more information see our [Advanced Routing](#) section. + # Next tutorial: [Basic error handling](./errorhandling.md) \ No newline at end of file From c85b9676e580e339cdaf46d2c1a3aeb9a9c28329 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 20:34:21 +0200 Subject: [PATCH 81/88] Added error handling tutorial --- Demo/main.swift | 23 +++++++++++ doc/gettingstarted/errorhandling.md | 60 +++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) diff --git a/Demo/main.swift b/Demo/main.swift index e4e5eeb..4c1c076 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -42,6 +42,29 @@ func test() throws -> Action { throw TestError.Test2 } +enum NastyError : ErrorType { + case Recoverable + case Fatal(reason:String) +} + +app.get("/error/:fatal?") { request in + guard let fatal = request.params["fatal"] else { + throw NastyError.Recoverable + } + + throw NastyError.Fatal(reason: fatal) +} + +app.errorHandler.register { (e:NastyError) in + switch e { + case .Recoverable: + return Action.redirect("/") + case .Fatal(let reason): + let content = AnyContent(str: "Unrecoverable nasty error happened. Reason: " + reason) + return Action.response(.InternalServerError, content: content) + } +} + app.errorHandler.register { e in guard let e = e as? TestError else { return nil diff --git a/doc/gettingstarted/errorhandling.md b/doc/gettingstarted/errorhandling.md index da2cf9c..0a42f6a 100644 --- a/doc/gettingstarted/errorhandling.md +++ b/doc/gettingstarted/errorhandling.md @@ -1,3 +1,63 @@ # Basic error handling +If you want to generate an error in [Express](http://swiftexpress.io/) you need to throw an exception. + +Let's say you have a `NastyError` defined: + +```swift +enum NastyError : ErrorType { + case Recoverable + case Fatal(reason:String) +} +``` + +Here is an example on how to throw it: + +```swift +app.get("/error/:fatal?") { request in + guard let fatal = request.params["fatal"] else { + throw NastyError.Recoverable + } + + throw NastyError.Fatal(reason: fatal) +} +``` + +Now how to handle it. In [Express](http://swiftexpress.io/) you can define error handlers of two types. General and specific. Specific are the ones with the specified `Error` type. In our case it's `NastyError`. Here is an example: + +```swift +app.errorHandler.register { (e:NastyError) in + switch e { + case .Recoverable: + return Action.redirect("/") + case .Fatal(let reason): + let content = AnyContent(str: "Unrecoverable nasty error happened. Reason: " + reason) + return Action.response(.InternalServerError, content: content) + } +} +``` + +General error handlers are the same except you omit the error type. Something like this: + +```swift +app.errorHandler.register { e in + return nil +} +``` + +If your handler can handle the error, you return an `Action`. Otherwise you return `nil`. Here is an example of selective error handling: + +```swift +/// Custom page not found error handler +app.errorHandler.register { (e:ExpressError) in + switch e { + case .PageNotFound(let path): + return Action.render("404", context: ["path": path], status: .NotFound) + default: + return nil + } +} +``` + + # Next tutorial: [Advanced something](#) \ No newline at end of file From 95706da10ed680ac222b2219337b67dedff2b8b2 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 20:44:02 +0200 Subject: [PATCH 82/88] Demo cleanup --- Demo/main.swift | 69 ++----------------- Demo/views/colored.mustache | 1 - .../{colored2.stencil => colored.stencil} | 0 Demo/views/test.mustache | 11 --- Demo/views/{test2.stencil => test.stencil} | 2 +- Express.xcodeproj/project.pbxproj | 24 +++---- 6 files changed, 13 insertions(+), 94 deletions(-) delete mode 100644 Demo/views/colored.mustache rename Demo/views/{colored2.stencil => colored.stencil} (100%) delete mode 100644 Demo/views/test.mustache rename Demo/views/{test2.stencil => test.stencil} (75%) diff --git a/Demo/main.swift b/Demo/main.swift index 4c1c076..ee5e8ce 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -23,25 +23,6 @@ app.get("/echo") { request in return Action.ok(request.query["call"]?.first) } -enum TestError { - case Test - case Test2 - - func items() -> Dictionary { - switch self { - case .Test: return ["blood": "red"] - case .Test2: return ["sickness": "purple"] - } - } -} - -extension TestError : ErrorType { -} - -func test() throws -> Action { - throw TestError.Test2 -} - enum NastyError : ErrorType { case Recoverable case Fatal(reason:String) @@ -65,23 +46,6 @@ app.errorHandler.register { (e:NastyError) in } } -app.errorHandler.register { e in - guard let e = e as? TestError else { - return nil - } - - let items = e.items() - - let viewItems = items.map { (k, v) in - ["name": k, "color": v] - } - - let context:[String: Any] = ["test": "error", "items": viewItems] - - return Action.render("test", context: context) -} - - /// Custom page not found error handler app.errorHandler.register { (e:ExpressError) in switch e { @@ -172,22 +136,19 @@ func testItems(request:Request) throws -> [String: Any] { ["name": k, "color": v] } - if ((request.query["throw"]?.first) != nil) { - throw TestError.Test + if let reason = request.query["throw"]?.first { + throw NastyError.Fatal(reason: reason) } return ["test": "ok", "items": viewItems] } -app.get("/test.html") { request in +app.get("/render.html") { request in let items = try testItems(request) return Action.render("test", context: items) } -app.get("/test2.html") { request in - return Action.render("test2", context: try testItems(request)) -} - +//TODO: make a list of pages app.get("/") { request in for me in request.body?.asJSON().map({$0["test"]}) { print(me) @@ -195,26 +156,6 @@ app.get("/") { request in return Action.ok(AnyContent(str:"{\"response\": \"hey hey\"}", contentType: "application/json")) } -func echoData(request:Request) -> Dictionary { - let call = request.body?.asJSON().map({$0["say"]})?.string - let response = call.getOrElse("I don't hear you!") - return ["said": response] -} - -func echo(request:Request) -> Action { - let data = echoData(request) - let tuple = data.first! - let str = "{\"" + tuple.0 + "\": \"" + tuple.1 + "\"}" - - return Action.ok(AnyContent(str:str, contentType: "application/json")) -} - -func echoRender(request:Request) -> Action { - var data = echoData(request) - data["hey"] = "Hello from render" - return Action.render(JsonView.name, context: data) -} - app.get("/test/redirect") { request in return future { let to = request.query["to"].flatMap{$0.first}.getOrElse("../test.html") @@ -231,5 +172,3 @@ app.listen(9999).onSuccess { server in } app.run() - -//TODO: proper error handling for sync requests \ No newline at end of file diff --git a/Demo/views/colored.mustache b/Demo/views/colored.mustache deleted file mode 100644 index 4f551d2..0000000 --- a/Demo/views/colored.mustache +++ /dev/null @@ -1 +0,0 @@ -
  • Item: {{name}}
  • \ No newline at end of file diff --git a/Demo/views/colored2.stencil b/Demo/views/colored.stencil similarity index 100% rename from Demo/views/colored2.stencil rename to Demo/views/colored.stencil diff --git a/Demo/views/test.mustache b/Demo/views/test.mustache deleted file mode 100644 index 76555ac..0000000 --- a/Demo/views/test.mustache +++ /dev/null @@ -1,11 +0,0 @@ - - - -

    Test: {{test}}

    -

    -{{#items}} -{{> colored }} -{{/items}} -

    - - diff --git a/Demo/views/test2.stencil b/Demo/views/test.stencil similarity index 75% rename from Demo/views/test2.stencil rename to Demo/views/test.stencil index ea8a9a1..2951ee0 100644 --- a/Demo/views/test2.stencil +++ b/Demo/views/test.stencil @@ -4,7 +4,7 @@

    Test Stencil: {{test}}

    {% for item in items %} -{% include "colored2.stencil" %} +{% include "colored.stencil" %} {% endfor %}

    diff --git a/Express.xcodeproj/project.pbxproj b/Express.xcodeproj/project.pbxproj index fa7cdca..7da15a5 100644 --- a/Express.xcodeproj/project.pbxproj +++ b/Express.xcodeproj/project.pbxproj @@ -39,7 +39,6 @@ 657C1A6E1C24C8850037041F /* Views.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657C1A6D1C24C8850037041F /* Views.swift */; }; 657C1A701C24C8FF0037041F /* ViewEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657C1A6F1C24C8FF0037041F /* ViewEngine.swift */; }; 657C1A721C24DD1D0037041F /* MustacheViewEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657C1A711C24DD1D0037041F /* MustacheViewEngine.swift */; }; - 657C1A751C24E0F40037041F /* test.mustache in Resources */ = {isa = PBXBuildFile; fileRef = 657C1A741C24E0F40037041F /* test.mustache */; }; 657CDBCF1C2A07C5005261BA /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 657CDBCE1C2A07C5005261BA /* Errors.swift */; }; 6589201B1C7A2982008EEE37 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6589201A1C7A2982008EEE37 /* Utils.swift */; }; 658DBDA31C221A2A00BE8AA9 /* BrightFutures.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 658DBDA01C221A2A00BE8AA9 /* BrightFutures.framework */; }; @@ -51,7 +50,6 @@ 658DBDAF1C22218100BE8AA9 /* Headers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658DBDAE1C22218100BE8AA9 /* Headers.swift */; }; 658DBDB11C2228F000BE8AA9 /* Response.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658DBDB01C2228F000BE8AA9 /* Response.swift */; }; 658F1CC81C3BF017004968B9 /* UrlMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658F1CC71C3BF017004968B9 /* UrlMatcher.swift */; }; - 659157FF1C285AFA00672785 /* colored.mustache in Resources */ = {isa = PBXBuildFile; fileRef = 659157FE1C285AFA00672785 /* colored.mustache */; }; 659E80AB1C7756C200DE85B1 /* TidyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 659E80AA1C7756C200DE85B1 /* TidyJSON.framework */; }; 659E80AC1C7756D000DE85B1 /* TidyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 659E80AA1C7756C200DE85B1 /* TidyJSON.framework */; }; 659E80AD1C7756DD00DE85B1 /* TidyJSON.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 659E80AA1C7756C200DE85B1 /* TidyJSON.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -75,8 +73,8 @@ 65E646E81C79A5010036D028 /* Stencil.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 65E646E21C79A4EB0036D028 /* Stencil.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 65E646EA1C79A5340036D028 /* StencilViewEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E646E91C79A5340036D028 /* StencilViewEngine.swift */; }; 65E646EC1C79AD280036D028 /* hello.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646EB1C79AD280036D028 /* hello.stencil */; }; - 65E646EE1C79AF4E0036D028 /* test2.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646ED1C79AF4E0036D028 /* test2.stencil */; }; - 65E646F01C79AF6C0036D028 /* colored2.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646EF1C79AF6C0036D028 /* colored2.stencil */; }; + 65E646EE1C79AF4E0036D028 /* test.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646ED1C79AF4E0036D028 /* test.stencil */; }; + 65E646F01C79AF6C0036D028 /* colored.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 65E646EF1C79AF6C0036D028 /* colored.stencil */; }; 961B9E8B1C23229A000506D2 /* libevent.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 961B9E8A1C23229A000506D2 /* libevent.dylib */; }; 961B9E8F1C2322CA000506D2 /* libevent_openssl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 961B9E8E1C2322CA000506D2 /* libevent_openssl.dylib */; }; 961B9E911C2324B4000506D2 /* EVHTP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 961B9E901C2324B4000506D2 /* EVHTP.swift */; }; @@ -142,7 +140,6 @@ 657C1A6D1C24C8850037041F /* Views.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Views.swift; sourceTree = ""; }; 657C1A6F1C24C8FF0037041F /* ViewEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewEngine.swift; sourceTree = ""; }; 657C1A711C24DD1D0037041F /* MustacheViewEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MustacheViewEngine.swift; sourceTree = ""; }; - 657C1A741C24E0F40037041F /* test.mustache */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = test.mustache; sourceTree = ""; }; 657CDBCE1C2A07C5005261BA /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = ""; }; 6589201A1C7A2982008EEE37 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 658DBDA01C221A2A00BE8AA9 /* BrightFutures.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BrightFutures.framework; path = Carthage/Build/Mac/BrightFutures.framework; sourceTree = ""; }; @@ -150,7 +147,6 @@ 658DBDAE1C22218100BE8AA9 /* Headers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Headers.swift; sourceTree = ""; }; 658DBDB01C2228F000BE8AA9 /* Response.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Response.swift; sourceTree = ""; }; 658F1CC71C3BF017004968B9 /* UrlMatcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UrlMatcher.swift; sourceTree = ""; }; - 659157FE1C285AFA00672785 /* colored.mustache */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = colored.mustache; sourceTree = ""; }; 659E80AA1C7756C200DE85B1 /* TidyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = TidyJSON.framework; path = Carthage/Build/Mac/TidyJSON.framework; sourceTree = ""; }; 65BA1E971C25D38200BCE076 /* JsonView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JsonView.swift; sourceTree = ""; }; 65BB8DAA1C2A226E00F8B52C /* ErrorHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = ""; }; @@ -164,8 +160,8 @@ 65E646E21C79A4EB0036D028 /* Stencil.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Stencil.framework; path = Carthage/Build/Mac/Stencil.framework; sourceTree = ""; }; 65E646E91C79A5340036D028 /* StencilViewEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StencilViewEngine.swift; sourceTree = ""; }; 65E646EB1C79AD280036D028 /* hello.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = hello.stencil; sourceTree = ""; }; - 65E646ED1C79AF4E0036D028 /* test2.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = test2.stencil; sourceTree = ""; }; - 65E646EF1C79AF6C0036D028 /* colored2.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = colored2.stencil; sourceTree = ""; }; + 65E646ED1C79AF4E0036D028 /* test.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = test.stencil; sourceTree = ""; }; + 65E646EF1C79AF6C0036D028 /* colored.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = colored.stencil; sourceTree = ""; }; 961B9E8A1C23229A000506D2 /* libevent.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libevent.dylib; path = ../../../../../usr/local/lib/libevent.dylib; sourceTree = ""; }; 961B9E8E1C2322CA000506D2 /* libevent_openssl.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libevent_openssl.dylib; path = ../../../../../usr/local/lib/libevent_openssl.dylib; sourceTree = ""; }; 961B9E901C2324B4000506D2 /* EVHTP.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EVHTP.swift; sourceTree = ""; }; @@ -302,11 +298,9 @@ 657C1A731C24E0F40037041F /* views */ = { isa = PBXGroup; children = ( - 657C1A741C24E0F40037041F /* test.mustache */, - 659157FE1C285AFA00672785 /* colored.mustache */, 65E646EB1C79AD280036D028 /* hello.stencil */, - 65E646ED1C79AF4E0036D028 /* test2.stencil */, - 65E646EF1C79AF6C0036D028 /* colored2.stencil */, + 65E646ED1C79AF4E0036D028 /* test.stencil */, + 65E646EF1C79AF6C0036D028 /* colored.stencil */, 651A23441C80552100A8480E /* 404.stencil */, ); path = views; @@ -434,11 +428,9 @@ buildActionMask = 2147483647; files = ( 65E646EC1C79AD280036D028 /* hello.stencil in Resources */, - 65E646F01C79AF6C0036D028 /* colored2.stencil in Resources */, - 65E646EE1C79AF4E0036D028 /* test2.stencil in Resources */, + 65E646F01C79AF6C0036D028 /* colored.stencil in Resources */, + 65E646EE1C79AF4E0036D028 /* test.stencil in Resources */, 651A23451C80552100A8480E /* 404.stencil in Resources */, - 659157FF1C285AFA00672785 /* colored.mustache in Resources */, - 657C1A751C24E0F40037041F /* test.mustache in Resources */, 6550774A1C2DC31500E42309 /* logo.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; From 1b16c6b9354e8993f2464ce599b961d1befc43d3 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 21:26:18 +0200 Subject: [PATCH 83/88] Demo index --- Demo/main.swift | 18 ++++++++++++++---- Demo/views/index.stencil | 8 ++++++++ Express.xcodeproj/project.pbxproj | 4 ++++ 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 Demo/views/index.stencil diff --git a/Demo/main.swift b/Demo/main.swift index ee5e8ce..7b2cdb2 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -150,10 +150,20 @@ app.get("/render.html") { request in //TODO: make a list of pages app.get("/") { request in - for me in request.body?.asJSON().map({$0["test"]}) { - print(me) - } - return Action.ok(AnyContent(str:"{\"response\": \"hey hey\"}", contentType: "application/json")) + let examples = [ + ["title": "Hello Express", "link": "/hello"], + ["title": "Echo", "link": "/echo?call=hello"], + ["title": "Echo with param", "link": "/echo/hello"], + ["title": "Error recoverable (will redirect back)", "link": "/error"], + ["title": "Error fatal", "link": "/error/thebigbanghappened"], + ["title": "Custom 404", "link": "/thisfiledoesnotexist"], + ["title": "Hello [username]. You can put your name instead", "link": "/hello/username.html"], + ///api/user - implement JSON post form + ["title": "Asynchronous factorial", "link": "/factorial/200"], + ["title": "Render", "link": "/render.html?sun=yellow&clouds=lightgray"], + ] + + return Action.render("index", context: ["examples": examples]) } app.get("/test/redirect") { request in diff --git a/Demo/views/index.stencil b/Demo/views/index.stencil new file mode 100644 index 0000000..5a3f191 --- /dev/null +++ b/Demo/views/index.stencil @@ -0,0 +1,8 @@ + + +

    Examples:

    +{% for example in examples %} +

    {{example.title}}

    +{% endfor %} + + \ No newline at end of file diff --git a/Express.xcodeproj/project.pbxproj b/Express.xcodeproj/project.pbxproj index 7da15a5..23f712b 100644 --- a/Express.xcodeproj/project.pbxproj +++ b/Express.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 6518267F1C22081600E49AC5 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6518267E1C22081600E49AC5 /* main.swift */; }; 651826801C2208A800E49AC5 /* Express.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 651475CE1C21F1550049825D /* Express.framework */; }; 651A23451C80552100A8480E /* 404.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 651A23441C80552100A8480E /* 404.stencil */; }; + 651A23471C80D98700A8480E /* index.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 651A23461C80D98700A8480E /* index.stencil */; }; 6525AC541C24E3C600131F58 /* Mustache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; }; 6525AC551C24E3E000131F58 /* Mustache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; }; 6525AC561C24E3E700131F58 /* Mustache.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -129,6 +130,7 @@ 6518267A1C2207D200E49AC5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6518267E1C22081600E49AC5 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 651A23441C80552100A8480E /* 404.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = 404.stencil; sourceTree = ""; }; + 651A23461C80D98700A8480E /* index.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = index.stencil; sourceTree = ""; }; 6525AC531C24E3C600131F58 /* Mustache.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Mustache.framework; path = Carthage/Build/Mac/Mustache.framework; sourceTree = ""; }; 655077451C2D936100E42309 /* Static.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Static.swift; sourceTree = ""; }; 655077491C2DC31500E42309 /* logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo.png; sourceTree = ""; }; @@ -302,6 +304,7 @@ 65E646ED1C79AF4E0036D028 /* test.stencil */, 65E646EF1C79AF6C0036D028 /* colored.stencil */, 651A23441C80552100A8480E /* 404.stencil */, + 651A23461C80D98700A8480E /* index.stencil */, ); path = views; sourceTree = ""; @@ -428,6 +431,7 @@ buildActionMask = 2147483647; files = ( 65E646EC1C79AD280036D028 /* hello.stencil in Resources */, + 651A23471C80D98700A8480E /* index.stencil in Resources */, 65E646F01C79AF6C0036D028 /* colored.stencil in Resources */, 65E646EE1C79AF4E0036D028 /* test.stencil in Resources */, 651A23451C80552100A8480E /* 404.stencil in Resources */, From b5a8256467bdef260e9a001b60888cc38399c6aa Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 21:29:53 +0200 Subject: [PATCH 84/88] fixed factorial --- Demo/main.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Demo/main.swift b/Demo/main.swift index 7b2cdb2..7bb7d72 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -159,7 +159,7 @@ app.get("/") { request in ["title": "Custom 404", "link": "/thisfiledoesnotexist"], ["title": "Hello [username]. You can put your name instead", "link": "/hello/username.html"], ///api/user - implement JSON post form - ["title": "Asynchronous factorial", "link": "/factorial/200"], + ["title": "Asynchronous factorial", "link": "/factorial/100"], ["title": "Render", "link": "/render.html?sun=yellow&clouds=lightgray"], ] From a72e39162a254aac008839cacf360f75e5a2f590 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 21:34:45 +0200 Subject: [PATCH 85/88] fixed 404 --- Demo/views/404.stencil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Demo/views/404.stencil b/Demo/views/404.stencil index 5f63434..749ae48 100644 --- a/Demo/views/404.stencil +++ b/Demo/views/404.stencil @@ -1,6 +1,6 @@ -

    +

    404: Page not found

    {{path}}

    From 1e10bcb486d9833e79b7e13ad2ee127672c3ac95 Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 21:35:04 +0200 Subject: [PATCH 86/88] added 2 more examples --- Demo/main.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Demo/main.swift b/Demo/main.swift index 7bb7d72..4293fd5 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -161,6 +161,8 @@ app.get("/") { request in ///api/user - implement JSON post form ["title": "Asynchronous factorial", "link": "/factorial/100"], ["title": "Render", "link": "/render.html?sun=yellow&clouds=lightgray"], + ["title": "Redirect", "link": "/test/redirect"], + ["title": "Merged query (form url encoded and query string)", "link": "/merged/query?some=param&another=param2"], ] return Action.render("index", context: ["examples": examples]) @@ -168,12 +170,12 @@ app.get("/") { request in app.get("/test/redirect") { request in return future { - let to = request.query["to"].flatMap{$0.first}.getOrElse("../test.html") + let to = request.query["to"].flatMap{$0.first}.getOrElse("../render.html") return Action.redirect(to) } } -app.post("/merged/query") { request in +app.all("/merged/query") { request in Action.render(JsonView.name, context: request.mergedQuery()) } From e9e45ab003af8a1af6593150ca351d180f33a3bb Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 22:55:38 +0200 Subject: [PATCH 87/88] index.html --- Demo/main.swift | 5 +++-- Demo/public/index.html | 24 ++++++++++++++++++++++++ Express.xcodeproj/project.pbxproj | 4 ++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 Demo/public/index.html diff --git a/Demo/main.swift b/Demo/main.swift index 4293fd5..cd2290c 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -150,7 +150,7 @@ app.get("/render.html") { request in //TODO: make a list of pages app.get("/") { request in - let examples = [ + /*let examples = [ ["title": "Hello Express", "link": "/hello"], ["title": "Echo", "link": "/echo?call=hello"], ["title": "Echo with param", "link": "/echo/hello"], @@ -165,7 +165,8 @@ app.get("/") { request in ["title": "Merged query (form url encoded and query string)", "link": "/merged/query?some=param&another=param2"], ] - return Action.render("index", context: ["examples": examples]) + return Action.render("index", context: ["examples": examples])*/ + return Action.redirect("/index.html") } app.get("/test/redirect") { request in diff --git a/Demo/public/index.html b/Demo/public/index.html new file mode 100644 index 0000000..1680a58 --- /dev/null +++ b/Demo/public/index.html @@ -0,0 +1,24 @@ + + +

    Examples:

    + +

    Hello Express

    + +

    Echo

    + +

    Echo with param

    + +

    Error recoverable (will redirect back)

    + +

    Error fatal

    + +

    Custom 404

    + +

    Hello [username]. You can put your name instead

    + +

    Asynchronous factorial

    + +

    Render

    + + + \ No newline at end of file diff --git a/Express.xcodeproj/project.pbxproj b/Express.xcodeproj/project.pbxproj index 23f712b..815173d 100644 --- a/Express.xcodeproj/project.pbxproj +++ b/Express.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 651826801C2208A800E49AC5 /* Express.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 651475CE1C21F1550049825D /* Express.framework */; }; 651A23451C80552100A8480E /* 404.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 651A23441C80552100A8480E /* 404.stencil */; }; 651A23471C80D98700A8480E /* index.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 651A23461C80D98700A8480E /* index.stencil */; }; + 651A23491C80F31000A8480E /* index.html in Resources */ = {isa = PBXBuildFile; fileRef = 651A23481C80F31000A8480E /* index.html */; }; 6525AC541C24E3C600131F58 /* Mustache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; }; 6525AC551C24E3E000131F58 /* Mustache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; }; 6525AC561C24E3E700131F58 /* Mustache.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -131,6 +132,7 @@ 6518267E1C22081600E49AC5 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 651A23441C80552100A8480E /* 404.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = 404.stencil; sourceTree = ""; }; 651A23461C80D98700A8480E /* index.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = index.stencil; sourceTree = ""; }; + 651A23481C80F31000A8480E /* index.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = index.html; sourceTree = ""; }; 6525AC531C24E3C600131F58 /* Mustache.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Mustache.framework; path = Carthage/Build/Mac/Mustache.framework; sourceTree = ""; }; 655077451C2D936100E42309 /* Static.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Static.swift; sourceTree = ""; }; 655077491C2DC31500E42309 /* logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo.png; sourceTree = ""; }; @@ -293,6 +295,7 @@ isa = PBXGroup; children = ( 655077491C2DC31500E42309 /* logo.png */, + 651A23481C80F31000A8480E /* index.html */, ); path = public; sourceTree = ""; @@ -430,6 +433,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 651A23491C80F31000A8480E /* index.html in Resources */, 65E646EC1C79AD280036D028 /* hello.stencil in Resources */, 651A23471C80D98700A8480E /* index.stencil in Resources */, 65E646F01C79AF6C0036D028 /* colored.stencil in Resources */, From 8f73fa7037767ab2d07a31ccf047571e836bd88e Mon Sep 17 00:00:00 2001 From: Daniel Leping Date: Fri, 26 Feb 2016 23:04:34 +0200 Subject: [PATCH 88/88] back to normal --- Demo/main.swift | 7 ++++--- Demo/public/index.html | 24 ------------------------ Express.xcodeproj/project.pbxproj | 4 ---- 3 files changed, 4 insertions(+), 31 deletions(-) delete mode 100644 Demo/public/index.html diff --git a/Demo/main.swift b/Demo/main.swift index cd2290c..7728987 100644 --- a/Demo/main.swift +++ b/Demo/main.swift @@ -150,7 +150,7 @@ app.get("/render.html") { request in //TODO: make a list of pages app.get("/") { request in - /*let examples = [ + let examples:[Any] = [ ["title": "Hello Express", "link": "/hello"], ["title": "Echo", "link": "/echo?call=hello"], ["title": "Echo with param", "link": "/echo/hello"], @@ -165,8 +165,9 @@ app.get("/") { request in ["title": "Merged query (form url encoded and query string)", "link": "/merged/query?some=param&another=param2"], ] - return Action.render("index", context: ["examples": examples])*/ - return Action.redirect("/index.html") + let context:[String: Any] = ["examples": examples] + + return Action.render("index", context: context) } app.get("/test/redirect") { request in diff --git a/Demo/public/index.html b/Demo/public/index.html deleted file mode 100644 index 1680a58..0000000 --- a/Demo/public/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - -

    Examples:

    - -

    Hello Express

    - -

    Echo

    - -

    Echo with param

    - -

    Error recoverable (will redirect back)

    - -

    Error fatal

    - -

    Custom 404

    - -

    Hello [username]. You can put your name instead

    - -

    Asynchronous factorial

    - -

    Render

    - - - \ No newline at end of file diff --git a/Express.xcodeproj/project.pbxproj b/Express.xcodeproj/project.pbxproj index 815173d..23f712b 100644 --- a/Express.xcodeproj/project.pbxproj +++ b/Express.xcodeproj/project.pbxproj @@ -27,7 +27,6 @@ 651826801C2208A800E49AC5 /* Express.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 651475CE1C21F1550049825D /* Express.framework */; }; 651A23451C80552100A8480E /* 404.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 651A23441C80552100A8480E /* 404.stencil */; }; 651A23471C80D98700A8480E /* index.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 651A23461C80D98700A8480E /* index.stencil */; }; - 651A23491C80F31000A8480E /* index.html in Resources */ = {isa = PBXBuildFile; fileRef = 651A23481C80F31000A8480E /* index.html */; }; 6525AC541C24E3C600131F58 /* Mustache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; }; 6525AC551C24E3E000131F58 /* Mustache.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; }; 6525AC561C24E3E700131F58 /* Mustache.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 6525AC531C24E3C600131F58 /* Mustache.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -132,7 +131,6 @@ 6518267E1C22081600E49AC5 /* main.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 651A23441C80552100A8480E /* 404.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = 404.stencil; sourceTree = ""; }; 651A23461C80D98700A8480E /* index.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = index.stencil; sourceTree = ""; }; - 651A23481C80F31000A8480E /* index.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = index.html; sourceTree = ""; }; 6525AC531C24E3C600131F58 /* Mustache.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Mustache.framework; path = Carthage/Build/Mac/Mustache.framework; sourceTree = ""; }; 655077451C2D936100E42309 /* Static.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Static.swift; sourceTree = ""; }; 655077491C2DC31500E42309 /* logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo.png; sourceTree = ""; }; @@ -295,7 +293,6 @@ isa = PBXGroup; children = ( 655077491C2DC31500E42309 /* logo.png */, - 651A23481C80F31000A8480E /* index.html */, ); path = public; sourceTree = ""; @@ -433,7 +430,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 651A23491C80F31000A8480E /* index.html in Resources */, 65E646EC1C79AD280036D028 /* hello.stencil in Resources */, 651A23471C80D98700A8480E /* index.stencil in Resources */, 65E646F01C79AF6C0036D028 /* colored.stencil in Resources */,