Skip to content

Commit

Permalink
Misc: Remove java Class, rely only on ClassTag
Browse files Browse the repository at this point in the history
  • Loading branch information
raquo committed Nov 9, 2024
1 parent 1544628 commit 0218c7e
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 56 deletions.
16 changes: 8 additions & 8 deletions js/src/test/scala/com/raquo/waypoint/BasePathSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,41 +39,41 @@ class BasePathSpec extends UnitSpec {
basePath = basePath
)

val homeRouteTotal: Route.Total[HomePage.type, Unit] = Route.staticTotal(
val homeRouteTotal: Route[HomePage.type, Unit] = Route.staticTotal(
HomePage,
pattern = root / endOfSegments,
basePath = basePath
)

val libraryRoute: Route.Total[LibraryPage, Int] = Route(
val libraryRoute: Route[LibraryPage, Int] = Route(
encode = _.libraryId,
decode = arg => LibraryPage(libraryId = arg),
pattern = root / "app" / "library" / segment[Int] / endOfSegments,
basePath = basePath
)

val textRoute: Route.Total[TextPage, String] = Route(
val textRoute: Route[TextPage, String] = Route(
encode = _.text,
decode = arg => TextPage(text = arg),
pattern = root / "app" / "test" / segment[String] / endOfSegments,
basePath = basePath
)

val noteRoute: Route.Total[NotePage, (Int, Int)] = Route(
val noteRoute: Route[NotePage, (Int, Int)] = Route(
encode = page => (page.libraryId, page.noteId),
decode = args => NotePage(libraryId = args._1, noteId = args._2, scrollPosition = 0),
pattern = root / "app" / "library" / segment[Int] / "note" / segment[Int] / endOfSegments,
basePath = basePath
)

val searchRoute: Route.Total[SearchPage, String] = Route.onlyQuery(
val searchRoute: Route[SearchPage, String] = Route.onlyQuery(
encode = page => page.query,
decode = arg => SearchPage(arg),
pattern = (root / "search" / endOfSegments) ? param[String]("query"),
basePath = basePath
)

val bigLegalRoute: Route.Total[BigLegalPage, FragmentPatternArgs[String, Unit, String]] = Route.withFragment(
val bigLegalRoute: Route[BigLegalPage, FragmentPatternArgs[String, Unit, String]] = Route.withFragment(
encode = page => FragmentPatternArgs(path = page.page, (), fragment = page.section),
decode = args => BigLegalPage(page = args.path, section = args.fragment),
pattern = (root / "legal" / segment[String] / endOfSegments) withFragment fragment[String],
Expand Down Expand Up @@ -274,7 +274,7 @@ class BasePathSpec extends UnitSpec {
urlForPage(DocsPage(NumPage(0))) shouldBe Some(s"$basePath/docs/zero/0")
urlForPage(DocsPage(NumPage(50))) shouldBe None
}

it("should not compile with non-singleton type for a staticTotal route") {
assertTypeError(
"""|Route.staticTotal(
Expand All @@ -285,7 +285,7 @@ class BasePathSpec extends UnitSpec {
|""".stripMargin
)
}

it("should not compile with non-singleton type ascription for a staticTotal route") {
assertTypeError(
"""|Route.staticTotal[AppPage](
Expand Down
25 changes: 11 additions & 14 deletions js/src/test/scala/com/raquo/waypoint/DynamicRouteSpec.scala
Original file line number Diff line number Diff line change
@@ -1,66 +1,63 @@
package com.raquo.waypoint

import com.raquo.waypoint.fixtures.{AppPage, UnitSpec}
import com.raquo.waypoint.fixtures.AppPage._
import com.raquo.airstream.ownership.Owner
import com.raquo.laminar.api._
import com.raquo.waypoint.fixtures.AppPage.DocsSection._
import com.raquo.waypoint.fixtures.AppPage.LibraryPage
import org.scalactic
import org.scalatest.Assertion
import com.raquo.waypoint.fixtures.AppPage._
import com.raquo.waypoint.fixtures.{AppPage, UnitSpec}
import upickle.default._

import scala.util.{Success, Try}
import scala.util.Try

class DynamicRouteSpec extends UnitSpec {

private val testOwner = new Owner {}

val origin = "http://example.com"

val libraryRoute: Route.Total[LibraryPage, Int] = Route(
val libraryRoute: Route[LibraryPage, Int] = Route(
encode = _.libraryId,
decode = arg => LibraryPage(libraryId = arg),
pattern = root / "app" / "library" / segment[Int] / endOfSegments
)

val textRoute: Route.Total[TextPage, String] = Route(
val textRoute: Route[TextPage, String] = Route(
encode = _.text,
decode = arg => TextPage(text = arg),
pattern = root / "app" / "test" / segment[String] / endOfSegments
)

val noteRoute: Route.Total[NotePage, (Int, Int)] = Route(
val noteRoute: Route[NotePage, (Int, Int)] = Route(
encode = page => (page.libraryId, page.noteId),
decode = args => NotePage(libraryId = args._1, noteId = args._2, scrollPosition = 0),
pattern = root / "app" / "library" / segment[Int] / "note" / segment[Int] / endOfSegments
)

val searchRoute: Route.Total[SearchPage, String] = Route.onlyQuery(
val searchRoute: Route[SearchPage, String] = Route.onlyQuery(
encode = page => page.query,
decode = arg => SearchPage(arg),
pattern = (root / "search" / endOfSegments) ? param[String]("query")
)

val workspaceSearchRoute: Route.Total[WorkspaceSearchPage, PatternArgs[String, String]] = Route.withQuery(
val workspaceSearchRoute: Route[WorkspaceSearchPage, PatternArgs[String, String]] = Route.withQuery(
encode = page => PatternArgs(page.workspaceId, page.query),
decode = args => WorkspaceSearchPage(workspaceId = args.path, query = args.params),
pattern = (root / "workspace" / segment[String] / endOfSegments) ? param[String]("query")
)

val legalRoute: Route.Total[LegalPage, String] = Route.onlyFragment(
val legalRoute: Route[LegalPage, String] = Route.onlyFragment(
encode = page => page.section,
decode = arg => LegalPage(arg),
pattern = (root / "legal" / endOfSegments) withFragment fragment[String]
)

val bigLegalRoute: Route.Total[BigLegalPage, FragmentPatternArgs[String, Unit, String]] = Route.withFragment(
val bigLegalRoute: Route[BigLegalPage, FragmentPatternArgs[String, Unit, String]] = Route.withFragment(
encode = page => FragmentPatternArgs(path = page.page, (), fragment = page.section),
decode = args => BigLegalPage(page = args.path, section = args.fragment),
pattern = (root / "legal" / segment[String] / endOfSegments) withFragment fragment[String]
)

val hugeLegalRoute: Route.Total[HugeLegalPage, FragmentPatternArgs[String, Int, String]] = Route.withQueryAndFragment(
val hugeLegalRoute: Route[HugeLegalPage, FragmentPatternArgs[String, Int, String]] = Route.withQueryAndFragment(
encode = page => FragmentPatternArgs(path = page.page, query = page.version, fragment = page.section),
decode = args => HugeLegalPage(page = args.path, version = args.query, section = args.fragment),
pattern = (root / "legal" / segment[String] / endOfSegments) ? param[Int]("version") withFragment fragment[String]
Expand Down
57 changes: 23 additions & 34 deletions shared/src/main/scala/com/raquo/waypoint/Route.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ sealed abstract class Route[Page, Args] private[waypoint](
}

/** @return None if the [[Route]] is partial and the given object does not match. */
def argsFromPage(page: Page): Option[Args] = encode(page)
def argsFromPage(page: Page): Option[Args] = encodeOpt(page)

/** @return None if the [[Route]] is partial and the given object does not match. */
def relativeUrlForPage[P >: Page](page: P): Option[String] =
encode(page).map(relativeUrlForArgs)
encodeOpt(page).map(relativeUrlForArgs)

def relativeUrlForArgs(args: Args): String =
basePath + createRelativeUrl(args)
Expand All @@ -47,7 +47,7 @@ sealed abstract class Route[Page, Args] private[waypoint](
val originMatches = Utils.absoluteUrlMatchesOrigin(origin, url)
val urlToMatch = if (originMatches) url.substring(origin.length) else url
// @TODO[API] We evaluate the page unconditionally, as that will consistently throw in case of malformed URL
val maybePage = matchRelativeUrl(urlToMatch).flatMap(decode) // This just ignores the origin present in the url
val maybePage = matchRelativeUrl(urlToMatch).flatMap(decodeOpt) // This just ignores the origin present in the url
if (originMatches) {
maybePage
} else {
Expand All @@ -68,21 +68,21 @@ sealed abstract class Route[Page, Args] private[waypoint](
}
if (url.startsWith(basePath)) {
val urlWithoutBasePath = url.substring(basePath.length)
matchRelativeUrl(origin + urlWithoutBasePath).flatMap(decode)
matchRelativeUrl(origin + urlWithoutBasePath).flatMap(decodeOpt)
} else if (Utils.basePathHasEmptyFragment(basePath) && url == Utils.basePathWithoutFragment(basePath)) {
matchRelativeUrl(origin).flatMap(decode)
matchRelativeUrl(origin).flatMap(decodeOpt)
} else {
None
}
}

private def encode(page: Any): Option[Args] = {
private def encodeOpt(page: Any): Option[Args] = {
matchEncodePF
.andThen(Some(_))
.applyOrElse(page, (_: Any) => None)
}

private def decode(args: Args): Option[Page] = {
private def decodeOpt(args: Args): Option[Page] = {
decodePF
.andThen[Option[Page]](Some(_))
.applyOrElse(args, (_: Args) => None)
Expand Down Expand Up @@ -119,7 +119,7 @@ object Route {
*/
class Total[Page, Args] private[waypoint](
encode: Page => Args,
decode: Args => Page,
decode: Args => Page, // #TODO[unused] Should we remove it? Or expose it?
matchEncodePF: PartialFunction[Any, Args],
decodePF: PartialFunction[Args, Page],
createRelativeUrl: Args => String,
Expand All @@ -135,16 +135,15 @@ object Route {
relativeUrlForArgs(encode(page))
}
object Total {
private[waypoint] def apply[Page, Args](
private[waypoint] def apply[Page: ClassTag, Args](
encode: Page => Args,
decode: Args => Page,
createRelativeUrl: Args => String,
matchRelativeUrl: String => Option[Args],
basePath: String,
klass: Class[Page]
basePath: String
): Total[Page, Args] = new Total(
encode, decode,
matchEncodePF = { case p if klass.isInstance(p) => encode(klass.cast(p)) },
matchEncodePF = { case p: Page => encode(p) },
decodePF = { case args => decode(args) },
createRelativeUrl = createRelativeUrl,
matchRelativeUrl = matchRelativeUrl,
Expand All @@ -169,8 +168,7 @@ object Route {
encode, decode,
createRelativeUrl = args => "/" + pattern.createPath(args),
matchRelativeUrl = relativeUrl => pattern.matchRawUrl(relativeUrl).toOption,
basePath = basePath,
klass = classFromClassTag
basePath = basePath
)
}

Expand Down Expand Up @@ -210,8 +208,7 @@ object Route {
encode, decode,
createRelativeUrl = args => "/" + pattern.createUrlString(path = (), params = args),
matchRelativeUrl = relativeUrl => pattern.matchRawUrl(relativeUrl).toOption.map(_.params),
basePath = basePath,
klass = classFromClassTag
basePath = basePath
)
}

Expand Down Expand Up @@ -250,8 +247,7 @@ object Route {
encode, decode,
createRelativeUrl = args => "/" + pattern.createUrlString(path = args.path, params = args.params),
matchRelativeUrl = relativeUrl => pattern.matchRawUrl(relativeUrl).toOption,
basePath = basePath,
klass = classFromClassTag
basePath = basePath
)
}

Expand Down Expand Up @@ -290,8 +286,7 @@ object Route {
encode, decode,
createRelativeUrl = args => "/" + pattern.fragmentOnly.createPart(args),
matchRelativeUrl = relativeUrl => pattern.matchRawUrl(relativeUrl).toOption.map(_.fragment),
basePath = basePath,
klass = classFromClassTag
basePath = basePath
)
}

Expand Down Expand Up @@ -333,8 +328,7 @@ object Route {
"/" + pattern.createPart(patternArgs)
},
matchRelativeUrl = relativeUrl => pattern.matchRawUrl(relativeUrl).toOption,
basePath = basePath,
klass = classFromClassTag
basePath = basePath
)
}

Expand Down Expand Up @@ -379,8 +373,7 @@ object Route {
"/" + pattern.createPart(patternArgs)
},
matchRelativeUrl = relativeUrl => pattern.matchRawUrl(relativeUrl).toOption,
basePath = basePath,
klass = classFromClassTag
basePath = basePath
)
}

Expand Down Expand Up @@ -425,8 +418,7 @@ object Route {
"/" + pattern.createPart(patternArgs)
},
matchRelativeUrl = relativeUrl => pattern.matchRawUrl(relativeUrl).toOption,
basePath = basePath,
klass = classFromClassTag
basePath = basePath
)
}

Expand Down Expand Up @@ -478,24 +470,21 @@ object Route {
/** Create a route for a static page that does not encode any data in the URL.
*
* This only allows using singleton types, like `object Foo`.
*
* @see [[ValueOf]]
*
* @see [[ValueOf]] - evidence that `Page` is a singleton type
* */
def staticTotal[Page](
def staticTotal[Page: ValueOf: ClassTag](
staticPage: Page,
pattern: PathSegment[Unit, DummyError],
basePath: String = ""
)(implicit valueOf: ValueOf[Page]): Total[Page, Unit] = {
): Total[Page, Unit] = {
Total[Page, Unit](
encode = _ => (),
decode = _ => staticPage,
createRelativeUrl = args => "/" + pattern.createPath(args),
matchRelativeUrl = relativeUrl => pattern.matchRawUrl(relativeUrl).toOption,
basePath = basePath,
klass = staticPage.getClass.asInstanceOf[Class[Page]]
basePath = basePath
)
}

private def classFromClassTag[A](implicit ct: ClassTag[A]): Class[A] =
ct.runtimeClass.asInstanceOf[Class[A]]
}

0 comments on commit 0218c7e

Please sign in to comment.