Skip to content

Commit

Permalink
Merge branch 'release/0.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
wspringer committed Feb 22, 2012
2 parents 3825273 + d437ea9 commit a2881fb
Show file tree
Hide file tree
Showing 22 changed files with 426 additions and 101 deletions.
34 changes: 28 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,31 +22,53 @@ created if there _would_ have been a solution that supports:
If ever such a solution arrives, then there is a chance Monkeyman will
no longer be maintained.

## Other features
## Features

* Jade, HAML (SCAML), SSP support (through Scalate)
* ZUSS support (Java alternative for LESS/SCSS)
* Jade, HAML (SCAML), Mustache, SSP for layouts
* LESS and ZUSS to CSS transformation
* Live preview (web server running on port 4567)
* Live updates (monitoring file system changes)
* Static web site generation
* Content management through tags

## Usage

For information on how to _use_ Monkeyman, check the [sample blog]
(http://monkeyman.flotsam.nl/).
(http://monkeyman.flotsam.nl/), or watch
[the video](http://www.youtube.com/watch?v=9giYvVGIi0U).

But basically, it boils down to this:

* Create a `source` directory; store HAML, Jade, SSP, Markdown files
with YAML frontmatter and other static resources in that directory.
* Create a `source` directory; store HAML, Jade, SSP, Markdown,
Mustache files with YAML frontmatter and other static resources in
that directory.
* Create a `layout` directory; store HAML, Jade or SSP layouts in that
directory.
* Have a web server pick up all of the changes you are making,
providing you live preview, by typing `monkeyman server`.
* Build your final version in the `target` directory, by typing
`middleman generate`.

This comment has been minimized.

Copy link
@anneveling

anneveling Feb 22, 2012

should be 'monkeyman' here

This comment has been minimized.

Copy link
@wspringer

wspringer Feb 22, 2012

Author Owner

Ha, that's hilarious. Good find!


## Installation

Download the monkeyman jar from the Downloads section. Start it with
`java -jar monkeyman.jar`, or create a script for it, as I did:

#!/bin/sh

java -jar ~/workspace/monkeyman/target/monkeyman.jar "$@"

## Changes

0.2

* 2012-02-20: Default site template (no need to start creating a layout)
* 2012-02-18: File system monitoring (detect changes and update content when required)

0.1

* 2012-02-14: Add LESS support.
* 2012-02-13: Add live preview support.



4 changes: 3 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import AssemblyKeys._

name := "monkeyman"

version := "0.1"
version := "0.2"

scalaVersion := "2.8.1"

Expand Down Expand Up @@ -45,3 +45,5 @@ mainClass in assembly := Some("nl.flotsam.monkeyman.Monkeyman")
jarName in assembly := "monkeyman.jar"

fork in run := true

connectInput in run := true
Binary file added src/main/resources/favicon.ico
Binary file not shown.
18 changes: 18 additions & 0 deletions src/main/resources/layout.scaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
- val dateFormat = org.joda.time.format.DateTimeFormat.forPattern("EEEE d MMMM")
!!!html
%html
%head
-# Don't get it
- if (title.isDefined)
%title= title.get
%link(rel="stylesheet" href="monkeyman/monkeyman.css" type="text/css")
%body
#main
#header
%h1= title.getOrElse("No title")
#subtitle= dateFormat.print(pubDateTime)
.hr
#body
- unescape(body)


Binary file added src/main/resources/monkeyman/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions src/main/resources/monkeyman/monkeyman.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
@base: #DDDDDD;
@fontcolor: #3F3F3F;

html, body {
margin:0;
padding: 0;
height: 100%;
}

body {
font-family: Helvetica, Arial, sans;
background-color: @base;
}

#main {
margin-left: auto;
margin-right: auto;
width: 800px;
background-color: lighten(@base, 10%);
padding-left: 40px;
padding-right: 40px;
padding-top: 0px;
margin-top: 0px;
margin-bottom: 0px;
min-height: 100%;
border-left: 1px solid darken(@base, 10%);
border-right: 1px solid darken(@base, 10%);
}

#header {
padding-top: 30px;
background-image: url("./logo.png");
background-repeat: no-repeat;
background-position: right;
height: 100px;
}

#body {
color: @fontcolor;
margin-top: 30px;
}

h1 {
margin-top: 0px;
color: darken(@fontcolor, 3%);
margin-bottom: 0px;
}

#subtitle {
font-weight: 300;
font-size: 18px;
}

.hr {
border-top: 1px solid darken(@base, 5%);
border-bottom: 1px solid white;
width: 100%;
height: 0px;
}
46 changes: 46 additions & 0 deletions src/main/scala/nl/flotsam/monkeyman/ClasspathResource.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Monkeyman static web site generator
* Copyright (C) 2012 Wilfred Springer
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package nl.flotsam.monkeyman

import org.joda.time.LocalDateTime
import eu.medsea.mimeutil.{MimeType, MimeUtil}
import collection.JavaConversions._

case class ClasspathResource(path: String) extends Resource {

val url = getClass.getResource("/" + path)

def title = None

def pubDateTime = LocalDateTime.now()

def contentType = MimeUtil.getMimeTypes(url).asInstanceOf[java.util.Set[MimeType]].head.toString

def open = url.openStream()

def tags = Set.empty

def published = true

def asHtmlFragment = None

def id = path

}
62 changes: 62 additions & 0 deletions src/main/scala/nl/flotsam/monkeyman/ClasspathResourceLoader.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Monkeyman static web site generator
* Copyright (C) 2012 Wilfred Springer
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

