{"action":"created","comment":{"url":"https://api.github.com/repos/ambiata/mundane/pulls/comments/24075139","id":24075139,"diff_hunk":"@@ -0,0 +1,245 @@\n+package com.ambiata.mundane.io\n+\n+import com.ambiata.mundane.control._\n+import com.ambiata.mundane.data._\n+import com.ambiata.mundane.path._\n+import com.ambiata.mundane.io.LocalFile._\n+import java.io._\n+import java.net.URI\n+import java.util.UUID\n+import scala.io.Codec\n+import scalaz._, Scalaz._, effect._, Effect._\n+\n+/**\n+ * 'LocalFile' represents a file that exists on a local file system\n+ */\n+class LocalFile private (val path: Path) extends AnyVal {\n+  def toLocalPath: LocalPath =\n+    LocalPath(path)\n+\n+  def toPath: Path =\n+    path\n+\n+  def toFile: File =\n+    new File(path.path)\n+\n+// ============= Operations =================\n+\n+  def exists: RIO[Boolean] = RIO.safe[Boolean] {\n+    val file = toFile\n+    file.exists && file.isFile\n+  }\n+\n+  def onExists[A](success: => RIO[A], missing: => RIO[A]): RIO[A] =\n+    exists >>= (e =>\n+      if (e) success\n+      else   missing\n+    )\n+\n+  def optionExists[A](thunk: => RIO[A]): RIO[Option[A]] =\n+    onExists(thunk.map(_.some), none.pure[RIO])\n+\n+  def whenExists(thunk: => RIO[Unit]): RIO[Unit] =\n+    onExists(thunk, RIO.unit)\n+\n+  def doesExist[A](error: String, thunk: => RIO[A]): RIO[A] =\n+    onExists(thunk, RIO.failIO(error))\n+\n+  def doesNotExist[A](error: String, thunk: => RIO[A]): RIO[A] =\n+    onExists(RIO.failIO(error), thunk)\n+\n+  def delete: RIO[Unit] =\n+    whenExists(RIO.safe(toFile.delete))\n+\n+  def read: RIO[Option[String]] =\n+    readWithEncoding(Codec.UTF8)\n+\n+  def readOrFail: RIO[String] =\n+    read.flatMap(RIO.fromOption(_, s\"Failed to read file - LocalFile($path) does not exist\"))\n+\n+  def readWithEncoding(encoding: Codec): RIO[Option[String]] =\n+    optionExists(RIO.using(this.toInputStream) { in => Streams.readWithEncoding(in, encoding) })\n+\n+  def readLines: RIO[Option[List[String]]] =\n+    readLinesWithEncoding(Codec.UTF8)\n+\n+  def readLinesWithEncoding(encoding: Codec): RIO[Option[List[String]]] =\n+    optionExists(readPerLineWithEncoding(encoding, scala.collection.mutable.ListBuffer[String]())((s, b) => { b += s; b }).map(_.toList))\n+\n+  def readUnsafe(f: java.io.InputStream => RIO[Unit]): RIO[Unit] =\n+    RIO.using(this.toInputStream)(f)\n+\n+  def doPerLine[A](f: String => RIO[Unit]): RIO[Unit] =\n+    RIO.using(this.toInputStream)(in =>\n+      RIO.io {\n+        val reader = new java.io.BufferedReader(new java.io.InputStreamReader(in, \"UTF-8\"))\n+        var line: String = null\n+        var result: RIO[Unit] = null\n+        while ({ line = reader.readLine; line != null && result == null })\n+          f(line).unsafePerformIO match {\n+            case Ok(_) =>\n+              ()\n+            case e @ Error(_) =>\n+              result = RIO.result[Unit](e)\n+          }\n+        }\n+      )\n+\n+  def readPerLine[A](empty: => A)(f: (String, A) => A): RIO[A] =\n+    readPerLineWithEncoding(Codec.UTF8, empty)(f)\n+\n+  def readPerLineWithEncoding[A](codec: Codec, empty: => A)(f: (String, A) => A): RIO[A] =\n+    RIO.io(empty).flatMap { s =>\n+      var state = s\n+      readUnsafe { in => RIO.io {\n+        val reader = new java.io.BufferedReader(new java.io.InputStreamReader(in, codec.name))\n+        var line: String = null\n+        while ({ line = reader.readLine; line != null })\n+          state = f(line, state)\n+      }}.as(state)\n+    }\n+\n+  def readBytes: RIO[Option[Array[Byte]]] =\n+    optionExists(RIO.using(this.toInputStream)(Streams.readBytes(_)))\n+\n+  def writeStream(content: InputStream): RIO[Unit] = for {\n+    _ <- toLocalPath.dirname.mkdirs\n+    _ <- RIO.using(path.toOutputStream)(Streams.pipe(content, _))\n+  } yield ()\n+\n+  def writeWithMode(content: String, mode: WriteMode): RIO[Unit] =\n+    mode.fold(append(content), overwrite(content), toLocalPath.write(content)).void\n+\n+  def writeWithEncodingMode(content: String, encoding: Codec, mode: WriteMode): RIO[Unit] =\n+    mode.fold(\n+        appendWithEncoding(content, encoding)\n+      , overwriteWithEncoding(content, encoding)\n+      , toLocalPath.writeWithEncoding(content, encoding)).void\n+\n+  def writeLinesWithMode(content: List[String], mode: WriteMode\n+): RIO[Unit] =\n+    mode.fold(appendLines(content), overwriteLines(content), toLocalPath.writeLines(content)).void\n+\n+  def writeLinesWithEncodingMode(content: List[String], encoding: Codec, mode: WriteMode): RIO[Unit] =\n+    mode.fold(\n+        appendLinesWithEncoding(content, encoding)\n+      , overwriteLinesWithEncoding(content, encoding)\n+      , toLocalPath.writeLinesWithEncoding(content, encoding)).void\n+\n+  def writeBytesWithMode(content: Array[Byte], mode: WriteMode): RIO[Unit] =\n+    mode.fold(appendBytes(content), overwriteBytes(content), toLocalPath.writeBytes(content)).void\n+\n+  def append(content: String): RIO[Unit] =\n+    appendWithEncoding(content, Codec.UTF8)\n+\n+  def appendWithEncoding(content: String, encoding: Codec): RIO[Unit] =\n+    RIO.using(path.toOutputStream)(out => Streams.writeWithEncoding(out, content, encoding))\n+\n+  def appendLines(content: List[String]): RIO[Unit] =\n+    appendLinesWithEncoding(content, Codec.UTF8)\n+\n+  def appendLinesWithEncoding(content: List[String], encoding: Codec): RIO[Unit] =\n+    appendWithEncoding(Lists.prepareForFile(content), encoding)\n+\n+  def appendBytes(content: Array[Byte]): RIO[Unit] =\n+    RIO.using(path.toOutputStream)(Streams.writeBytes(_, content))\n+\n+  def overwrite(content: String): RIO[Unit] =\n+    overwriteWithEncoding(content, Codec.UTF8)\n+\n+  def overwriteWithEncoding(content: String, encoding: Codec): RIO[Unit] =\n+    RIO.using(path.toOverwriteOutputStream)(out => Streams.writeWithEncoding(out, content, encoding))\n+\n+  def overwriteLines(content: List[String]): RIO[Unit] =\n+    overwriteLinesWithEncoding(content, Codec.UTF8)\n+\n+  def overwriteLinesWithEncoding(content: List[String], encoding: Codec): RIO[Unit] =\n+    overwriteWithEncoding(Lists.prepareForFile(content), encoding)\n+\n+  def overwriteBytes(content: Array[Byte]): RIO[Unit] =\n+    RIO.using(path.toOverwriteOutputStream)(Streams.writeBytes(_, content))\n+\n+  def move(destination: LocalPath): RIO[LocalFile] =\n+    doesExist(s\"Source file does not exist. LocalFile($path)\",\n+      destination.doesNotExist(s\"A file/directory exists in target location $destination. Can not move source file LocalFile($path)\",\n+        RIO.safe {\n+          val destFile = destination.toFile\n+          destination.dirname.toFile.mkdirs\n+          this.toFile.renameTo(destFile)\n+        }.as(LocalFile.unsafe(destination.path.path))))\n+\n+  def moveWithMode(destination: LocalPath, mode: TargetMode): RIO[LocalFile] =\n+    mode.fold(doesExist(s\"Source file does not exist. LocalFile($path)\",\n+      RIO.safe {\n+        val destFile = destination.toFile\n+        destination.dirname.toFile.mkdirs\n+        this.toFile.renameTo(destFile)\n+      }.as(LocalFile.unsafe(destination.path.path))), move(destination))\n+\n+  def moveTo(destination: LocalDirectory): RIO[LocalFile] =\n+    (path.basename match {\n+      case None =>\n+        RIO.fail(s\"Source is a top level directory, can't move. Source($path)\")\n+      case Some(filename) =>\n+        move(destination.toLocalPath | filename)\n+    })\n+\n+  def copy(destination: LocalPath): RIO[LocalFile] =\n+    doesExist(s\"Source file does not exist. LocalFile($path)\",\n+      destination.doesNotExist(s\"A file/directory exists in target location $destination. Can not move source file LocalFile($path)\",\n+        destination.dirname.mkdirs >>\n+          RIO.using(RIO.safe[InputStream](new FileInputStream(toFile)))(destination.writeStream(_))).as(LocalFile.unsafe(destination.path.path)))\n+\n+  def copyWithMode(destination: LocalPath, mode: TargetMode): RIO[LocalFile] =\n+    mode.fold(doesExist(s\"Source file does not exist. LocalFile($path)\",\n+      destination.dirname.mkdirs >>\n+        RIO.using(RIO.safe[InputStream](new FileInputStream(toFile)))(destination.overwriteStream(_))).as(LocalFile.unsafe(destination.path.path)),\n+      copy(destination))\n+\n+  def copyTo(destination: LocalDirectory): RIO[LocalFile] =\n+    path.basename match {\n+      case None =>\n+        RIO.fail(s\"Source is a top level directory, can't copy. Source($path)\")\n+      case Some(filename) =>\n+        val destLocalFile = destination.toLocalPath | filename\n+        RIO.using(RIO.safe[InputStream](new FileInputStream(toFile)))(destLocalFile.writeStream(_)).as(\n+          LocalFile.unsafe(destLocalFile.path.path))\n+    }\n+}\n+\n+object LocalFile {\n+  /** Construct a path from a java.io.File. */\n+  def fromFile(f: File): LocalFile =\n+    Option(f.getParentFile) match {\n+      case None =>\n+        val base = if (f.isAbsolute) new LocalFile(Root) else new LocalFile(Relative)\n+        if (f.getName.isEmpty) base else new LocalFile(Components(base.path, Component.unsafe(f.getName)))\n+      case Some(p) =>\n+        new LocalFile(fromFile(p).path | Component.unsafe(f.getName))\n+    }\n+\n+  def fromString(s: String): Option[LocalFile] =\n+    s.split(\"/\").toList match {","path":"mundane-io/src/main/scala/com/ambiata/mundane/io/LocalFile.scala","position":222,"original_position":222,"commit_id":"ca942dfd319bbe3db55a31b2a103fb94eed2530f","original_commit_id":"ca942dfd319bbe3db55a31b2a103fb94eed2530f","user":{"login":"markhibberd","id":22832,"avatar_url":"https://avatars.githubusercontent.com/u/22832?v=3","gravatar_id":"","url":"https://api.github.com/users/markhibberd","html_url":"https://github.com/markhibberd","followers_url":"https://api.github.com/users/markhibberd/followers","following_url":"https://api.github.com/users/markhibberd/following{/other_user}","gists_url":"https://api.github.com/users/markhibberd/gists{/gist_id}","starred_url":"https://api.github.com/users/markhibberd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/markhibberd/subscriptions","organizations_url":"https://api.github.com/users/markhibberd/orgs","repos_url":"https://api.github.com/users/markhibberd/repos","events_url":"https://api.github.com/users/markhibberd/events{/privacy}","received_events_url":"https://api.github.com/users/markhibberd/received_events","type":"User","site_admin":false},"body":"Could this just use the `Path` code and wrap in LocalFile.","created_at":"2015-02-04T10:47:14Z","updated_at":"2015-02-04T10:47:14Z","html_url":"https://github.com/ambiata/mundane/pull/71#discussion_r24075139","pull_request_url":"https://api.github.com/repos/ambiata/mundane/pulls/71","_links":{"self":{"href":"https://api.github.com/repos/ambiata/mundane/pulls/comments/24075139"},"html":{"href":"https://github.com/ambiata/mundane/pull/71#discussion_r24075139"},"pull_request":{"href":"https://api.github.com/repos/ambiata/mundane/pulls/71"}}},"pull_request":{"url":"https://api.github.com/repos/ambiata/mundane/pulls/71","id":26117242,"html_url":"https://github.com/ambiata/mundane/pull/71","diff_url":"https://github.com/ambiata/mundane/pull/71.diff","patch_url":"https://github.com/ambiata/mundane/pull/71.patch","issue_url":"https://api.github.com/repos/ambiata/mundane/issues/71","number":71,"state":"open","locked":false,"title":"WIP: Topic/path","user":{"login":"nhibberd","id":447873,"avatar_url":"https://avatars.githubusercontent.com/u/447873?v=3","gravatar_id":"","url":"https://api.github.com/users/nhibberd","html_url":"https://github.com/nhibberd","followers_url":"https://api.github.com/users/nhibberd/followers","following_url":"https://api.github.com/users/nhibberd/following{/other_user}","gists_url":"https://api.github.com/users/nhibberd/gists{/gist_id}","starred_url":"https://api.github.com/users/nhibberd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nhibberd/subscriptions","organizations_url":"https://api.github.com/users/nhibberd/orgs","repos_url":"https://api.github.com/users/nhibberd/repos","events_url":"https://api.github.com/users/nhibberd/events{/privacy}","received_events_url":"https://api.github.com/users/nhibberd/received_events","type":"User","site_admin":false},"body":"Ingore this, just for review :car: ","created_at":"2014-12-16T01:43:36Z","updated_at":"2015-02-04T10:47:14Z","closed_at":null,"merged_at":null,"merge_commit_sha":"85f83594d02dca94266d4ee76ed54d103af35651","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ambiata/mundane/pulls/71/commits","review_comments_url":"https://api.github.com/repos/ambiata/mundane/pulls/71/comments","review_comment_url":"https://api.github.com/repos/ambiata/mundane/pulls/comments/{number}","comments_url":"https://api.github.com/repos/ambiata/mundane/issues/71/comments","statuses_url":"https://api.github.com/repos/ambiata/mundane/statuses/ca942dfd319bbe3db55a31b2a103fb94eed2530f","head":{"label":"ambiata:topic/path","ref":"topic/path","sha":"ca942dfd319bbe3db55a31b2a103fb94eed2530f","user":{"login":"ambiata","id":1691023,"avatar_url":"https://avatars.githubusercontent.com/u/1691023?v=3","gravatar_id":"","url":"https://api.github.com/users/ambiata","html_url":"https://github.com/ambiata","followers_url":"https://api.github.com/users/ambiata/followers","following_url":"https://api.github.com/users/ambiata/following{/other_user}","gists_url":"https://api.github.com/users/ambiata/gists{/gist_id}","starred_url":"https://api.github.com/users/ambiata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ambiata/subscriptions","organizations_url":"https://api.github.com/users/ambiata/orgs","repos_url":"https://api.github.com/users/ambiata/repos","events_url":"https://api.github.com/users/ambiata/events{/privacy}","received_events_url":"https://api.github.com/users/ambiata/received_events","type":"Organization","site_admin":false},"repo":{"id":14035086,"name":"mundane","full_name":"ambiata/mundane","owner":{"login":"ambiata","id":1691023,"avatar_url":"https://avatars.githubusercontent.com/u/1691023?v=3","gravatar_id":"","url":"https://api.github.com/users/ambiata","html_url":"https://github.com/ambiata","followers_url":"https://api.github.com/users/ambiata/followers","following_url":"https://api.github.com/users/ambiata/following{/other_user}","gists_url":"https://api.github.com/users/ambiata/gists{/gist_id}","starred_url":"https://api.github.com/users/ambiata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ambiata/subscriptions","organizations_url":"https://api.github.com/users/ambiata/orgs","repos_url":"https://api.github.com/users/ambiata/repos","events_url":"https://api.github.com/users/ambiata/events{/privacy}","received_events_url":"https://api.github.com/users/ambiata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ambiata/mundane","description":"Low-level scala tools for building applications that might actually work ","fork":false,"url":"https://api.github.com/repos/ambiata/mundane","forks_url":"https://api.github.com/repos/ambiata/mundane/forks","keys_url":"https://api.github.com/repos/ambiata/mundane/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ambiata/mundane/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ambiata/mundane/teams","hooks_url":"https://api.github.com/repos/ambiata/mundane/hooks","issue_events_url":"https://api.github.com/repos/ambiata/mundane/issues/events{/number}","events_url":"https://api.github.com/repos/ambiata/mundane/events","assignees_url":"https://api.github.com/repos/ambiata/mundane/assignees{/user}","branches_url":"https://api.github.com/repos/ambiata/mundane/branches{/branch}","tags_url":"https://api.github.com/repos/ambiata/mundane/tags","blobs_url":"https://api.github.com/repos/ambiata/mundane/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ambiata/mundane/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ambiata/mundane/git/refs{/sha}","trees_url":"https://api.github.com/repos/ambiata/mundane/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ambiata/mundane/statuses/{sha}","languages_url":"https://api.github.com/repos/ambiata/mundane/languages","stargazers_url":"https://api.github.com/repos/ambiata/mundane/stargazers","contributors_url":"https://api.github.com/repos/ambiata/mundane/contributors","subscribers_url":"https://api.github.com/repos/ambiata/mundane/subscribers","subscription_url":"https://api.github.com/repos/ambiata/mundane/subscription","commits_url":"https://api.github.com/repos/ambiata/mundane/commits{/sha}","git_commits_url":"https://api.github.com/repos/ambiata/mundane/git/commits{/sha}","comments_url":"https://api.github.com/repos/ambiata/mundane/comments{/number}","issue_comment_url":"https://api.github.com/repos/ambiata/mundane/issues/comments/{number}","contents_url":"https://api.github.com/repos/ambiata/mundane/contents/{+path}","compare_url":"https://api.github.com/repos/ambiata/mundane/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ambiata/mundane/merges","archive_url":"https://api.github.com/repos/ambiata/mundane/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ambiata/mundane/downloads","issues_url":"https://api.github.com/repos/ambiata/mundane/issues{/number}","pulls_url":"https://api.github.com/repos/ambiata/mundane/pulls{/number}","milestones_url":"https://api.github.com/repos/ambiata/mundane/milestones{/number}","notifications_url":"https://api.github.com/repos/ambiata/mundane/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ambiata/mundane/labels{/name}","releases_url":"https://api.github.com/repos/ambiata/mundane/releases{/id}","created_at":"2013-11-01T03:46:02Z","updated_at":"2015-01-07T02:25:13Z","pushed_at":"2015-02-04T05:20:50Z","git_url":"git://github.com/ambiata/mundane.git","ssh_url":"git@github.com:ambiata/mundane.git","clone_url":"https://github.com/ambiata/mundane.git","svn_url":"https://github.com/ambiata/mundane","homepage":"","size":3514,"stargazers_count":6,"watchers_count":6,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":29,"forks":0,"open_issues":29,"watchers":6,"default_branch":"master"}},"base":{"label":"ambiata:master","ref":"master","sha":"a11fc35f45200d0f0989591f47f3fe7f52558469","user":{"login":"ambiata","id":1691023,"avatar_url":"https://avatars.githubusercontent.com/u/1691023?v=3","gravatar_id":"","url":"https://api.github.com/users/ambiata","html_url":"https://github.com/ambiata","followers_url":"https://api.github.com/users/ambiata/followers","following_url":"https://api.github.com/users/ambiata/following{/other_user}","gists_url":"https://api.github.com/users/ambiata/gists{/gist_id}","starred_url":"https://api.github.com/users/ambiata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ambiata/subscriptions","organizations_url":"https://api.github.com/users/ambiata/orgs","repos_url":"https://api.github.com/users/ambiata/repos","events_url":"https://api.github.com/users/ambiata/events{/privacy}","received_events_url":"https://api.github.com/users/ambiata/received_events","type":"Organization","site_admin":false},"repo":{"id":14035086,"name":"mundane","full_name":"ambiata/mundane","owner":{"login":"ambiata","id":1691023,"avatar_url":"https://avatars.githubusercontent.com/u/1691023?v=3","gravatar_id":"","url":"https://api.github.com/users/ambiata","html_url":"https://github.com/ambiata","followers_url":"https://api.github.com/users/ambiata/followers","following_url":"https://api.github.com/users/ambiata/following{/other_user}","gists_url":"https://api.github.com/users/ambiata/gists{/gist_id}","starred_url":"https://api.github.com/users/ambiata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ambiata/subscriptions","organizations_url":"https://api.github.com/users/ambiata/orgs","repos_url":"https://api.github.com/users/ambiata/repos","events_url":"https://api.github.com/users/ambiata/events{/privacy}","received_events_url":"https://api.github.com/users/ambiata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ambiata/mundane","description":"Low-level scala tools for building applications that might actually work ","fork":false,"url":"https://api.github.com/repos/ambiata/mundane","forks_url":"https://api.github.com/repos/ambiata/mundane/forks","keys_url":"https://api.github.com/repos/ambiata/mundane/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ambiata/mundane/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ambiata/mundane/teams","hooks_url":"https://api.github.com/repos/ambiata/mundane/hooks","issue_events_url":"https://api.github.com/repos/ambiata/mundane/issues/events{/number}","events_url":"https://api.github.com/repos/ambiata/mundane/events","assignees_url":"https://api.github.com/repos/ambiata/mundane/assignees{/user}","branches_url":"https://api.github.com/repos/ambiata/mundane/branches{/branch}","tags_url":"https://api.github.com/repos/ambiata/mundane/tags","blobs_url":"https://api.github.com/repos/ambiata/mundane/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ambiata/mundane/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ambiata/mundane/git/refs{/sha}","trees_url":"https://api.github.com/repos/ambiata/mundane/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ambiata/mundane/statuses/{sha}","languages_url":"https://api.github.com/repos/ambiata/mundane/languages","stargazers_url":"https://api.github.com/repos/ambiata/mundane/stargazers","contributors_url":"https://api.github.com/repos/ambiata/mundane/contributors","subscribers_url":"https://api.github.com/repos/ambiata/mundane/subscribers","subscription_url":"https://api.github.com/repos/ambiata/mundane/subscription","commits_url":"https://api.github.com/repos/ambiata/mundane/commits{/sha}","git_commits_url":"https://api.github.com/repos/ambiata/mundane/git/commits{/sha}","comments_url":"https://api.github.com/repos/ambiata/mundane/comments{/number}","issue_comment_url":"https://api.github.com/repos/ambiata/mundane/issues/comments/{number}","contents_url":"https://api.github.com/repos/ambiata/mundane/contents/{+path}","compare_url":"https://api.github.com/repos/ambiata/mundane/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ambiata/mundane/merges","archive_url":"https://api.github.com/repos/ambiata/mundane/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ambiata/mundane/downloads","issues_url":"https://api.github.com/repos/ambiata/mundane/issues{/number}","pulls_url":"https://api.github.com/repos/ambiata/mundane/pulls{/number}","milestones_url":"https://api.github.com/repos/ambiata/mundane/milestones{/number}","notifications_url":"https://api.github.com/repos/ambiata/mundane/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ambiata/mundane/labels{/name}","releases_url":"https://api.github.com/repos/ambiata/mundane/releases{/id}","created_at":"2013-11-01T03:46:02Z","updated_at":"2015-01-07T02:25:13Z","pushed_at":"2015-02-04T05:20:50Z","git_url":"git://github.com/ambiata/mundane.git","ssh_url":"git@github.com:ambiata/mundane.git","clone_url":"https://github.com/ambiata/mundane.git","svn_url":"https://github.com/ambiata/mundane","homepage":"","size":3514,"stargazers_count":6,"watchers_count":6,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":29,"forks":0,"open_issues":29,"watchers":6,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ambiata/mundane/pulls/71"},"html":{"href":"https://github.com/ambiata/mundane/pull/71"},"issue":{"href":"https://api.github.com/repos/ambiata/mundane/issues/71"},"comments":{"href":"https://api.github.com/repos/ambiata/mundane/issues/71/comments"},"review_comments":{"href":"https://api.github.com/repos/ambiata/mundane/pulls/71/comments"},"review_comment":{"href":"https://api.github.com/repos/ambiata/mundane/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/ambiata/mundane/pulls/71/commits"},"statuses":{"href":"https://api.github.com/repos/ambiata/mundane/statuses/ca942dfd319bbe3db55a31b2a103fb94eed2530f"}}}}