{"action":"created","comment":{"url":"https://api.github.com/repos/47deg/fetch/pulls/comments/63501003","id":63501003,"diff_hunk":"@@ -0,0 +1,598 @@\n+---\n+layout: docs\n+---\n+\n+# Introduction\n+\n+Fetch is a library that allows your data fetches to be written in a concise,\n+composable way while executing efficiently. You don't need to use any explicit\n+concurrency construct but existing idioms: applicative for concurrency and\n+monad for sequencing.\n+\n+Oftentimes, our applications read and manipulate data from a variety of\n+different sources such as databases, web services or file systems. These data\n+sources are subject to latency, and we'd prefer to query them efficiently.\n+\n+If we are just reading data, we can make a series of optimizations such as:\n+\n+ - batching requests to the same data source\n+ - requesting independent data from different sources in parallel\n+ - caching previously seen results\n+\n+However, if we mix these optimizations with the code that fetches the data\n+we may end up trading clarity for performance. Furthermore, we are\n+mixing low-level (optimization) and high-level (business logic with the data\n+we read) concerns.\n+\n+# Installation\n+\n+First of all, add the following dependency to your SBT build file:\n+\n+```scala\n+\"com.fortysevendeg\" %%% \"fetch\" % \"0.1.0\"\n+```\n+\n+Now you'll have Fetch available both in Scala and Scala.js.\n+\n+# Usage\n+\n+In order to tell Fetch how to retrieve data, we must implement the `DataSource` typeclass.\n+\n+```scala\n+trait DataSource[Identity, Result]{\n+  def fetch(ids: List[Identity]): Eval[Map[Identity, Result]]\n+}\n+```\n+\n+It takes two type parameters:\n+\n+ - `Identity`: the identity we want to fetch (a `UserId` if we were fetching users)\n+ - `Result`: the type of the data we retrieve (a `User` if we were fetching users)\n+\n+The `fetch` method takes a non empty list of identities and must return an `Eval` that will result\n+in a map from identities to results. Accepting a list of identities gives Fetch the ability to batch requests to\n+the same data source, and returning a mapping from identities to results Fetch can detect whenever an identity\n+couldn't be fetched or no longer exists.\n+\n+Returning `Eval` makes it possible to defer evaluation with a concurrency monad when running a fetch.\n+\n+## Writing your first data source\n+\n+Now that we know about the `DataSource` typeclass, let's write our first data source! We'll start implementing a data\n+source for fetching users given their id. The identity type will be a user's id and the result type a user.\n+\n+```scala\n+import cats.Eval\n+\n+import fetch._\n+\n+type UserId = Int\n+case class User(id: UserId, username: String)\n+\n+implicit object UserSource extends DataSource[UserId, User]{\n+  override def fetch(ids: List[UserId]): Eval[Map[UserId, User]] = {\n+    Eval.later({\n+      println(s\"Fetching users $ids\")\n+\t  ids.map(i => (i, User(i, s\"@egg_$i\"))).toMap\n+    })\n+  }\n+}\n+```\n+\n+Now that we have a data source we can write a function for fetching users\n+given an id, we just have to pass a `UserId` as an argument to `Fetch`.\n+\n+```scala\n+def getUser(id: UserId): Fetch[User] = Fetch(id) // or, more explicitly: Fetch(id)(UserSource)\n+```\n+\n+## Declaring and running a fetch\n+\n+We are now ready to declare and run fetches. We need to provide Fetch with a target\n+monad when we want to execute a fetch. We'll be using `Id` for now, make sure to import\n+`fetch.implicits._` since Fetch needs an instance of `MonadError[Id, Throwable]` for running\n+a fetch in the `Id` monad:\n+\n+```scala\n+import cats.Id\n+import fetch.implicits._\n+\n+val fch: Fetch[User] = getUser(1)\n+\n+val result: User = Fetch.run[Id](fch)\n+// Fetching users List(1)\n+\n+//=> result: User = User(1,@egg_1)\n+```\n+\n+In the previous example, we:\n+\n+- brought the implicit instance of `MonadError[Id, Throwable]` into scope importing `fetch.implicits._`\n+- created a fetch for a `User` using the `getUser` function\n+- interpreted the fetch to a `Id[User]` (which is just a `User`) using `Fetch.run`\n+\n+As you can see, the fetch was executed only in one round to fetch the user and was finished after that.\n+\n+### Sequencing\n+\n+When we have two fetches that depend on each other, we can use `flatMap` to combine them. When composing fetches with `flatMap` we are telling Fetch that the second one depends on the previous one, so it isn't able to make any optimizations.\n+\n+```scala\n+val fch: Fetch[(User, User)] = for {\n+  aUser <- getUser(1)\n+  anotherUser <- getUser(aUser.id + 1)\n+} yield (aUser, anotherUser)\n+\n+val result: (User, User) = Fetch.run[Id](fch)\n+// Fetching users List(1)\n+// Fetching users List(2)\n+\n+//=> result: (User, User) = (User(1,@egg_1),User(2,@egg_2))\n+```\n+\n+### Batching\n+\n+If we combine two independent requests to the same data source, Fetch will\n+automatically batch them together into a single request. Fetch's `join` operator\n+help us tell the library that two fetches are independent, and thus can be batched\n+if they use the same data source:\n+\n+```scala\n+import cats.syntax.cartesian._\n+\n+val fch: Fetch[(User, User)] = getUser(1).product(getUser(2))\n+\n+val result: (User, User) = Fetch.run[Id](fch)\n+// Fetching users List(1, 2)\n+\n+//=> result: (User, User) = (User(1,@egg_1),User(2,@egg_2))\n+```\n+\n+### Deduplication\n+\n+If two independent requests ask for the same identity, Fetch will detect that and\n+deduplicate such id.\n+\n+```scala\n+val fch: Fetch[(User, User)] = getUser(1).product(getUser(1))\n+\n+val result: (User, User) = Fetch.run[Id](fch)\n+// Fetching users List(1)\n+\n+//=> result: (User, User) = (User(1,@egg_1),User(1,@egg_1))\n+```\n+\n+### Caching\n+\n+During a fetch, previously requested results are implicitly cached. This allows us to write\n+fetches in a very modular way, asking for all the data they need as if it\n+was in memory; furthermore, it also avoids refetching an identity that may have changed\n+during the course of a fetch execution, which can lead to inconsistencies in the data.\n+\n+```scala\n+val fch: Fetch[(User, User)] = for {\n+  aUser <- getUser(1)\n+  anotherUser <- getUser(1)\n+} yield (aUser, anotherUser)\n+\n+val result: (User, User) = Fetch.run[Id](fch)\n+// Fetching users List(1)\n+\n+//=> result: (User, User) = (User(1,@egg_1),User(1,@egg_1))\n+```\n+\n+As you can see, the `User` with id 1 was fetched only once in a single round-trip. The next\n+time it was needed we used the cached versions, thus avoiding another request to the user data\n+source.\n+\n+## Combining data from multiple sources\n+\n+Now that we know about some of the optimizations that Fetch can make to read data efficiently,\n+let's look at how we can combine more than one data source. Imagine that we are rendering a blog\n+and have the following types for posts and post information:\n+\n+```scala\n+type PostId = Int\n+case class Post(id: PostId, author: UserId, content: String)\n+case class PostInfo(topic: String)\n+```\n+\n+As you can see, every `Post` has an author, but it refers to it by its id. We'll implement two data sources:\n+\n+- one for retrieving a post given a post id\n+- another for retrieveng post metadata given a post id\n+\n+```scala\n+implicit object PostSource extends DataSource[PostId, Post]{\n+  override def fetch(ids: List[PostId]): Eval[Map[PostId, Post]] = {\n+    Eval.later({\n+      println(s\"Fetching posts $ids\")\n+      ids.map(i => (i, Post(i, i % 3 + 1, s\"An article with id $i\"))).toMap\n+    })\n+  }\n+}\n+\n+def getPost(id: PostId): Fetch[Post] = Fetch(id)\n+\n+implicit object PostInfoSource extends DataSource[PostId, PostInfo]{\n+  override def fetch(ids: List[PostId]): Eval[Map[PostId, PostInfo]] = {\n+    Eval.later({\n+      println(s\"Fetching post info $ids\")\n+      ids.map(i => (i, PostInfo(if (i % 2 == 0) \"applicative\" else \"monad\"))).toMap\n+    })\n+  }\n+}\n+\n+def getPostInfo(id: PostId): Fetch[PostInfo] = Fetch(id)\n+```\n+\n+We can also implement a function for fetching a post's author given a post:\n+\n+```scala\n+def getAuthor(p: Post): Fetch[User] = Fetch(p.author)\n+```\n+\n+Now that we have multiple sources, let's mix them in the same fetch.\n+\n+```scala\n+val fch: Fetch[(Post, User)] = for {\n+  post <- getPost(1)\n+  user <- getAuthor(post)\n+} yield (post, user)\n+\n+val result: (Post, User) = Fetch.run[Id](fch)\n+// Fetching posts List(1)\n+// Fetching users List(2)\n+\n+//=> result: (Post, User) = (Post(1,2,An article with id 1),User(2,@egg_2))\n+```\n+\n+In the previous example we fetched a post given its id, and then fetched its author. These\n+data could come from entirely different places but Fetch makes working with heterogeneous sources\n+of data very easy.\n+\n+### Parallelism\n+\n+Combining multiple independent requests to the same data source results in batching but what happens\n+when combining independent requests to *different* data sources? We have the oportunity to query the\n+different data sources at the same time.\n+\n+```scala\n+val fch: Fetch[(Post, User)] = getPost(1).product(getUser(2))\n+\n+val result: (Post, User) = Fetch.run[Id](fch)\n+// Fetching posts List(1)\n+// Fetching users List(2)\n+\n+//=> result: (Post, User) = (Post(1,2,An article with id 1),User(2,@egg_2))\n+```\n+\n+Since we are interpreting the fetch to the `Id` monad, that doesn't give us any concurrency, the fetches\n+will be run sequentially. However, if we interpret it to a `Future` each request will run in its own logical\n+thread.\n+\n+## Combinators\n+\n+Besides `flatMap` for sequencing fetches and `product` for running them concurrently, Fetch provides a number of\n+other combinators.\n+\n+### Sequence\n+\n+Whenever we have a list of fetches of the same type and want to run them concurrently we can use the `sequence`\n+combinator. It takes a `List[Fetch[A]]` and gives you back a `Fetch[List[A]]`, batching the fetches to the same\n+data source and running fetches to different sources in parallel.\n+\n+Note that the `sequence` combinator is more general and not only works on lists but any type that has a [`Traverse`](http://typelevel.org/cats/tut/traverse.html) instance.\n+\n+```scala\n+import cats.std.list._\n+import cats.syntax.traverse._\n+\n+val fch: Fetch[List[User]] = List(getUser(1), getUser(2), getUser(3)).sequence\n+\n+val result: List[User] = Fetch.run[Id](fch)\n+// Fetching users List(1, 2, 3)\n+\n+//=> result: List[User] = List(User(1,@egg_1), User(2,@egg_2), User(3,@egg_3))\n+```\n+\n+As you can see, requests to the user data source were batched, thus fetching all the data in one round.\n+\n+### Traverse\n+\n+Another interesing combinator is `traverse`, which is the composition of `map` and `sequence`.\n+\n+```scala\n+val fch: Fetch[List[User]] = List(1, 2, 3).traverse(getUser)\n+\n+val result: List[User] = Fetch.run[Id](fch)\n+// Fetching users List(1, 2, 3)\n+\n+//=> result: List[User] = List(User(1,@egg_1), User(2,@egg_2), User(3,@egg_3))\n+```\n+\n+# Interpreting a fetch to a concurrency monad\n+\n+Albeit the examples use `Id` as the concurrency Monad, `Fetch` is not limited to just `Id`, any monad `M` that\n+implements `MonadError[M, Throwable]` will do. Fetch provides `MonadError` instances for some existing monads like\n+`Future`, `cats.Id` and `cats.Eval` and is easy to write your own.\n+\n+In practice you'll be interpreting a fetch to a concurrency monad like `Future` or `scalaz.concurrent.Task` to exploit","path":"docs/src/jekyll/docs.md","position":320,"original_position":320,"commit_id":"7096c32b994cf5ae7e6cbddec2d16da3d93bd34a","original_commit_id":"2eb8b0ede84eed400d6590a3c4a9e0a93b7b9508","user":{"login":"raulraja","id":456796,"avatar_url":"https://avatars.githubusercontent.com/u/456796?v=3","gravatar_id":"","url":"https://api.github.com/users/raulraja","html_url":"https://github.com/raulraja","followers_url":"https://api.github.com/users/raulraja/followers","following_url":"https://api.github.com/users/raulraja/following{/other_user}","gists_url":"https://api.github.com/users/raulraja/gists{/gist_id}","starred_url":"https://api.github.com/users/raulraja/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/raulraja/subscriptions","organizations_url":"https://api.github.com/users/raulraja/orgs","repos_url":"https://api.github.com/users/raulraja/repos","events_url":"https://api.github.com/users/raulraja/events{/privacy}","received_events_url":"https://api.github.com/users/raulraja/received_events","type":"User","site_admin":false},"body":"If we namespace Task we should namespace Future too as scalaz.concurrent also includes a lower primitive type called `Future` which is contained inside `Task`","created_at":"2016-05-17T10:43:38Z","updated_at":"2016-05-17T10:43:38Z","html_url":"https://github.com/47deg/fetch/pull/24#discussion_r63501003","pull_request_url":"https://api.github.com/repos/47deg/fetch/pulls/24","_links":{"self":{"href":"https://api.github.com/repos/47deg/fetch/pulls/comments/63501003"},"html":{"href":"https://github.com/47deg/fetch/pull/24#discussion_r63501003"},"pull_request":{"href":"https://api.github.com/repos/47deg/fetch/pulls/24"}}},"pull_request":{"url":"https://api.github.com/repos/47deg/fetch/pulls/24","id":70182636,"html_url":"https://github.com/47deg/fetch/pull/24","diff_url":"https://github.com/47deg/fetch/pull/24.diff","patch_url":"https://github.com/47deg/fetch/pull/24.patch","issue_url":"https://api.github.com/repos/47deg/fetch/issues/24","number":24,"state":"open","locked":false,"title":"Documentation","user":{"login":"dialelo","id":409039,"avatar_url":"https://avatars.githubusercontent.com/u/409039?v=3","gravatar_id":"","url":"https://api.github.com/users/dialelo","html_url":"https://github.com/dialelo","followers_url":"https://api.github.com/users/dialelo/followers","following_url":"https://api.github.com/users/dialelo/following{/other_user}","gists_url":"https://api.github.com/users/dialelo/gists{/gist_id}","starred_url":"https://api.github.com/users/dialelo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dialelo/subscriptions","organizations_url":"https://api.github.com/users/dialelo/orgs","repos_url":"https://api.github.com/users/dialelo/repos","events_url":"https://api.github.com/users/dialelo/events{/privacy}","received_events_url":"https://api.github.com/users/dialelo/received_events","type":"User","site_admin":false},"body":"@raulraja take a look and let me know your suggestions please.","created_at":"2016-05-16T11:17:26Z","updated_at":"2016-05-17T10:43:38Z","closed_at":null,"merged_at":null,"merge_commit_sha":"93951df47c2a6b1e25a98cb1240c732d79247ad7","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/47deg/fetch/pulls/24/commits","review_comments_url":"https://api.github.com/repos/47deg/fetch/pulls/24/comments","review_comment_url":"https://api.github.com/repos/47deg/fetch/pulls/comments{/number}","comments_url":"https://api.github.com/repos/47deg/fetch/issues/24/comments","statuses_url":"https://api.github.com/repos/47deg/fetch/statuses/7096c32b994cf5ae7e6cbddec2d16da3d93bd34a","head":{"label":"47deg:docs","ref":"docs","sha":"7096c32b994cf5ae7e6cbddec2d16da3d93bd34a","user":{"login":"47deg","id":479857,"avatar_url":"https://avatars.githubusercontent.com/u/479857?v=3","gravatar_id":"","url":"https://api.github.com/users/47deg","html_url":"https://github.com/47deg","followers_url":"https://api.github.com/users/47deg/followers","following_url":"https://api.github.com/users/47deg/following{/other_user}","gists_url":"https://api.github.com/users/47deg/gists{/gist_id}","starred_url":"https://api.github.com/users/47deg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/47deg/subscriptions","organizations_url":"https://api.github.com/users/47deg/orgs","repos_url":"https://api.github.com/users/47deg/repos","events_url":"https://api.github.com/users/47deg/events{/privacy}","received_events_url":"https://api.github.com/users/47deg/received_events","type":"Organization","site_admin":false},"repo":{"id":53124080,"name":"fetch","full_name":"47deg/fetch","owner":{"login":"47deg","id":479857,"avatar_url":"https://avatars.githubusercontent.com/u/479857?v=3","gravatar_id":"","url":"https://api.github.com/users/47deg","html_url":"https://github.com/47deg","followers_url":"https://api.github.com/users/47deg/followers","following_url":"https://api.github.com/users/47deg/following{/other_user}","gists_url":"https://api.github.com/users/47deg/gists{/gist_id}","starred_url":"https://api.github.com/users/47deg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/47deg/subscriptions","organizations_url":"https://api.github.com/users/47deg/orgs","repos_url":"https://api.github.com/users/47deg/repos","events_url":"https://api.github.com/users/47deg/events{/privacy}","received_events_url":"https://api.github.com/users/47deg/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/47deg/fetch","description":"[WIP] Efficient, Concurrent and Concise remote data access for Scala","fork":false,"url":"https://api.github.com/repos/47deg/fetch","forks_url":"https://api.github.com/repos/47deg/fetch/forks","keys_url":"https://api.github.com/repos/47deg/fetch/keys{/key_id}","collaborators_url":"https://api.github.com/repos/47deg/fetch/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/47deg/fetch/teams","hooks_url":"https://api.github.com/repos/47deg/fetch/hooks","issue_events_url":"https://api.github.com/repos/47deg/fetch/issues/events{/number}","events_url":"https://api.github.com/repos/47deg/fetch/events","assignees_url":"https://api.github.com/repos/47deg/fetch/assignees{/user}","branches_url":"https://api.github.com/repos/47deg/fetch/branches{/branch}","tags_url":"https://api.github.com/repos/47deg/fetch/tags","blobs_url":"https://api.github.com/repos/47deg/fetch/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/47deg/fetch/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/47deg/fetch/git/refs{/sha}","trees_url":"https://api.github.com/repos/47deg/fetch/git/trees{/sha}","statuses_url":"https://api.github.com/repos/47deg/fetch/statuses/{sha}","languages_url":"https://api.github.com/repos/47deg/fetch/languages","stargazers_url":"https://api.github.com/repos/47deg/fetch/stargazers","contributors_url":"https://api.github.com/repos/47deg/fetch/contributors","subscribers_url":"https://api.github.com/repos/47deg/fetch/subscribers","subscription_url":"https://api.github.com/repos/47deg/fetch/subscription","commits_url":"https://api.github.com/repos/47deg/fetch/commits{/sha}","git_commits_url":"https://api.github.com/repos/47deg/fetch/git/commits{/sha}","comments_url":"https://api.github.com/repos/47deg/fetch/comments{/number}","issue_comment_url":"https://api.github.com/repos/47deg/fetch/issues/comments{/number}","contents_url":"https://api.github.com/repos/47deg/fetch/contents/{+path}","compare_url":"https://api.github.com/repos/47deg/fetch/compare/{base}...{head}","merges_url":"https://api.github.com/repos/47deg/fetch/merges","archive_url":"https://api.github.com/repos/47deg/fetch/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/47deg/fetch/downloads","issues_url":"https://api.github.com/repos/47deg/fetch/issues{/number}","pulls_url":"https://api.github.com/repos/47deg/fetch/pulls{/number}","milestones_url":"https://api.github.com/repos/47deg/fetch/milestones{/number}","notifications_url":"https://api.github.com/repos/47deg/fetch/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/47deg/fetch/labels{/name}","releases_url":"https://api.github.com/repos/47deg/fetch/releases{/id}","deployments_url":"https://api.github.com/repos/47deg/fetch/deployments","created_at":"2016-03-04T09:32:16Z","updated_at":"2016-05-17T00:24:28Z","pushed_at":"2016-05-17T10:41:21Z","git_url":"git://github.com/47deg/fetch.git","ssh_url":"git@github.com:47deg/fetch.git","clone_url":"https://github.com/47deg/fetch.git","svn_url":"https://github.com/47deg/fetch","homepage":null,"size":151,"stargazers_count":18,"watchers_count":18,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":5,"mirror_url":null,"open_issues_count":12,"forks":5,"open_issues":12,"watchers":18,"default_branch":"master"}},"base":{"label":"47deg:master","ref":"master","sha":"8c3d3d28df306b10c80156150ea3e5a2ef0326dc","user":{"login":"47deg","id":479857,"avatar_url":"https://avatars.githubusercontent.com/u/479857?v=3","gravatar_id":"","url":"https://api.github.com/users/47deg","html_url":"https://github.com/47deg","followers_url":"https://api.github.com/users/47deg/followers","following_url":"https://api.github.com/users/47deg/following{/other_user}","gists_url":"https://api.github.com/users/47deg/gists{/gist_id}","starred_url":"https://api.github.com/users/47deg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/47deg/subscriptions","organizations_url":"https://api.github.com/users/47deg/orgs","repos_url":"https://api.github.com/users/47deg/repos","events_url":"https://api.github.com/users/47deg/events{/privacy}","received_events_url":"https://api.github.com/users/47deg/received_events","type":"Organization","site_admin":false},"repo":{"id":53124080,"name":"fetch","full_name":"47deg/fetch","owner":{"login":"47deg","id":479857,"avatar_url":"https://avatars.githubusercontent.com/u/479857?v=3","gravatar_id":"","url":"https://api.github.com/users/47deg","html_url":"https://github.com/47deg","followers_url":"https://api.github.com/users/47deg/followers","following_url":"https://api.github.com/users/47deg/following{/other_user}","gists_url":"https://api.github.com/users/47deg/gists{/gist_id}","starred_url":"https://api.github.com/users/47deg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/47deg/subscriptions","organizations_url":"https://api.github.com/users/47deg/orgs","repos_url":"https://api.github.com/users/47deg/repos","events_url":"https://api.github.com/users/47deg/events{/privacy}","received_events_url":"https://api.github.com/users/47deg/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/47deg/fetch","description":"[WIP] Efficient, Concurrent and Concise remote data access for Scala","fork":false,"url":"https://api.github.com/repos/47deg/fetch","forks_url":"https://api.github.com/repos/47deg/fetch/forks","keys_url":"https://api.github.com/repos/47deg/fetch/keys{/key_id}","collaborators_url":"https://api.github.com/repos/47deg/fetch/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/47deg/fetch/teams","hooks_url":"https://api.github.com/repos/47deg/fetch/hooks","issue_events_url":"https://api.github.com/repos/47deg/fetch/issues/events{/number}","events_url":"https://api.github.com/repos/47deg/fetch/events","assignees_url":"https://api.github.com/repos/47deg/fetch/assignees{/user}","branches_url":"https://api.github.com/repos/47deg/fetch/branches{/branch}","tags_url":"https://api.github.com/repos/47deg/fetch/tags","blobs_url":"https://api.github.com/repos/47deg/fetch/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/47deg/fetch/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/47deg/fetch/git/refs{/sha}","trees_url":"https://api.github.com/repos/47deg/fetch/git/trees{/sha}","statuses_url":"https://api.github.com/repos/47deg/fetch/statuses/{sha}","languages_url":"https://api.github.com/repos/47deg/fetch/languages","stargazers_url":"https://api.github.com/repos/47deg/fetch/stargazers","contributors_url":"https://api.github.com/repos/47deg/fetch/contributors","subscribers_url":"https://api.github.com/repos/47deg/fetch/subscribers","subscription_url":"https://api.github.com/repos/47deg/fetch/subscription","commits_url":"https://api.github.com/repos/47deg/fetch/commits{/sha}","git_commits_url":"https://api.github.com/repos/47deg/fetch/git/commits{/sha}","comments_url":"https://api.github.com/repos/47deg/fetch/comments{/number}","issue_comment_url":"https://api.github.com/repos/47deg/fetch/issues/comments{/number}","contents_url":"https://api.github.com/repos/47deg/fetch/contents/{+path}","compare_url":"https://api.github.com/repos/47deg/fetch/compare/{base}...{head}","merges_url":"https://api.github.com/repos/47deg/fetch/merges","archive_url":"https://api.github.com/repos/47deg/fetch/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/47deg/fetch/downloads","issues_url":"https://api.github.com/repos/47deg/fetch/issues{/number}","pulls_url":"https://api.github.com/repos/47deg/fetch/pulls{/number}","milestones_url":"https://api.github.com/repos/47deg/fetch/milestones{/number}","notifications_url":"https://api.github.com/repos/47deg/fetch/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/47deg/fetch/labels{/name}","releases_url":"https://api.github.com/repos/47deg/fetch/releases{/id}","deployments_url":"https://api.github.com/repos/47deg/fetch/deployments","created_at":"2016-03-04T09:32:16Z","updated_at":"2016-05-17T00:24:28Z","pushed_at":"2016-05-17T10:41:21Z","git_url":"git://github.com/47deg/fetch.git","ssh_url":"git@github.com:47deg/fetch.git","clone_url":"https://github.com/47deg/fetch.git","svn_url":"https://github.com/47deg/fetch","homepage":null,"size":151,"stargazers_count":18,"watchers_count":18,"language":"Scala","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":5,"mirror_url":null,"open_issues_count":12,"forks":5,"open_issues":12,"watchers":18,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/47deg/fetch/pulls/24"},"html":{"href":"https://github.com/47deg/fetch/pull/24"},"issue":{"href":"https://api.github.com/repos/47deg/fetch/issues/24"},"comments":{"href":"https://api.github.com/repos/47deg/fetch/issues/24/comments"},"review_comments":{"href":"https://api.github.com/repos/47deg/fetch/pulls/24/comments"},"review_comment":{"href":"https://api.github.com/repos/47deg/fetch/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/47deg/fetch/pulls/24/commits"},"statuses":{"href":"https://api.github.com/repos/47deg/fetch/statuses/7096c32b994cf5ae7e6cbddec2d16da3d93bd34a"}}}}