package nl.flotsam.monkeyman

import org.joda.time.LocalDateTime
import collection.JavaConversions._
import eu.medsea.mimeutil.detector.ExtensionMimeDetector
import eu.medsea.mimeutil.{MimeType, MimeUtil}


class ClasspathResourceLoader(paths: Seq[String], loader: ResourceLoader) extends ResourceLoader {

private val resources = paths.map(path => ClasspathResource(path))

def load = {
val loaded = loader.load
val ids = loaded.map(_.id)
resources.filter(resource => !ids.contains(resource.id)) ++ loaded
}

def register(listener: ResourceListener) {
loader.register(new ResourceListener {
def deleted(id: String) {
resources.find(_.id == id) match {
case Some(resource) => listener.modified(resource)
case None => listener.deleted(id)
}
}

def modified(resource: Resource) {
listener.modified(resource)
}

def added(resource: Resource) {
resources.find(_.id == resource.id) match {
case Some(resource) => listener.modified(resource)
case None => listener.added(resource)
}
}
})
}



}
2 changes: 2 additions & 0 deletions src/main/scala/nl/flotsam/monkeyman/FileSystemResource.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import org.joda.time.LocalDateTime

case class FileSystemResource(baseDir: File, path: String) extends Resource {

lazy val url = file.toURI.toURL

lazy val file = new File(baseDir, path)

lazy val title = None
Expand Down
73 changes: 38 additions & 35 deletions src/main/scala/nl/flotsam/monkeyman/FileSystemResourceLoader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ import nl.flotsam.monkeyman.ext.ResourceUtils
import org.apache.commons.io.filefilter._
import collection.mutable.Buffer
import name.pachler.nio.file.StandardWatchEventKind._
import java.util.concurrent.Executors
import java.io.{FileFilter, File}
import collection.JavaConversions
import JavaConversions._
import util.Logging
import name.pachler.nio.file._
import java.util.concurrent.{ExecutorService, Executors}

class FileSystemResourceLoader(baseDir: File)
extends ResourceLoader with Logging {
Expand All @@ -54,49 +54,52 @@ class FileSystemResourceLoader(baseDir: File)
path = Paths.get(dir.getAbsolutePath)
} {
keys += path.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) -> path
Executors.newSingleThreadExecutor().execute(new Runnable() {
def run() {
while (true) {
val watchKey: WatchKey = watchService.take()
keys.get(watchKey) match {
case Some(dir) =>
val events = watchKey.pollEvents().toList
watchKey.reset()
for (event <- events) {
if (event.kind() == ENTRY_CREATE) {
val created = dir.resolve(event.context().asInstanceOf[Path])
if ((new File(created.toString)).isDirectory) {
debug("Added directory {}", created)
keys += created.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) -> created
} else {
debug("Added file {}", created)
listeners.map(_.added(new FileSystemResource(baseDir, relative(created))))
}
}
else if (event.kind() == ENTRY_DELETE) {
val deleted = dir.resolve(event.context().asInstanceOf[Path])
debug("Deleted {}", deleted)
listeners.map(_.deleted(relative(deleted)))
}

private val watchThread = Executors.newSingleThreadExecutor().submit(new Runnable() {
def run() {
while (true) {
val watchKey: WatchKey = watchService.take()
keys.get(watchKey) match {
case Some(dir) =>
val events = watchKey.pollEvents().toList
watchKey.reset()
for (event <- events) {
if (event.kind() == ENTRY_CREATE) {
val created = dir.resolve(event.context().asInstanceOf[Path])
if ((new File(created.toString)).isDirectory) {
debug("Added directory {}", created)
keys += created.register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) -> created
} else {
debug("Added file {}", created)
listeners.map(_.added(new FileSystemResource(baseDir, relative(created))))
}
else if (event.kind() == ENTRY_MODIFY) {
val modified = dir.resolve(event.context().asInstanceOf[Path])
if (!(new File(modified.toString)).isDirectory) {
debug("Modified {}", modified)
listeners.map(_.modified(new FileSystemResource(baseDir, relative(modified))))
}
}
else if (event.kind() == ENTRY_DELETE) {
val deleted = dir.resolve(event.context().asInstanceOf[Path])
debug("Deleted {}", deleted)
listeners.map(_.deleted(relative(deleted)))
}
else if (event.kind() == ENTRY_MODIFY) {
val modified = dir.resolve(event.context().asInstanceOf[Path])
if (!(new File(modified.toString)).isDirectory) {
debug("Modified {}", modified)
listeners.map(_.modified(new FileSystemResource(baseDir, relative(modified))))
}
}
case _ =>
}
}
case _ =>
}
}
})
}
}
})

def dispose {
for ((key, path) <- keys) {
key.cancel()
try { key.cancel() } catch { case t: Throwable => }
}
watchService.close()
watchThread.cancel(true)
}

def load(file: File) = {
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/nl/flotsam/monkeyman/LayoutResolver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ import org.fusesource.scalate.Template

trait LayoutResolver {

def resolve(path: String): Option[Template]
def resolve(path: String): Template

}
Loading

0 comments on commit a2881fb

Please sign in to comment.