Zak Patterson
5 years ago
13 changed files with 706 additions and 0 deletions
-
142.gitignore
-
23.scalafmt.conf
-
33.travis.yml
-
8README.md
-
77build.sbt
-
43docs/Intro.md
-
220outwatch-router/src/main/scala/outwatch/router/Path.scala
-
9outwatch-router/src/main/scala/outwatch/router/State.scala
-
84project/Dependencies.scala
-
58project/Options.scala
-
4project/Version.scala
-
1project/build.properties
-
4project/plugins.sbt
@ -0,0 +1,142 @@ |
|||||
|
|
||||
|
# Created by https://www.gitignore.io/api/sbt,scala,bloop,metals,intellij,sublimetext |
||||
|
# Edit at https://www.gitignore.io/?templates=sbt,scala,bloop,metals,intellij,sublimetext |
||||
|
|
||||
|
### Bloop ### |
||||
|
.bloop/ |
||||
|
|
||||
|
### Intellij ### |
||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm |
||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 |
||||
|
|
||||
|
# User-specific stuff |
||||
|
.idea/**/workspace.xml |
||||
|
.idea/**/tasks.xml |
||||
|
.idea/**/usage.statistics.xml |
||||
|
.idea/**/dictionaries |
||||
|
.idea/**/shelf |
||||
|
|
||||
|
# Generated files |
||||
|
.idea/**/contentModel.xml |
||||
|
|
||||
|
# Sensitive or high-churn files |
||||
|
.idea/**/dataSources/ |
||||
|
.idea/**/dataSources.ids |
||||
|
.idea/**/dataSources.local.xml |
||||
|
.idea/**/sqlDataSources.xml |
||||
|
.idea/**/dynamic.xml |
||||
|
.idea/**/uiDesigner.xml |
||||
|
.idea/**/dbnavigator.xml |
||||
|
|
||||
|
# Gradle |
||||
|
.idea/**/gradle.xml |
||||
|
.idea/**/libraries |
||||
|
|
||||
|
# Gradle and Maven with auto-import |
||||
|
# When using Gradle or Maven with auto-import, you should exclude module files, |
||||
|
# since they will be recreated, and may cause churn. Uncomment if using |
||||
|
# auto-import. |
||||
|
# .idea/modules.xml |
||||
|
# .idea/*.iml |
||||
|
# .idea/modules |
||||
|
|
||||
|
# CMake |
||||
|
cmake-build-*/ |
||||
|
|
||||
|
# Mongo Explorer plugin |
||||
|
.idea/**/mongoSettings.xml |
||||
|
|
||||
|
# File-based project format |
||||
|
*.iws |
||||
|
|
||||
|
# IntelliJ |
||||
|
out/ |
||||
|
|
||||
|
# mpeltonen/sbt-idea plugin |
||||
|
.idea_modules/ |
||||
|
|
||||
|
# JIRA plugin |
||||
|
atlassian-ide-plugin.xml |
||||
|
|
||||
|
# Cursive Clojure plugin |
||||
|
.idea/replstate.xml |
||||
|
|
||||
|
# Crashlytics plugin (for Android Studio and IntelliJ) |
||||
|
com_crashlytics_export_strings.xml |
||||
|
crashlytics.properties |
||||
|
crashlytics-build.properties |
||||
|
fabric.properties |
||||
|
|
||||
|
# Editor-based Rest Client |
||||
|
.idea/httpRequests |
||||
|
|
||||
|
# Android studio 3.1+ serialized cache file |
||||
|
.idea/caches/build_file_checksums.ser |
||||
|
|
||||
|
### Intellij Patch ### |
||||
|
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 |
||||
|
|
||||
|
# *.iml |
||||
|
# modules.xml |
||||
|
# .idea/misc.xml |
||||
|
# *.ipr |
||||
|
|
||||
|
# Sonarlint plugin |
||||
|
.idea/sonarlint |
||||
|
|
||||
|
### Metals ### |
||||
|
.metals/ |
||||
|
|
||||
|
### SBT ### |
||||
|
# Simple Build Tool |
||||
|
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control |
||||
|
|
||||
|
dist/* |
||||
|
target/ |
||||
|
lib_managed/ |
||||
|
src_managed/ |
||||
|
project/boot/ |
||||
|
project/plugins/project/ |
||||
|
.history |
||||
|
.cache |
||||
|
.lib/ |
||||
|
|
||||
|
### Scala ### |
||||
|
*.class |
||||
|
*.log |
||||
|
|
||||
|
### SublimeText ### |
||||
|
# Cache files for Sublime Text |
||||
|
*.tmlanguage.cache |
||||
|
*.tmPreferences.cache |
||||
|
*.stTheme.cache |
||||
|
|
||||
|
# Workspace files are user-specific |
||||
|
*.sublime-workspace |
||||
|
|
||||
|
# Project files should be checked into the repository, unless a significant |
||||
|
# proportion of contributors will probably not be using Sublime Text |
||||
|
# *.sublime-project |
||||
|
|
||||
|
# SFTP configuration file |
||||
|
sftp-config.json |
||||
|
|
||||
|
# Package control specific files |
||||
|
Package Control.last-run |
||||
|
Package Control.ca-list |
||||
|
Package Control.ca-bundle |
||||
|
Package Control.system-ca-bundle |
||||
|
Package Control.cache/ |
||||
|
Package Control.ca-certs/ |
||||
|
Package Control.merged-ca-bundle |
||||
|
Package Control.user-ca-bundle |
||||
|
oscrypto-ca-bundle.crt |
||||
|
bh_unicode_properties.cache |
||||
|
|
||||
|
# Sublime-github package stores a github token in this file |
||||
|
# https://packagecontrol.io/packages/sublime-github |
||||
|
GitHub.sublime-settings |
||||
|
|
||||
|
# End of https://www.gitignore.io/api/sbt,scala,bloop,metals,intellij,sublimetext |
||||
|
|
||||
|
/.idea/ |
@ -0,0 +1,23 @@ |
|||||
|
style = default |
||||
|
|
||||
|
maxColumn = 100 |
||||
|
|
||||
|
// Vertical alignment is pretty, but leads to bigger diffs |
||||
|
align = none |
||||
|
|
||||
|
// Insist on trailing commas for better difs in element construction |
||||
|
trailingCommas = always |
||||
|
danglingParentheses = true |
||||
|
|
||||
|
rewrite.rules = [ |
||||
|
AvoidInfix |
||||
|
RedundantBraces |
||||
|
RedundantParens |
||||
|
AsciiSortImports |
||||
|
PreferCurlyFors |
||||
|
] |
||||
|
|
||||
|
project.excludeFilters = [ |
||||
|
"scalafix-inputs", |
||||
|
"scalafix-outputs" |
||||
|
] |
@ -0,0 +1,33 @@ |
|||||
|
language: scala |
||||
|
|
||||
|
install: |
||||
|
- rvm use 2.3.0 --install --fuzzy |
||||
|
- gem update --system |
||||
|
- gem install sass |
||||
|
- gem install jekyll -v 3.2.1 |
||||
|
|
||||
|
scala: |
||||
|
- 2.12.8 |
||||
|
|
||||
|
jobs: |
||||
|
include: |
||||
|
- stage: verify |
||||
|
|
||||
|
script: |
||||
|
- sbt ++$TRAVIS_SCALA_VERSION todomvc/fullOptJS::webpack |
||||
|
- sbt docs/mdoc |
||||
|
- sbt docs/makeMicrosite |
||||
|
- mkdir -p router-docs/site/todomvc |
||||
|
- cp -R router-docs/target/site/* ./router-docs/site/ |
||||
|
- cp ./router-docs/site/Readme.html ./router-docs/site/index.html |
||||
|
|
||||
|
deploy: |
||||
|
provider: pages |
||||
|
skip-cleanup: true |
||||
|
github-token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable |
||||
|
keep-history: true |
||||
|
local-dir: router-docs/site |
||||
|
target-branch: gh-pages |
||||
|
on: |
||||
|
branch: master |
||||
|
|
@ -0,0 +1,8 @@ |
|||||
|
outwatch-router |
||||
|
=== |
||||
|
|
||||
|
Easy routing for [outwatch](https://outwatch.github.io) on scala.js |
||||
|
|
||||
|
Most of this code is adapted from [http4s](http4s.org)'s route parsing and path pattern matching. |
||||
|
|
||||
|
See [documentation](https://clovellytech.github.io/outwatch-router/index.html) |
@ -0,0 +1,77 @@ |
|||||
|
import dependencies._ |
||||
|
|
||||
|
cancelable in Global := true |
||||
|
|
||||
|
val commonSettings = Seq( |
||||
|
organization := "com.clovellytech", |
||||
|
version := Version.version, |
||||
|
scalaVersion := Version.scalaVersion, |
||||
|
resolvers ++= addResolvers, |
||||
|
scalacOptions ++= options.scalac, |
||||
|
scalacOptions in (Compile, console) := options.scalacConsole, |
||||
|
updateOptions := updateOptions.value.withLatestSnapshots(false) |
||||
|
) ++ compilerPlugins |
||||
|
|
||||
|
val withTests : String = "compile->compile;test->test" |
||||
|
val testOnly : String = "test->test" |
||||
|
|
||||
|
lazy val docs = (project in file("./router-docs")) |
||||
|
.settings(name := "outwatch-router-docs") |
||||
|
.enablePlugins(MdocPlugin) |
||||
|
.settings(commonSettings) |
||||
|
.dependsOn(router) |
||||
|
|
||||
|
lazy val copyFastOptJS = TaskKey[Unit]("copyFastOptJS", "Copy javascript files to target directory") |
||||
|
|
||||
|
lazy val router = (project in file("./outwatch-router")) |
||||
|
.settings(name := "outwatch-router") |
||||
|
.enablePlugins(ScalaJSPlugin) |
||||
|
.enablePlugins(ScalaJSBundlerPlugin) |
||||
|
.settings(commonSettings) |
||||
|
.settings( |
||||
|
scalaJSModuleKind := ModuleKind.CommonJSModule, |
||||
|
scalacOptions += "-P:scalajs:sjsDefinedByDefault", |
||||
|
useYarn := true, // makes scalajs-bundler use yarn instead of npm |
||||
|
jsEnv in Test := new org.scalajs.jsenv.jsdomnodejs.JSDOMNodeJSEnv, |
||||
|
scalaJSUseMainModuleInitializer := true, |
||||
|
scalaJSModuleKind := ModuleKind.CommonJSModule, // configure Scala.js to emit a JavaScript module instead of a top-level script |
||||
|
version in webpack := "4.16.1", |
||||
|
version in startWebpackDevServer := "3.1.4", |
||||
|
webpackDevServerExtraArgs := Seq("--progress", "--color"), |
||||
|
webpackConfigFile in fastOptJS := Some(baseDirectory.value / "webpack.config.dev.js"), |
||||
|
// https://scalacenter.github.io/scalajs-bundler/cookbook.html#performance |
||||
|
webpackBundlingMode in fastOptJS := BundlingMode.LibraryOnly(), |
||||
|
resolvers += "jitpack" at "https://jitpack.io", |
||||
|
libraryDependencies ++= Seq( |
||||
|
"io.github.outwatch" % "outwatch" % "ea240c6d04", |
||||
|
"org.http4s" %% "parboiled" % "1.0.0", |
||||
|
"org.scalatest" %%% "scalatest" % "3.0.5" % Test |
||||
|
), |
||||
|
copyFastOptJS := { |
||||
|
val inDir = (crossTarget in (Compile, fastOptJS)).value |
||||
|
val outDir = (crossTarget in (Compile, fastOptJS)).value / "dev" |
||||
|
val files = Seq("outwatch-router-fastopt-loader.js", "outwatch-router-frontend-fastopt.js", "outwatch-router-frontend-fastopt.js.map") map { p => (inDir / p, outDir / p) } |
||||
|
IO.copy(files, overwrite = true, preserveLastModified = true, preserveExecutable = true) |
||||
|
}, |
||||
|
// hot reloading configuration: |
||||
|
// https://github.com/scalacenter/scalajs-bundler/issues/180 |
||||
|
addCommandAlias("dev", "; compile; fastOptJS::startWebpackDevServer; devwatch; fastOptJS::stopWebpackDevServer"), |
||||
|
addCommandAlias("devwatch", "~; fastOptJS; copyFastOptJS") |
||||
|
) |
||||
|
|
||||
|
lazy val exampleApp = (project in file("router-example")) |
||||
|
.settings(name := "outwatch-example") |
||||
|
.settings(commonSettings) |
||||
|
.dependsOn(router) |
||||
|
|
||||
|
lazy val root = (project in file(".")) |
||||
|
.settings(name := "outwatch-router-root") |
||||
|
.settings(commonSettings) |
||||
|
.settings( |
||||
|
skip in publish := true, |
||||
|
aggregate in reStart := false, |
||||
|
) |
||||
|
.dependsOn(router) |
||||
|
.aggregate(router) |
||||
|
|
||||
|
|
@ -0,0 +1,43 @@ |
|||||
|
Outwatch Router |
||||
|
=== |
||||
|
|
||||
|
```scala mdoc |
||||
|
import cats._ |
||||
|
import cats.implicits._ |
||||
|
import cats.Applicative |
||||
|
import cats.data.Kleisli |
||||
|
import outwatch.router._, Router._ |
||||
|
|
||||
|
sealed abstract class Page |
||||
|
case class RootPage() extends Page |
||||
|
case class Login() extends Page |
||||
|
case class Register() extends Page |
||||
|
case class Profile(userId: String) extends Page |
||||
|
case class NotFound() extends Page |
||||
|
|
||||
|
object Page{ |
||||
|
def root: Page = RootPage() |
||||
|
def login: Page = Login() |
||||
|
def register: Page = Register() |
||||
|
def profile(userId: String): Page = Profile(userId) |
||||
|
def notFound: Page = NotFound() |
||||
|
} |
||||
|
|
||||
|
def routes[F[_]: Applicative]: AppRouter[F, Page] = Kleisli[F, Path, Page] { |
||||
|
case Root => Page.root.pure[F] |
||||
|
case Root / "login" => Page.login.pure[F] |
||||
|
case Root / "register" => Page.register.pure[F] |
||||
|
case Root / "profile" / userId => Page.profile(userId).pure[F] |
||||
|
case _ => Page.notFound.pure[F] |
||||
|
} |
||||
|
|
||||
|
val router = routes[Id] |
||||
|
|
||||
|
router.run(Root) |
||||
|
router.run(Root / "login") |
||||
|
router.run(Root / "profile" / "saopa98f") |
||||
|
|
||||
|
router.run(Path("/profile/asd")) |
||||
|
router.run(Path("/apsinoasn")) |
||||
|
``` |
||||
|
|
@ -0,0 +1,220 @@ |
|||||
|
// Completely copied from org.http4s.dsl.impl |
||||
|
|
||||
|
package outwatch.router |
||||
|
|
||||
|
import cats.implicits._ |
||||
|
import java.nio.{ByteBuffer, CharBuffer} |
||||
|
import java.nio.charset.Charset |
||||
|
import java.nio.charset.StandardCharsets.UTF_8 |
||||
|
|
||||
|
/** Base class for path extractors. */ |
||||
|
trait Path { |
||||
|
def /(child: String) = new /(this, child) |
||||
|
def toList: List[String] |
||||
|
def parent: Path |
||||
|
def lastOption: Option[String] |
||||
|
def startsWith(other: Path): Boolean |
||||
|
} |
||||
|
|
||||
|
object Path { |
||||
|
|
||||
|
/** Constructs a path from a single string by splitting on the `'/'` |
||||
|
* character. |
||||
|
* |
||||
|
* Leading slashes do not create an empty path segment. This is to |
||||
|
* reflect that there is no distinction between a request to |
||||
|
* `http://www.example.com` from `http://www.example.com/`. |
||||
|
* |
||||
|
* Trailing slashes result in a path with an empty final segment, |
||||
|
* unless the path is `"/"`, which is `Root`. |
||||
|
* |
||||
|
* Segments are URL decoded. |
||||
|
* |
||||
|
* {{{ |
||||
|
* scala> Path("").toList |
||||
|
* res0: List[String] = List() |
||||
|
* scala> Path("/").toList |
||||
|
* res1: List[String] = List() |
||||
|
* scala> Path("a").toList |
||||
|
* res2: List[String] = List(a) |
||||
|
* scala> Path("/a").toList |
||||
|
* res3: List[String] = List(a) |
||||
|
* scala> Path("/a/").toList |
||||
|
* res4: List[String] = List(a, "") |
||||
|
* scala> Path("//a").toList |
||||
|
* res5: List[String] = List("", a) |
||||
|
* scala> Path("/%2F").toList |
||||
|
* res0: List[String] = List(/) |
||||
|
* }}} |
||||
|
*/ |
||||
|
def apply(str: String): Path = |
||||
|
if (str == "" || str == "/") |
||||
|
Root |
||||
|
else { |
||||
|
val segments = str.split("/", -1) |
||||
|
// .head is safe because split always returns non-empty array |
||||
|
val segments0 = if (segments.head == "") segments.drop(1) else segments |
||||
|
segments0.foldLeft(Root: Path)((path, seg) => path / UrlCodingUtils.urlDecode(seg)) |
||||
|
} |
||||
|
|
||||
|
def apply(first: String, rest: String*): Path = |
||||
|
rest.foldLeft(Root / first)(_ / _) |
||||
|
|
||||
|
def apply(list: List[String]): Path = |
||||
|
list.foldLeft(Root: Path)(_ / _) |
||||
|
|
||||
|
def unapplySeq(path: Path): Some[List[String]] = |
||||
|
Some(path.toList) |
||||
|
// |
||||
|
// def unapplySeq[F[_]](request: Request[F]): Some[List[String]] = |
||||
|
// Some(Path(request.pathInfo).toList) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
final case class /(parent: Path, child: String) extends Path { |
||||
|
lazy val toList: List[String] = parent.toList ++ List(child) |
||||
|
|
||||
|
def lastOption: Some[String] = Some(child) |
||||
|
|
||||
|
lazy val asString: String = s"$parent/${UrlCodingUtils.pathEncode(child)}" |
||||
|
override def toString: String = asString |
||||
|
def startsWith(other: Path): Boolean = { |
||||
|
val components = other.toList |
||||
|
toList.take(components.length) === components |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Root extractor: |
||||
|
* {{{ |
||||
|
* Path("/") match { |
||||
|
* case Root => ... |
||||
|
* } |
||||
|
* }}} |
||||
|
*/ |
||||
|
case object Root extends Path { |
||||
|
def toList: List[String] = Nil |
||||
|
def parent: Path = this |
||||
|
def lastOption: None.type = None |
||||
|
override def toString = "" |
||||
|
def startsWith(other: Path): Boolean = other == Root |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private[router] object UrlCodingUtils { |
||||
|
|
||||
|
private val lower = ('a' to 'z').toSet |
||||
|
private val upper = ('A' to 'Z').toSet |
||||
|
private val num = ('0' to '9').toSet |
||||
|
val Unreserved: Set[Char] = lower ++ upper ++ num ++ "-_.~" |
||||
|
|
||||
|
private val toSkip : Set[Char] = Unreserved ++ "!$&'()*+,;=:/?@" |
||||
|
|
||||
|
private val HexUpperCaseChars: Array[Char] = ('A' to 'F').toArray |
||||
|
/** |
||||
|
* Percent-encodes a string. Depending on the parameters, this method is |
||||
|
* appropriate for URI or URL form encoding. Any resulting percent-encodings |
||||
|
* are normalized to uppercase. |
||||
|
* |
||||
|
* @param toEncode the string to encode |
||||
|
* @param charset the charset to use for characters that are percent encoded |
||||
|
* @param spaceIsPlus if space is not skipped, determines whether it will be |
||||
|
* rendreed as a `"+"` or a percent-encoding according to `charset`. |
||||
|
* @param toSkip a predicate of characters exempt from encoding. In typical |
||||
|
* use, this is composed of all Unreserved URI characters and sometimes a |
||||
|
* subset of Reserved URI characters. |
||||
|
*/ |
||||
|
def urlEncode( |
||||
|
toEncode: String, |
||||
|
charset: Charset = UTF_8, |
||||
|
spaceIsPlus: Boolean = false, |
||||
|
toSkip: Char => Boolean = toSkip): String = { |
||||
|
val in = charset.encode(toEncode) |
||||
|
val out = CharBuffer.allocate((in.remaining() * 3).toInt) |
||||
|
while (in.hasRemaining) { |
||||
|
val c = in.get().toChar |
||||
|
if (toSkip(c)) { |
||||
|
out.put(c) |
||||
|
} else if (c == ' ' && spaceIsPlus) { |
||||
|
out.put('+') |
||||
|
} else { |
||||
|
out.put('%') |
||||
|
out.put(HexUpperCaseChars((c >> 4) & 0xF)) |
||||
|
out.put(HexUpperCaseChars(c & 0xF)) |
||||
|
} |
||||
|
} |
||||
|
out.flip() |
||||
|
out.toString |
||||
|
} |
||||
|
|
||||
|
private val SkipEncodeInPath = |
||||
|
Unreserved ++ ":@!$&'()*+,;=" |
||||
|
|
||||
|
def pathEncode(s: String, charset: Charset = UTF_8): String = |
||||
|
UrlCodingUtils.urlEncode(s, charset, false, SkipEncodeInPath) |
||||
|
|
||||
|
/** |
||||
|
* Percent-decodes a string. |
||||
|
* |
||||
|
* @param toDecode the string to decode |
||||
|
* @param charset the charset of percent-encoded characters |
||||
|
* @param plusIsSpace true if `'+'` is to be interpreted as a `' '` |
||||
|
* @param toSkip a predicate of characters whose percent-encoded form |
||||
|
* is left percent-encoded. Almost certainly should be left empty. |
||||
|
*/ |
||||
|
def urlDecode( |
||||
|
toDecode: String, |
||||
|
charset: Charset = UTF_8, |
||||
|
plusIsSpace: Boolean = false, |
||||
|
toSkip: Char => Boolean = Function.const(false)): String = { |
||||
|
val in = CharBuffer.wrap(toDecode) |
||||
|
// reserve enough space for 3-byte UTF-8 characters. 4-byte characters are represented |
||||
|
// as surrogate pairs of characters, and will get a luxurious 6 bytes of space. |
||||
|
val out = ByteBuffer.allocate(in.remaining() * 3) |
||||
|
while (in.hasRemaining) { |
||||
|
val mark = in.position() |
||||
|
val c = in.get() |
||||
|
if (c == '%') { |
||||
|
if (in.remaining() >= 2) { |
||||
|
val xc = in.get() |
||||
|
val yc = in.get() |
||||
|
// scalastyle:off magic.number |
||||
|
val x = Character.digit(xc, 0x10) |
||||
|
val y = Character.digit(yc, 0x10) |
||||
|
// scalastyle:on magic.number |
||||
|
if (x != -1 && y != -1) { |
||||
|
val oo = (x << 4) + y |
||||
|
if (!toSkip(oo.toChar)) { |
||||
|
out.put(oo.toByte) |
||||
|
} else { |
||||
|
out.put('%'.toByte) |
||||
|
out.put(xc.toByte) |
||||
|
out.put(yc.toByte) |
||||
|
} |
||||
|
} else { |
||||
|
out.put('%'.toByte) |
||||
|
in.position(mark + 1) |
||||
|
} |
||||
|
} else { |
||||
|
// This is an invalid encoding. Fail gracefully by treating the '%' as |
||||
|
// a literal. |
||||
|
out.put(c.toByte) |
||||
|
while (in.hasRemaining) out.put(in.get().toByte) |
||||
|
} |
||||
|
} else if (c == '+' && plusIsSpace) { |
||||
|
out.put(' '.toByte) |
||||
|
} else { |
||||
|
// normally `out.put(c.toByte)` would be enough since the url is %-encoded, |
||||
|
// however there are cases where a string can be partially decoded |
||||
|
// so we have to make sure the non us-ascii chars get preserved properly. |
||||
|
if (this.toSkip(c)) { |
||||
|
out.put(c.toByte) |
||||
|
} else { |
||||
|
out.put(charset.encode(String.valueOf(c))) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
out.flip() |
||||
|
charset.decode(out).toString |
||||
|
} |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
package outwatch.router |
||||
|
|
||||
|
import cats.data.Kleisli |
||||
|
|
||||
|
object Router { |
||||
|
type AppRouter[F[_], A] = Kleisli[F, Path, A] |
||||
|
val AppRouter = Kleisli |
||||
|
|
||||
|
} |
@ -0,0 +1,84 @@ |
|||||
|
import sbt._ |
||||
|
import sbt.librarymanagement.DependencyBuilders |
||||
|
import org.portablescala.sbtplatformdeps._ |
||||
|
|
||||
|
object dependencies { |
||||
|
val addResolvers = Seq( |
||||
|
"52north for postgis" at "http://52north.org/maven/repo/releases/", |
||||
|
Resolver.sonatypeRepo("releases"), |
||||
|
Resolver.sonatypeRepo("snapshots") |
||||
|
) |
||||
|
|
||||
|
val compilerPlugins = Seq( |
||||
|
addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.9"), |
||||
|
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.1" cross CrossVersion.full) |
||||
|
) |
||||
|
|
||||
|
val bcrypt = "3.1" |
||||
|
val cats = "1.4.0" |
||||
|
val catsMtl = "0.4.0" |
||||
|
val catsEffect = "1.2.0" |
||||
|
val circe = "0.11.0" |
||||
|
val circeConfig = "0.6.1" |
||||
|
val doobie = "0.6.0" |
||||
|
val flyway = "5.2.4" |
||||
|
val fs2 = "1.0.2" |
||||
|
val h4sm = "0.0.17" |
||||
|
val http4s = "0.20.0-M5" |
||||
|
val logback = "1.2.3" |
||||
|
val monocle = "1.5.0" |
||||
|
val postgis = "1.3.3" |
||||
|
val postgres = "42.2.5" |
||||
|
val scalaCheck = "1.14.0" |
||||
|
val scalaTest = "3.0.5" |
||||
|
val simulacrum = "0.14.0" |
||||
|
|
||||
|
val httpDeps = Seq( |
||||
|
"http4s-blaze-server", |
||||
|
"http4s-blaze-client", |
||||
|
"http4s-circe", |
||||
|
"http4s-dsl" |
||||
|
).map("org.http4s" %% _ % http4s) ++ Seq( |
||||
|
"circe-core", |
||||
|
"circe-generic", |
||||
|
"circe-parser", |
||||
|
"circe-java8" |
||||
|
).map("io.circe" %% _ % circe) |
||||
|
|
||||
|
val testDeps = Seq( |
||||
|
"org.scalatest" %% "scalatest" % scalaTest, |
||||
|
"org.tpolecat" %% "doobie-scalatest" % doobie, |
||||
|
"org.scalacheck" %% "scalacheck" % scalaCheck, |
||||
|
"com.clovellytech" %% "h4sm-dbtesting" % h4sm |
||||
|
) |
||||
|
|
||||
|
val testDepsInTestOnly = testDeps.map(_ % "test") |
||||
|
|
||||
|
val dbDeps = Seq( |
||||
|
"org.flywaydb" % "flyway-core" % flyway, |
||||
|
"org.postgresql" % "postgresql" % postgres, |
||||
|
"org.postgis" % "postgis-jdbc" % postgis |
||||
|
) ++ Seq( |
||||
|
"doobie-core", |
||||
|
"doobie-postgres", |
||||
|
"doobie-hikari" |
||||
|
).map("org.tpolecat" %% _ % doobie) |
||||
|
|
||||
|
val commonDeps = Seq( |
||||
|
"io.circe" %% "circe-config" % circeConfig, |
||||
|
"ch.qos.logback" % "logback-classic" % logback, |
||||
|
"com.github.mpilquist" %% "simulacrum" % simulacrum |
||||
|
) ++ Seq( |
||||
|
"h4sm-auth", |
||||
|
"h4sm-files", |
||||
|
"h4sm-permissions" |
||||
|
).map("com.clovellytech" %% _ % h4sm) ++ Seq( |
||||
|
"monocle-core", |
||||
|
"monocle-generic", |
||||
|
"monocle-macro", |
||||
|
"monocle-state", |
||||
|
"monocle-refined" |
||||
|
).map("com.github.julien-truffaut" %% _ % monocle) |
||||
|
|
||||
|
val allDeps = httpDeps ++ dbDeps ++ commonDeps ++ testDepsInTestOnly |
||||
|
} |
@ -0,0 +1,58 @@ |
|||||
|
|
||||
|
object options { |
||||
|
|
||||
|
val scalac = Seq( |
||||
|
// format: off |
||||
|
"-deprecation", // Emit warning and location for usages of deprecated APIs. |
||||
|
"-encoding", "utf-8", // Specify character encoding used by source files. |
||||
|
"-explaintypes", // Explain type errors in more detail. |
||||
|
"-feature", // Emit warning and location for usages of features that should be imported explicitly. |
||||
|
"-language:existentials", // Existential types (besides wildcard types) can be written and inferred |
||||
|
"-language:experimental.macros", // Allow macro definition (besides implementation and application) |
||||
|
"-language:higherKinds", // Allow higher-kinded types |
||||
|
"-language:implicitConversions", // Allow definition of implicit functions called views |
||||
|
"-unchecked", // Enable additional warnings where generated code depends on assumptions. |
||||
|
"-Xcheckinit", // Wrap field accessors to throw an exception on uninitialized access. |
||||
|
"-Xfatal-warnings", // Fail the compilation if there are any warnings. |
||||
|
"-Xfuture", // Turn on future language features. |
||||
|
"-Xlint:adapted-args", // Warn if an argument list is modified to match the receiver. |
||||
|
"-Xlint:by-name-right-associative", // By-name parameter of right associative operator. |
||||
|
"-Xlint:constant", // Evaluation of a constant arithmetic expression results in an error. |
||||
|
"-Xlint:delayedinit-select", // Selecting member of DelayedInit. |
||||
|
"-Xlint:doc-detached", // A Scaladoc comment appears to be detached from its element. |
||||
|
"-Xlint:inaccessible", // Warn about inaccessible types in method signatures. |
||||
|
"-Xlint:infer-any", // Warn when a type argument is inferred to be `Any`. |
||||
|
"-Xlint:missing-interpolator", // A string literal appears to be missing an interpolator id. |
||||
|
"-Xlint:nullary-override", // Warn when non-nullary `def f()' overrides nullary `def f'. |
||||
|
"-Xlint:nullary-unit", // Warn when nullary methods return Unit. |
||||
|
"-Xlint:option-implicit", // Option.apply used implicit view. |
||||
|
"-Xlint:package-object-classes", // Class or object defined in package object. |
||||
|
"-Xlint:poly-implicit-overload", // Parameterized overloaded implicit methods are not visible as view bounds. |
||||
|
"-Xlint:private-shadow", // A private field (or class parameter) shadows a superclass field. |
||||
|
"-Xlint:stars-align", // Pattern sequence wildcard must align with sequence component. |
||||
|
"-Xlint:type-parameter-shadow", // A local type parameter shadows a type already in scope. |
||||
|
"-Xlint:unsound-match", // Pattern match may not be typesafe. |
||||
|
"-Yno-adapted-args", // Do not adapt an argument list (either by inserting () or creating a tuple) to match the receiver. |
||||
|
"-Ypartial-unification", // Enable partial unification in type constructor inference |
||||
|
"-Ywarn-dead-code", // Warn when dead code is identified. |
||||
|
"-Ywarn-extra-implicit", // Warn when more than one implicit parameter section is defined. |
||||
|
"-Ywarn-inaccessible", // Warn about inaccessible types in method signatures. |
||||
|
"-Ywarn-infer-any", // Warn when a type argument is inferred to be `Any`. |
||||
|
"-Ywarn-nullary-unit", // Warn when nullary methods return Unit. |
||||
|
"-Ywarn-nullary-override", // Warn when non-nullary `def f()' overrides nullary `def f'. |
||||
|
"-Ywarn-numeric-widen", // Warn when numerics are widened. |
||||
|
"-Ywarn-unused:implicits", // Warn if an implicit parameter is unused. |
||||
|
"-Ywarn-unused:imports", // Warn if an import selector is not referenced. |
||||
|
"-Ywarn-unused:locals", // Warn if a local definition is unused. |
||||
|
"-Ywarn-unused:params", // Warn if a value parameter is unused. |
||||
|
"-Ywarn-unused:patvars", // Warn if a variable bound in a pattern is unused. |
||||
|
"-Ywarn-unused:privates", // Warn if a private member is unused. |
||||
|
"-Ywarn-value-discard", // Warn when non-Unit expression results are unused. |
||||
|
"-Yrangepos" |
||||
|
// format: on |
||||
|
) |
||||
|
|
||||
|
val badScalacConsoleFlags = Seq("-Xfatal-warnings", "-Ywarn-unused:imports") |
||||
|
|
||||
|
val scalacConsole = scalac.filterNot(badScalacConsoleFlags.contains(_)) |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
object Version{ |
||||
|
val version = "0.0.1" |
||||
|
val scalaVersion = "2.12.8" |
||||
|
} |
@ -0,0 +1 @@ |
|||||
|
sbt.version=1.2.3 |
@ -0,0 +1,4 @@ |
|||||
|
addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.13.1") |
||||
|
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.26") |
||||
|
addSbtPlugin("org.scalameta" % "sbt-mdoc" % "1.2.8" ) |
||||
|
addSbtPlugin("com.47deg" % "sbt-microsites" % "0.8.0") |
Write
Preview
Loading…
Cancel
Save
Reference in new issue