Skip to content

Commit

Permalink
tut powered blog
Browse files Browse the repository at this point in the history
  • Loading branch information
larsrh committed Oct 9, 2016
1 parent 7d9a902 commit f7c71d3
Show file tree
Hide file tree
Showing 10 changed files with 249 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ Gemfile.lock
/vendor/bundle
css/main.css
.sass-cache
target
2 changes: 1 addition & 1 deletion _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ kramdown:
hard_wrap: false
highlighter: rouge
exclude:
["node_modules", "gulpfile.js", "package.json", "README.md", "CNAME", "vendor"]
["node_modules", "gulpfile.js", "package.json", "README.md", "CNAME", "vendor", "src", "project", "build.sbt", "_tut"]
permalink: /blog/:year/:month/:day/:title.html
paginate: 10
paginate_path: "/blog/:num/"
Expand Down
31 changes: 31 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
scalaVersion := "2.11.8"

name := "typelevel-blog-tut"

scalacOptions ++= Seq(
"-deprecation",
"-feature"
)

libraryDependencies ++= Seq(
"io.get-coursier" %% "coursier" % "1.0.0-M14",
"io.get-coursier" %% "coursier-cache" % "1.0.0-M14",
"com.chuusai" %% "shapeless" % "2.3.0",
"org.yaml" % "snakeyaml" % "1.17"
)

lazy val tutInput = SettingKey[File]("tutInput")
lazy val tutOutput = SettingKey[File]("tutOutput")
lazy val tutVersion = SettingKey[String]("tutVersion")

tutInput := (baseDirectory in ThisBuild).value / "_tut"
tutOutput := (baseDirectory in ThisBuild).value / "_posts"
tutVersion := "0.4.4"

watchSources ++= (tutInput.value ** "*.md").get

enablePlugins(BuildInfoPlugin)

buildInfoKeys := Seq[BuildInfoKey](tutInput, tutOutput, tutVersion)

buildInfoPackage := "org.typelevel.blog"
1 change: 1 addition & 0 deletions project/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
sbt.version=0.13.12
1 change: 1 addition & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.6.1")
39 changes: 39 additions & 0 deletions src/main/scala/FrontMatter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.typelevel.blog

import coursier._
import coursier.util.Parse
import java.io.File
import java.net.URLClassLoader

case class Tut(scala: String, binaryScala: String, dependencies: List[String]) {

val tutResolution: Resolution = Resolution(Set(
Dependency(Module("org.tpolecat", s"tut-core_$binaryScala"), BuildInfo.tutVersion)
))

val libResolution: Resolution = Resolution(dependencies.map { dep =>
val (mod, v) = Parse.moduleVersion(dep, binaryScala).right.get
Dependency(mod, v)
}.toSet)

def invoke(file: File): Unit = {
val tutClasspath = resolve(tutResolution).get
val libClasspath = resolve(libResolution).get

val classLoader = new URLClassLoader(tutClasspath.map(_.toURI.toURL).toArray, null)
val tutClass = classLoader.loadClass("tut.TutMain")
val tutMain = tutClass.getDeclaredMethod("main", classOf[Array[String]])

val commandLine = Array(
file.toString,
BuildInfo.tutOutput.toString,
".*",
"-classpath",
libClasspath.mkString(File.pathSeparator)
)

tutMain.invoke(null, commandLine)
}
}

case class FrontMatter(tut: Tut)
13 changes: 13 additions & 0 deletions src/main/scala/Main.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.typelevel.blog

import org.yaml.snakeyaml.Yaml

object Main extends App {

val posts = BuildInfo.tutInput.listFiles().toList.filter { file =>
file.isFile() && file.getName.endsWith(".md")
}.map(Post(_))

posts.foreach(_.process())

}
41 changes: 41 additions & 0 deletions src/main/scala/Post.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.typelevel.blog

import coursier._
import java.io.{File, FileInputStream}
import java.nio.file.Files
import org.yaml.snakeyaml.Yaml
import scala.util.Try

