-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Type scheme once only #5
Changes from 5 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,21 +3,21 @@ import Foundation | |
public typealias SimpleRouter = Router<Void> | ||
|
||
public final class Router<UserInfo> { | ||
public let scheme: String | ||
private let scheme: String | ||
private var routes: [Route<UserInfo>] = [] | ||
|
||
public init(scheme: String) { | ||
self.scheme = scheme | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you execute |
||
internal func register(_ route: Route<UserInfo>) { | ||
if scheme != route.patternURL.scheme { | ||
assertionFailure("Router and pattern must have the same schemes. expect: \(scheme), actual: \(route.patternURL.scheme)") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please do not remove this error. |
||
} else { | ||
routes.append(route) | ||
} | ||
} | ||
|
||
@discardableResult | ||
public func openIfPossible(_ url: URL, userInfo: UserInfo) -> Bool { | ||
if scheme != url.scheme { | ||
|
@@ -32,10 +32,16 @@ public final class Router<UserInfo> { | |
} | ||
return routes.first { $0.responds(to: url, userInfo: userInfo) } != nil | ||
} | ||
|
||
public func register(_ routes: [(String, Route<UserInfo>.Handler)]) { | ||
for (pattern, handler) in routes { | ||
guard let patternURL = PatternURL(string: pattern) else { | ||
let patternURLString: String | ||
if pattern.hasPrefix("\(scheme)://") { | ||
patternURLString = pattern | ||
} else { | ||
patternURLString = "\(scheme)://\(pattern)" | ||
} | ||
guard let patternURL = PatternURL(string: patternURLString) else { | ||
assertionFailure("\(pattern) is invalid") | ||
continue | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,10 +3,10 @@ import XCTest | |
import Crossroad | ||
|
||
final class RouterTest: XCTestCase { | ||
let schema = "foobar" | ||
let scheme = "foobar" | ||
|
||
func testCanRespond() { | ||
let router = SimpleRouter(scheme: schema) | ||
let router = SimpleRouter(scheme: scheme) | ||
router.register([ | ||
("foobar://static", { _ in true }), | ||
("foobar://foo/bar", { _ in true }), | ||
|
@@ -21,10 +21,39 @@ final class RouterTest: XCTestCase { | |
XCTAssertFalse(router.responds(to: URL(string: "foobar://aaa/bbb")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "notfoobar://aaa/bbb")!)) | ||
XCTAssertTrue(router.responds(to: URL(string: "foobar://spam/ham")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "static")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "foo")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "foo/bar")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "foo/10000")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "aaa/bbb")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "spam/ham")!)) | ||
} | ||
|
||
func testCanRespondWithoutScheme() { | ||
let router = SimpleRouter(scheme: scheme) | ||
router.register([ | ||
("static", { _ in true }), | ||
("foo/bar", { _ in true }), | ||
("spam/ham", { _ in false }), | ||
(":keyword", { _ in true }), | ||
("foo/:keyword", { _ in true }), | ||
]) | ||
XCTAssertTrue(router.responds(to: URL(string: "foobar://static")!)) | ||
XCTAssertTrue(router.responds(to: URL(string: "foobar://foo")!)) | ||
XCTAssertTrue(router.responds(to: URL(string: "foobar://foo/bar")!)) | ||
XCTAssertTrue(router.responds(to: URL(string: "foobar://foo/10000")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "notfoobar://aaa/bbb")!)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add assertions to check without scheme patterns? XCTAssertFalse(router.responds(to: URL(string: "static")!))
XCTAssertFalse(router.responds(to: URL(string: "foo/bar")!))
XCTAssertFalse(router.responds(to: URL(string: "spam/ham")!)) |
||
XCTAssertTrue(router.responds(to: URL(string: "foobar://spam/ham")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "static")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "foo")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "foo/bar")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "foo/10000")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "aaa/bbb")!)) | ||
XCTAssertFalse(router.responds(to: URL(string: "spam/ham")!)) | ||
} | ||
|
||
func testHandle() { | ||
let router = SimpleRouter(scheme: schema) | ||
let router = SimpleRouter(scheme: scheme) | ||
let expectation = self.expectation(description: "Should called handler four times") | ||
expectation.expectedFulfillmentCount = 4 | ||
router.register([ | ||
|
@@ -59,11 +88,60 @@ final class RouterTest: XCTestCase { | |
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://foo/hoge/fuga")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "foobar://spam/ham")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "notfoobar://static")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "static")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "foo/bar?param0=123")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "hoge")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "foo/hoge/fuga")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "spam/ham")!)) | ||
wait(for: [expectation], timeout: 2.0) | ||
} | ||
|
||
func testHandleWithoutScheme() { | ||
let router = SimpleRouter(scheme: scheme) | ||
let expectation = self.expectation(description: "Should called handler four times") | ||
expectation.expectedFulfillmentCount = 4 | ||
router.register([ | ||
("static", { context in | ||
XCTAssertEqual(context.url, URL(string: "foobar://static")!) | ||
expectation.fulfill() | ||
return true | ||
}), | ||
("foo/bar", { context in | ||
XCTAssertEqual(context.parameter(for: "param0"), 123) | ||
XCTAssertEqual(context.url, URL(string: "foobar://foo/bar?param0=123")!) | ||
expectation.fulfill() | ||
return true | ||
}), | ||
(":keyword", { context in | ||
XCTAssertEqual(context.url, URL(string: "foobar://hoge")!) | ||
XCTAssertEqual(try? context.argument(for: "keyword"), "hoge") | ||
expectation.fulfill() | ||
return true | ||
}), | ||
("foo/:keyword/:keyword2", { context in | ||
XCTAssertEqual(context.url, URL(string: "foobar://foo/hoge/fuga")!) | ||
XCTAssertEqual(try? context.argument(for: "keyword"), "hoge") | ||
XCTAssertEqual(try? context.argument(for: "keyword2"), "fuga") | ||
expectation.fulfill() | ||
return true | ||
}), | ||
]) | ||
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://static")!)) | ||
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://foo/bar?param0=123")!)) | ||
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://hoge")!)) | ||
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://foo/hoge/fuga")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "foobar://spam/ham")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "notfoobar://static")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "static")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "foo/bar?param0=123")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "hoge")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "foo/hoge/fuga")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "spam/ham")!)) | ||
wait(for: [expectation], timeout: 2.0) | ||
} | ||
|
||
func testHandlerWithSamePatterns() { | ||
let router = SimpleRouter(scheme: schema) | ||
let router = SimpleRouter(scheme: scheme) | ||
let idExpectation = self.expectation(description: "Should called handler with ID") | ||
let keywordExpectation = self.expectation(description: "Should called handler with keyword") | ||
router.register([ | ||
|
@@ -86,11 +164,42 @@ final class RouterTest: XCTestCase { | |
]) | ||
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://foo/42")!)) | ||
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://foo/bar")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "foo/42")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "foo/bar")!)) | ||
wait(for: [idExpectation, keywordExpectation], timeout: 2.0) | ||
} | ||
|
||
func testHandlerWithSamePatternsWithoutScheme() { | ||
let router = SimpleRouter(scheme: scheme) | ||
let idExpectation = self.expectation(description: "Should called handler with ID") | ||
let keywordExpectation = self.expectation(description: "Should called handler with keyword") | ||
router.register([ | ||
("foo/:id", { context in | ||
guard let id: Int = try? context.argument(for: "id") else { | ||
return false | ||
} | ||
XCTAssertEqual(context.url, URL(string: "foobar://foo/42")!) | ||
XCTAssertEqual(id, 42) | ||
idExpectation.fulfill() | ||
return true | ||
}), | ||
("foo/:keyword", { context in | ||
let keyword: String = try! context.argument(for: "keyword") | ||
XCTAssertEqual(context.url, URL(string: "foobar://foo/bar")!) | ||
XCTAssertEqual(keyword, "bar") | ||
keywordExpectation.fulfill() | ||
return true | ||
}), | ||
]) | ||
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://foo/42")!)) | ||
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://foo/bar")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "foo/42")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "foo/bar")!)) | ||
wait(for: [idExpectation, keywordExpectation], timeout: 2.0) | ||
} | ||
|
||
func testHandleReturnsFalse() { | ||
let router = SimpleRouter(scheme: schema) | ||
let router = SimpleRouter(scheme: scheme) | ||
let expectation = self.expectation(description: "Should called handler twice") | ||
expectation.expectedFulfillmentCount = 2 | ||
router.register([ | ||
|
@@ -105,14 +214,35 @@ final class RouterTest: XCTestCase { | |
}), | ||
]) | ||
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://foo/bar")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "foo/bar")!)) | ||
wait(for: [expectation], timeout: 2.0) | ||
} | ||
|
||
func testHandleReturnsFalseWithoutScheme() { | ||
let router = SimpleRouter(scheme: scheme) | ||
let expectation = self.expectation(description: "Should called handler twice") | ||
expectation.expectedFulfillmentCount = 2 | ||
router.register([ | ||
("foo/bar", { _ in | ||
expectation.fulfill() | ||
return false | ||
}), | ||
("foo/:keyword", { context in | ||
XCTAssertEqual(try? context.argument(for: "keyword"), "bar") | ||
expectation.fulfill() | ||
return true | ||
}), | ||
]) | ||
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://foo/bar")!)) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "foo/bar")!)) | ||
wait(for: [expectation], timeout: 2.0) | ||
} | ||
|
||
func testWithUserInfo() { | ||
struct UserInfo { | ||
let value: Int | ||
} | ||
let router = Router<UserInfo>(scheme: schema) | ||
let router = Router<UserInfo>(scheme: scheme) | ||
var userInfo: UserInfo? = nil | ||
router.register([ | ||
("foobar://static", { context in | ||
|
@@ -122,6 +252,25 @@ final class RouterTest: XCTestCase { | |
}), | ||
]) | ||
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://static")!, userInfo: UserInfo(value: 42))) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "static")!, userInfo: UserInfo(value: 42))) | ||
XCTAssertEqual(userInfo?.value, 42) | ||
} | ||
|
||
func testWithUserInfoWithoutScheme() { | ||
struct UserInfo { | ||
let value: Int | ||
} | ||
let router = Router<UserInfo>(scheme: scheme) | ||
var userInfo: UserInfo? = nil | ||
router.register([ | ||
("static", { context in | ||
XCTAssertEqual(context.url, URL(string: "foobar://static")!) | ||
userInfo = context.userInfo | ||
return true | ||
}), | ||
]) | ||
XCTAssertTrue(router.openIfPossible(URL(string: "foobar://static")!, userInfo: UserInfo(value: 42))) | ||
XCTAssertFalse(router.openIfPossible(URL(string: "static")!, userInfo: UserInfo(value: 42))) | ||
XCTAssertEqual(userInfo?.value, 42) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we conceal this property?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, I guess that nobody use
Router.scheme
from external. 🤔There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so. I think it might be good to release API, once it becomes useful.