+ first version of directory creation
authorAlexandre Bertails <bertails@gmail.com>
Sat, 15 Oct 2011 23:52:14 -0400
changeset 73 5a8fc47be78b
parent 72 c9986c05e45e
child 83 642d8a1b35d5
child 106 0e01bf07ab68
child 108 36aaa14c27c5
+ first version of directory creation
src/main/scala/Authoritative.scala
src/main/scala/Filesystem.scala
src/main/scala/ReadWriteWebMain.scala
src/main/scala/Representation.scala
src/main/scala/Resource.scala
src/main/scala/plan.scala
src/test/scala/CreateContentSpecs.scala
src/test/scala/CreateDirectorySpec.scala
src/test/scala/ReadWriteWebSpecs.scala
src/test/scala/util/specs.scala
--- a/src/main/scala/Authoritative.scala	Sat Oct 15 23:51:32 2011 -0400
+++ b/src/main/scala/Authoritative.scala	Sat Oct 15 23:52:14 2011 -0400
@@ -11,8 +11,10 @@
     val uri = req.underlying.getRequestURL.toString
     val suffixOpt = uri match {
       case r(_, suffix) => Some(suffix)
+      case _ if uri endsWith "/" => Some("/")
       case _ => None
     }
     Some((new URL(uri), Representation(suffixOpt, Accept(req))))
   }
+
 }
--- a/src/main/scala/Filesystem.scala	Sat Oct 15 23:51:32 2011 -0400
+++ b/src/main/scala/Filesystem.scala	Sat Oct 15 23:52:14 2011 -0400
@@ -7,29 +7,41 @@
 import org.slf4j.{Logger, LoggerFactory}
 import com.hp.hpl.jena.rdf.model._
 import com.hp.hpl.jena.shared.JenaException
-import sys.error
-import scalaz._
+
+import scalaz.{sys => _, _}
 import Scalaz._
 
 class Filesystem(
   baseDirectory: File,
   val basePath: String,
-  val lang: String = "RDF/XML-ABBREV")(mode: RWWMode) extends ResourceManager {
+  val lang: Lang)(mode: RWWMode) extends ResourceManager {
   
   val logger: Logger = LoggerFactory.getLogger(this.getClass)
   
-  def sanityCheck(): Boolean = baseDirectory.exists
+  def sanityCheck(): Boolean =
+    baseDirectory.exists && baseDirectory.isDirectory
   
   def resource(url: URL): Resource = new Resource {
     val relativePath: String = url.getPath.replaceAll("^"+basePath.toString+"/?", "")
     val fileOnDisk = new File(baseDirectory, relativePath)
     
-    private def createFileOnDisk(): Unit = {
-      // create parent directory if needed
+    private def parentMustExist(): Unit = {
       val parent = fileOnDisk.getParentFile
-      if (! parent.exists) println(parent.mkdirs)
+      if (! parent.exists) sys.error("Parent directory %s does not exist" format parent.getAbsolutePath)
+      if (! parent.isDirectory) sys.error("Parent %s is not a directory" format parent.getAbsolutePath)
+    }
+    
+    private def createDirectoryOnDisk(): Unit = {
+      parentMustExist()
+      val r = fileOnDisk.mkdir()
+      if (!r) sys.error("Could not create %s" format fileOnDisk.getAbsolutePath)
+      logger.debug("%s successfully created: %s" format (fileOnDisk.getAbsolutePath, r.toString))
+    }
+    
+    private def createFileOnDisk(): Unit = {
+      parentMustExist()
       val r = fileOnDisk.createNewFile()
-      logger.debug("Create file %s with success: %s" format (fileOnDisk.getAbsolutePath, r.toString))
+      logger.debug("%s successfully created: %s" format (fileOnDisk.getAbsolutePath, r.toString))
     }
     
     def get(): Validation[Throwable, Model] = {
@@ -37,10 +49,10 @@
       if (fileOnDisk.exists()) {
         val fis = new FileInputStream(fileOnDisk)
         try {
-          val reader = model.getReader(lang)
+          val reader = model.getReader(lang.jenaLang)
           reader.read(model, fis, url.toString)
         } catch {
-          case je:JenaException => error("@@@")
+          case je: JenaException => throw je
         }
         fis.close()
         model.success
@@ -56,13 +68,24 @@
       try {
         createFileOnDisk()
         val fos = new FileOutputStream(fileOnDisk)
-        val writer = model.getWriter(lang)
+        val writer = model.getWriter(lang.jenaLang)
         writer.write(model, fos, url.toString)
         fos.close().success
       } catch {
         case t => t.fail
       }
 
+    def createDirectory(model: Model): Validation[Throwable, Unit] =
+      try {
+        createDirectoryOnDisk().success
+//        val fos = new FileOutputStream(fileOnDisk)
+//        val writer = model.getWriter(lang.contentType)
+//        writer.write(model, fos, url.toString)
+//        fos.close().success
+      } catch {
+        case t => t.fail
+      }
+
   }
   
 }
--- a/src/main/scala/ReadWriteWebMain.scala	Sat Oct 15 23:51:32 2011 -0400
+++ b/src/main/scala/ReadWriteWebMain.scala	Sat Oct 15 23:52:14 2011 -0400
@@ -27,12 +27,12 @@
       }
     }
 
-  val rdfLanguage = parser.option[String](List("language"), "l", "RDF language") {
+  val rdfLanguage = parser.option[Lang](List("language"), "l", "RDF language") {
     (sValue, opt) =>
       sValue match {
-        case "n3" => "N3"
-        case "turtle" => "N3"
-        case "rdfxml" => "RDF/XML-ABBREV"
+        case "n3" => N3
+        case "turtle" => TURTLE
+        case "rdfxml" => RDFXML
         case _ => throw new ArgotConversionException("Option %s: must be either n3, turtle or rdfxml" format (opt.name, sValue))
       }
   }
@@ -64,7 +64,7 @@
       new Filesystem(
         rootDirectory.value.get,
         baseURL.value.get,
-        lang=rdfLanguage.value getOrElse "N3")(mode.value getOrElse ResourcesDontExistByDefault)
+        lang=rdfLanguage.value getOrElse RDFXML)(mode.value getOrElse ResourcesDontExistByDefault)
     
     val app = new ReadWriteWeb(filesystem)
 
--- a/src/main/scala/Representation.scala	Sat Oct 15 23:51:32 2011 -0400
+++ b/src/main/scala/Representation.scala	Sat Oct 15 23:52:14 2011 -0400
@@ -13,14 +13,15 @@
       case "turtle" | "ttl" => RDFRepr(TURTLE)
       case "rdf" => RDFRepr(RDFXML)
       case "htm" | "html" | "xhtml" => HTMLRepr
+      case "/" => DirectoryRepr
       case _ => UnknownRepr
     }
   }
   
