From 245efe7db986dd169a73e64e9fefcfa92d5e8216 Mon Sep 17 00:00:00 2001 From: Zak Patterson Date: Mon, 18 Feb 2019 12:59:27 -0500 Subject: [PATCH] Add ability to parse root site deployment path (#2) --- README.md | 2 +- .../main/scala/outwatch/router/Router.scala | 43 +++++++++++++++++-- project/Version.scala | 2 +- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1c16834..9df145c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Most of this code is adapted from [http4s](http4s.org)'s route parsing and path Add to library dependencies: ``` - "com.clovellytech" %%% "outwatch-router" % "0.0.5" + "com.clovellytech" %%% "outwatch-router" % "0.0.6" ``` See [documentation][doc-root] diff --git a/outwatch-router/src/main/scala/outwatch/router/Router.scala b/outwatch-router/src/main/scala/outwatch/router/Router.scala index fcdfadd..5f36c40 100644 --- a/outwatch-router/src/main/scala/outwatch/router/Router.scala +++ b/outwatch-router/src/main/scala/outwatch/router/Router.scala @@ -12,10 +12,19 @@ final case class Replace(path: Path) extends Action final case class RouterState[P](page: P) -class AppRouter[P](root: Path, f: Path => P) { +/** + * An AppRouter handles parsing of URLs and mapping to pages of the given type. + * @param siteRoot - The prefix part of a pathname, or the subpath at which your site is applied. + * Usually this is just Root, but your site might need a prefix as in /my_site/[parsed pathname] + * @param parent - The parent path at which this router is mounted. You can have routers contained in subroots of your site. + * @param f - a mapping function from a Path to a page P. + * @tparam P - Your page type, such as a sealed trait root type. + */ +class AppRouter[P](siteRoot: Path, parent: Path, f: Path => P) { + // Sync from the required page to the window.location def routerReducer(state: RouterState[P], action: Action): RouterState[P] = action match { case Replace(path) => - window.history.replaceState("", "", Path(root, path).toString) + window.history.pushState("", "", Path(siteRoot, Path(parent, path)).toString) state.copy(page = f(path)) case _ => state } @@ -39,5 +48,33 @@ object AppRouter{ create[P](Root, notFound)(f) def create[P](parent: Path, notFound: P)(f: PartialFunction[Path, P]): AppRouter[P] = - new AppRouter[P](parent, f.lift.andThen(_.getOrElse(notFound))) + new AppRouter[P](Root, parent, f.lift.andThen(_.getOrElse(notFound))) + + def createParseSiteRoot[P](notFound: P)(f: PartialFunction[Path, P]): AppRouter[P] = + createParseSiteRoot[P](Root, notFound)(f) + + /** + * Automatically determine what siteroot we're using, based on the current URL and expected parent. + * For example, your site could be deployed at /example/directory/, your router root path could be /names, + * and the current url could be /example/directory/names/alice. So given the call: + * createParseSubRoot[Page](Path("/names"), NotFound)(f), the router will work out that the window location + * prefix must be /example/directory/names, and will handle actions such as Replace(Path("/names/bob")) + * @param parent the parent for this router, another path perhaps managed by another router + * @param notFound - the default case page assignment + * @param f - a router function from Path to instances of your page type + * @tparam P - your page type + */ + def createParseSiteRoot[P](parent: Path, notFound: P)(f: PartialFunction[Path, P]): AppRouter[P] = { + val initUrl = window.location.pathname + // url is of form /sra/srb/src/pa/pb/pc... + // so just drop the parent part from the right of the url if it exists. + + val siteRoot = initUrl.lastIndexOf(parent.toString) match { + case x if x < 1 => Root + case x => Path(initUrl.substring(0, x)) + } + + val routerFun: Path => P = f.lift.andThen(_.getOrElse(notFound)) + new AppRouter[P](siteRoot, parent, routerFun) + } } diff --git a/project/Version.scala b/project/Version.scala index 8b7ea6e..8c8eaba 100644 --- a/project/Version.scala +++ b/project/Version.scala @@ -1,4 +1,4 @@ object Version{ - val version = "0.0.5" + val version = "0.0.6" val scalaVersion = "2.12.8" }