Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions src/main/twirl/com/twitter/scaffold/main.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta charset="utf-8"/>
<title>@title</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="description" content=""/>
<meta name="author" content=""/>

<link href="/assets/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet">
<link href="/assets/bootstrap/2.3.2/css/bootstrap-responsive.min.css" rel="stylesheet">
<link href="/assets/bootstrap/2.3.2/css/bootstrap.min.css" rel="stylesheet"/>
<link href="/assets/bootstrap/2.3.2/css/bootstrap-responsive.min.css" rel="stylesheet"/>

<link href="/assets/codemirror/3.14/lib/codemirror.css" rel="stylesheet">
<link href="/assets/codemirror/3.14/theme/solarized.css" rel="stylesheet">
<link href="/assets/scaffold.css" rel="stylesheet">
<link href="/assets/show-hint.css" rel="stylesheet">
<link href="/assets/codemirror/3.14/lib/codemirror.css" rel="stylesheet"/>
<link href="/assets/codemirror/3.14/theme/solarized.css" rel="stylesheet"/>
<link href="/assets/scaffold.css" rel="stylesheet"/>
<link href="/assets/show-hint.css" rel="stylesheet"/>

<!--[if lt IE 9]>
<script src="/assets/html5shiv/3.6.2/html5shiv.min.js"></script>
Expand All @@ -32,7 +32,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="brand" href="/"><img src="/assets/img/twitter-bird-dark-bgs.png"> Scala School 2</a>
<a class="brand" href="/"><img src="/assets/img/twitter-bird-dark-bgs.png"/> Scala School 2</a>
<div class="nav-collapse collapse">
<ul class="nav">
<li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Data and Control Flow <b class="caret"></b></a>
Expand Down
Empty file.
3 changes: 3 additions & 0 deletions src/test/resources/markdown/heading_with_lead.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# the heading

And the lead
19 changes: 19 additions & 0 deletions src/test/resources/markdown/multiple_headings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# first/title

LEAD



# section 1
Body1
# Sec B



Second body


# Part III

A section
be here
1 change: 1 addition & 0 deletions src/test/resources/markdown/no_heading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
no heading
1 change: 1 addition & 0 deletions src/test/resources/markdown/only_heading.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# only heading
7 changes: 7 additions & 0 deletions src/test/resources/markdown/with_code.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# With some code

# code section

Code preface

var actual_code = 0
68 changes: 68 additions & 0 deletions src/test/scala/com/twitter/scaffold/DocumentSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.twitter.scaffold

import org.scalatest.WordSpec
import org.scalatest.matchers.MustMatchers
import com.twitter.scaffold.Document
import DocumentSpecHelpers._

class DocumentSpec extends WordSpec with MustMatchers {
def render(name: String) = Document.render(name)
def getHtml(name: String): String = render(name).get.toString

"Document.render" should {
"return None for non-existent data file" in {
render("not_a_file") must equal (None)
}

"return None for empty data file" in {
render("empty") must equal (None)
}

"return None for a file not starting with a heading" in {
render("no_heading") must equal (None)
}
}

"Document.render" can {
"render a file with only a heading" in {
val xml = parse(getHtml("only_heading"))
title(xml) must equal ("Scala School 2 - only heading")
firstHeading(xml) must equal ("only heading")
lead(xml) must equal ("")
}

"render a heading and lead" in {
val xml = parse(getHtml("heading_with_lead"))
title(xml) must equal ("Scala School 2 - the heading")
firstHeading(xml) must equal ("the heading")
lead(xml) must equal ("And the lead")
}

"render multiple headings" in {
val xml = parse(getHtml("multiple_headings"))
title(xml) must equal ("Scala School 2 - first/title")
firstHeading(xml) must equal ("first/title")
lead(xml) must equal ("LEAD")
toc(xml) must equal (
List(
("section 1", "#section-1"),
("Sec B", "#sec-b"),
("Part III", "#part-iii")
)
)
sections(xml) must equal (
List(
("section-1", "section 1", "<p>Body1</p>"),
("sec-b", "Sec B", "<p>Second body</p>"),
("part-iii", "Part III", "<p>A section<br/>be here</p>")
)
)
}

"render code segments" in {
val xml = parse(getHtml("with_code"))
toc(xml).head must equal (("code section", "#code-section"))
sections(xml).head must equal (("code-section", "code section", "<p>Code preface</p><textarea>var actual_code = 0\n</textarea>"))
}
}
}
81 changes: 81 additions & 0 deletions src/test/scala/com/twitter/scaffold/DocumentSpecHelpers.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.twitter.scaffold

import scala.xml._

object DocumentSpecHelpers {
def parse(html: String) = XML.loadString(html)

def title(xml: Elem): String = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than explicitly doing flatMap and map, you can do this a bit more idiomatically using for-expressions:

for {
  head <- xml.child find { _.label == "head" }
  title <- head.child find { _.label == "title" }
} yield title.text

And probably shouldn't do the getOrElse "", to avoid masking errors in the tests.

xml.child.find {
_.label == "head"
} flatMap {
_.child.find(_.label == "title")
} map {
_.text
} getOrElse ""
}

def firstHeading(xml: Elem): String = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same trick here. The pattern you can recognize is: a sequence of flatMaps followed by a final map is directly translatable to a for-yield.

xml.child.find {
_.label == "body"
} flatMap {
_.child.find(_.label == "header")
} flatMap {
_.child.find(_.label == "div")
} flatMap {
_.child.find(_.label == "h1")
} map {
_.text
} getOrElse ""
}

def lead(xml: Elem): String = {
xml.child.find {
_.label == "body"
} flatMap {
_.child.find(_.label == "header")
} flatMap {
_.child.find(_.label == "div")
} flatMap {
_.child.find(_.label == "p")
} map {
_.text
} getOrElse ""
}

def toc(xml: Elem): List[(String, String)] = {
xml.child.find {
_.label == "body"
} flatMap {
_.child.find { n: Node =>
n.label == "div" && n.attributes("class") != null && n.attributes("class").head.text == "span3 scaffold-sidebar"
}
} flatMap {
_.child.find(_.label == "ul")
} map {
_.child.toList.filter(_.label == "li") map { n: Node =>
val c = n.child.find(_.label == "a").get
(c.text.tail, c.attributes("href").head.text)
}
} getOrElse List()
}

def sections(xml: Elem): List[(String, String, String)] = {
xml.child.find {
_.label == "body"
} flatMap {
_.child.find { n: Node =>
n.label == "div" && n.attributes("class") != null && n.attributes("class").head.text == "container"
}
} map {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one's a slightly different situation: if you have two chained calls to map, you can replace them with a single one. In this case:

... map {
  _.child(1).child(1).child.toList.filter ...
}

Although it'd probably be better here to split it up like:

... map { elem =>
  val firstGrandchild = elem.child(1).child(1)
  ...
}

_.child(1).child(1)
} map {
_.child.toList.filter(_.label == "section") map { n: Node =>
val link = n.attributes("id").head.text
val header = n.child(1).child(0).text
val rest = n.child.tail.tail.mkString("").trim
(link, header, rest)
}
} getOrElse List()
}
}