-  val htmlCharsets = Set("text/html", "application/xhtml+xml")
+  val htmlContentTypes = Set("text/html", "application/xhtml+xml")
   
   def acceptsHTML(ct: Iterable[String]) =
-    ! (htmlCharsets & ct.toSet).isEmpty
+    ! (htmlContentTypes & ct.toSet).isEmpty
   
   def fromAcceptedContentTypes(ct: Iterable[String]): Representation = {
     Lang(ct) map RDFRepr.apply getOrElse {
@@ -50,5 +51,5 @@
 
 case class RDFRepr(lang: Lang) extends Representation
 case object HTMLRepr extends Representation
+case object DirectoryRepr extends Representation
 case object UnknownRepr extends Representation
-case object NoRepr extends Representation
--- a/src/main/scala/Resource.scala	Sat Oct 15 23:51:32 2011 -0400
+++ b/src/main/scala/Resource.scala	Sat Oct 15 23:52:14 2011 -0400
@@ -16,5 +16,6 @@
 trait Resource {
   def get(): Validation[Throwable, Model]
   def save(model: Model): Validation[Throwable, Unit]
+  def createDirectory(model: Model): Validation[Throwable, Unit]
 }
 
--- a/src/main/scala/plan.scala	Sat Oct 15 23:51:32 2011 -0400
+++ b/src/main/scala/plan.scala	Sat Oct 15 23:52:14 2011 -0400
@@ -21,15 +21,18 @@
 import scalaz._
 import Scalaz._
 
+//object ReadWriteWeb {
+//  
+//  val defaultHandler: PartialFunction[Throwable, HttpResponse[_]] = {
+//    case t => InternalServerError ~> ResponseString(t.getStackTraceString)
+//  }
+//  
+//}
+
 class ReadWriteWeb(rm: ResourceManager) {
   
   val logger: Logger = LoggerFactory.getLogger(this.getClass)
 
-  def isHTML(accepts: List[String]): Boolean = {
-    val accept = accepts.headOption
-    accept == Some("text/html") || accept == Some("application/xhtml+xml")
-  }
-  
   /** I believe some documentation is needed here, as many different tricks
    *  are used to make this code easy to read and still type-safe
    *  
@@ -57,7 +60,7 @@
       val Authoritative(uri, representation) = req
       val r: Resource = rm.resource(uri)
       req match {
-        case GET(_) & Accept(accepts) if isHTML(accepts) => {
+        case GET(_) if representation == HTMLRepr => {
           val source = Source.fromFile("src/main/resources/skin.html")("UTF-8")
           val body = source.getLines.mkString("\n")
           Ok ~> ViaSPARQL ~> ContentType("text/html") ~> ResponseString(body)
@@ -75,6 +78,12 @@
               case HEAD(_) => Ok ~> ViaSPARQL ~> ContentType(lang.contentType)
             }
           }
+        case PUT(_) & RequestLang(lang) if representation == DirectoryRepr => {
+          for {
+            bodyModel <- modelFromInputStream(Body.stream(req), uri, lang) failMap { t => BadRequest ~> ResponseString(t.getStackTraceString) }
+            _ <- r.createDirectory(bodyModel) failMap { t => InternalServerError ~> ResponseString(t.getStackTraceString) }
+          } yield Created
+        }
         case PUT(_) & RequestLang(lang) =>
           for {
             bodyModel <- modelFromInputStream(Body.stream(req), uri, lang) failMap { t => BadRequest ~> ResponseString(t.getStackTraceString) }
--- a/src/test/scala/CreateContentSpecs.scala	Sat Oct 15 23:51:32 2011 -0400
+++ b/src/test/scala/CreateContentSpecs.scala	Sat Oct 15 23:52:14 2011 -0400
@@ -5,7 +5,7 @@
 
 import dispatch._
 
-object PutRDFXMLSpec extends FilesystemBased with SomeRDF with SomeURI {
+object PutRDFXMLSpec extends SomePeopleDirectory {
 
   "PUTing an RDF document on Joe's URI (which does not exist yet)" should {
     "return a 201" in {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/test/scala/CreateDirectorySpec.scala	Sat Oct 15 23:52:14 2011 -0400
@@ -0,0 +1,20 @@
+package org.w3.readwriteweb
+
+import org.w3.readwriteweb.util._
+import org.w3.readwriteweb.utiltest._
+
+import dispatch._
+
+object CreateDirSpec extends FilesystemBased with SomeRDF with SomeURI {
+
+  "PUTing an RDF document on /people/" should {
+    "return a 201" in {
+      val httpCode = Http(dirUri.put(RDFXML, rdfxml) get_statusCode)
+      httpCode must_== 201
+    }
+    "create a directory on disk" in {
+      directory must be directory
+    }
+  }
+
+}
--- a/src/test/scala/ReadWriteWebSpecs.scala	Sat Oct 15 23:51:32 2011 -0400
+++ b/src/test/scala/ReadWriteWebSpecs.scala	Sat Oct 15 23:52:14 2011 -0400
@@ -7,6 +7,8 @@
       // access content
       GetStrictModeSpec, GetWikiModeSpec,
       ContentNegociationSpec,
+      // create directory
+      CreateDirSpec,
       // create content
       PutRDFXMLSpec, PostRDFSpec,
       PutInvalidRDFXMLSpec, PostOnNonExistingResourceSpec,
--- a/src/test/scala/util/specs.scala	Sat Oct 15 23:51:32 2011 -0400
+++ b/src/test/scala/util/specs.scala	Sat Oct 15 23:52:14 2011 -0400
@@ -31,13 +31,13 @@
   
   lazy val mode: RWWMode = ResourcesDontExistByDefault
   
-  lazy val language = "RDF/XML-ABBREV"
+  lazy val lang = RDFXML
     
-  lazy val baseURL = "/2007/wiki"
+  lazy val baseURL = "/wiki"
   
   lazy val root = new File(new File(System.getProperty("java.io.tmpdir")), "readwriteweb")
 
-  lazy val resourceManager = new Filesystem(root, baseURL, language)(mode)
+  lazy val resourceManager = new Filesystem(root, baseURL, lang)(mode)
   
   doBeforeSpec {
     if (root.exists) root.deleteRecursively()
@@ -70,15 +70,28 @@
   
   val emptyModel = com.hp.hpl.jena.rdf.model.ModelFactory.createDefaultModel()
   
-  lazy val uri = host / "2007/wiki/people/JoeLambda"
+  lazy val dirUri = host / "wiki/people/"
+  
+  lazy val uri = host / "wiki/people/JoeLambda"
   
   lazy val uriBase = baseURI(uri)
   
+  lazy val directory = new File(root, "people")
+  
   lazy val resourceOnDisk = new File(root, "people/JoeLambda")
   
 }
 
-trait SomeDataInStore extends FilesystemBased with SomeRDF with SomeURI {
+trait SomePeopleDirectory extends SomeRDF with SomeURI {
+  
+  doBeforeSpec {
+    val httpCode = Http(dirUri.put(RDFXML, rdfxml) get_statusCode)
+    httpCode must_== 201
+  }
+  
+}
+
+trait SomeDataInStore extends SomePeopleDirectory {
   
   doBeforeSpec {
     val httpCode = Http(uri.put(RDFXML, rdfxml) get_statusCode)