case class Post(file: File) {

lazy val frontMatter: Option[FrontMatter] = Try {
val yaml = new Yaml()
val stream = new FileInputStream(file)
val any = yaml.loadAll(stream).iterator.next()
stream.close()
any
}.flatMap(YAML.decodeTo[FrontMatter]).toOption

lazy val out: File =
new File(BuildInfo.tutOutput.toString + File.separator + file.getName)

def outdated(): Boolean =
!(out.exists() && out.isFile() && file.lastModified() <= out.lastModified())

def process(): Unit =
if (outdated()) {
println(s"Processing ${file.getName} ...")

frontMatter match {
case Some(FrontMatter(tut)) =>
tut.invoke(file)
case None =>
println("No tut header, copying.")
Files.copy(file.toPath, out.toPath)
}
}
else {
println(s"Skipping ${file.getName} (up to date).")
}

}
66 changes: 66 additions & 0 deletions src/main/scala/YAML.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.typelevel.blog

import org.yaml.snakeyaml.Yaml
import scala.collection.JavaConverters._
import scala.util.Try
import shapeless._
import shapeless.labelled.FieldType

trait YAML[T] {
def rawDecode(any: Any): T

final def decode(any: Any): Try[T] =
Try(rawDecode(any))
}

trait LowPriorityYAML {

implicit def deriveInstance[F, G](implicit gen: LabelledGeneric.Aux[F, G], yg: Lazy[YAML[G]]): YAML[F] =
new YAML[F] {
def rawDecode(any: Any) = gen.from(yg.value.rawDecode(any))
}

}

object YAML extends LowPriorityYAML {

def apply[T](implicit T: YAML[T]): YAML[T] = T

def decodeTo[T : YAML](any: Any): Try[T] =
YAML[T].decode(any)

implicit def listYAML[T : YAML]: YAML[List[T]] =
new YAML[List[T]] {
def rawDecode(any: Any) =
any.asInstanceOf[java.util.List[_]].asScala.toList.map(YAML[T].rawDecode)
}

implicit def stringYAML: YAML[String] =
new YAML[String] {
def rawDecode(any: Any) =
any.asInstanceOf[String]
}

implicit def deriveHNil: YAML[HNil] =
new YAML[HNil] {
def rawDecode(any: Any) = HNil
}

implicit def deriveHCons[K <: Symbol, V, T <: HList]
(implicit
key: Witness.Aux[K],
yv: Lazy[YAML[V]],
yt: Lazy[YAML[T]]
): YAML[FieldType[K, V] :: T] = new YAML[FieldType[K, V] :: T] {
def rawDecode(any: Any) = {
val k = key.value.name
val map = any.asInstanceOf[java.util.Map[String, _]]
if (!map.containsKey(k))
throw new IllegalArgumentException(s"key $k not defined")
val head: FieldType[K, V] = labelled.field(yv.value.rawDecode(map.get(k)))
val tail = yt.value.rawDecode(map)
head :: tail
}
}

}
55 changes: 55 additions & 0 deletions src/main/scala/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.typelevel.blog

import coursier._
import java.io.File
import scala.language.implicitConversions
import scala.util.{Failure, Success, Try}
import scalaz.\/
import scalaz.concurrent.Task
import scalaz.std.list._
import scalaz.syntax.traverse._

object `package` {

val repositories = Seq(
Cache.ivy2Local,
MavenRepository("https://repo1.maven.org/maven2"),
MavenRepository("https://dl.bintray.com/tpolecat/maven/")
)

implicit def eitherToTry[T](e: Throwable \/ T): Try[T] =
e.fold(Failure(_), Success(_))

def resolve(start: Resolution): Try[List[File]] = {
val logger = new Cache.Logger {
override def downloadingArtifact(url: String, file: File) =
println(s"Downloading artifact from $url ...")
override def downloadedArtifact(url: String, success: Boolean) = {
val file = url.split('/').last
if (success)
println(s"Successfully downloaded $file")
else
println(s"Failed to download $file")
}
}

val fetch = Fetch.from(repositories, Cache.fetch(logger = Some(logger)))

start.process.run(fetch).unsafePerformSyncAttempt.flatMap { resolution =>
if (!resolution.isDone)
\/.left(new RuntimeException("resolution did not converge"))
else if (!resolution.conflicts.isEmpty)
\/.left(new RuntimeException(s"resolution has conflicts: ${resolution.conflicts.mkString(", ")}"))
else if (!resolution.errors.isEmpty)
\/.left(new RuntimeException(s"resolution has errors: ${resolution.errors.mkString(", ")}"))
else {
Task.gatherUnordered(
resolution.artifacts.map(artifact =>
Cache.file(artifact, logger = Some(logger)).run
)
).unsafePerformSyncAttempt.map(_.sequenceU).flatMap(_.leftMap(err => new RuntimeException(err.describe)))
}
}
}

}

0 comments on commit f7c71d3

Please sign in to comment.