{"action":"created","comment":{"url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls/comments/92114654","pull_request_review_id":12622101,"id":92114654,"diff_hunk":"@@ -0,0 +1,285 @@\n++++\n+author = [\"Scott Mansfield\"]\n+date = \"2016-12-17T05:26:58-05:00\"\n+title = \"Goroutree: A tree-based set made of coordinating goroutines\"\n+series = [\"Advent 2016\"]\n++++\n+\n+This was one of thoe projects that sat in the back of my mind for quite a while. It was destined to\n+join the many others in my side project graveyard unless I had a good reason to finish it, like a\n+date for a blog post.\n+\n+This post is an axplanation and exploration of the goroutree data structure. The structure itself is\n+composed of many separate but coordinating goroutines that make up the tree itself. It has only a\n+few operations and can't self-balance, but it's a decent proof of concept.\n+\n+I show a bit of code, but most of my descriptions are in prose because code would make the post get\n+quite long. if you want to follow along in the code, then open the link at the bottom to the GitHub\n+repository that hold the package.\n+\n+# Example\n+\n+Let's take a look at a usage example right off the bat:\n+\n+```go\n+import \"github.com/ScottMansfield/goroutree\"\n+\n+func main() {\n+    g := goroutree.New()\n+\n+    boolres := make(chan bool)\n+    g.Insert(boolres, 5)\n+    <-boolres // will return true\n+\n+    g.Contains(boolres, 5)\n+    <-boolres // will return true\n+\n+    g.Contains(boolres, 4)\n+    <-boolres // will return false\n+\n+    g.Delete(boolres, 4)\n+    <-boolres // will return false\n+\n+    g.Delete(boolres, 5)\n+    <-boolres // will return true\n+}\n+```\n+\n+# Goroutree\n+\n+The `Goroutree` type is very simple:\n+\n+```go\n+type Goroutree struct {\n+    cmdchan chan cmd\n+}\n+```\n+\n+It only has a command channel to send commands to and nothing else. The `New()`\n+function only needs to start up the manager goroutine and return a pointer to\n+the `Goroutree`.\n+\n+```go\n+func New() *Goroutree {\n+    cmdchan := make(chan cmd)\n+    go manager(cmdchan)\n+\n+    return &Goroutree{cmdchan}\n+}\n+```\n+\n+# Manager Goroutine\n+\n+There's actually a hidden \"super root\" node that takes the initial requests and then forwards them\n+as needed to the root node. This is to support things like having an empty tree or deleting the root\n+node. For example, when checking if the tree contains a number, if the root node is null the answer\n+is always false. For deleting the root node, someone has to be notified of the new root's handle\n+(read: input channel). THe manager goroutine is there to play that role.\n+\n+Introducing the manager also made the synchronous interface be completely separated from the tree\n+instead of sometimes requiring a new goroutine to be spawned just to respond to the request.\n+\n+# Node Goroutine\n+\n+Each node is a separate goroutine that owns a particular value. It has an input channel that is a\n+stream of commands which the node will react to one at a time.\n+\n+Nodes have knowledge of their own value (an int), and several channels:\n+\n+* input\n+* parent\n+* left child\n+* right child\n+\n+The input and parent channels are guaranteed to be not nil, but the left and right are not. Each\n+node is independently and concurrently receiving and serving requests coming in. There's some\n+situations where a child will message a parent, but mostly the commands go downward.\n+\n+# Operations\n+\n+There's three main operations on the set: `Insert`, `Contains`, and `Delete`. There is a fourth,\n+`Print`, that is mainly used for debugging.\n+\n+The operations all follow the basic binary search tree patterns. There's no facny rotations that\n+happen in the middle, but there are a couple somewhat complicated interactions during deletion of a\n+node. There's some extra commands that are only used internally that I will show as well.\n+\n+## Sidenote: types\n+\n+I created a command type and many implementations of that command in order to send the proper data\n+around. These are the constant type values and interface for the command type:\n+\n+```go\n+type cmdType int\n+\n+const (\n+    ctInvalid cmdType = iota\n+    ctInsert\n+    ctContains\n+    ctDelete\n+    ctPrint\n+    ctNewChild\n+    ctExtractMin\n+)\n+\n+type cmd interface {\n+    typ() cmdType\n+}\n+```\n+\n+This is probably a pattern that will make some cringe. I use a `typ()` function to determine the\n+type of the command instead of doing a type switch. I do it because I like it. There's no real\n+reason to do it that way over using a type switch.\n+\n+## Insert\n+\n+The `Insert` function will add a new value to the set if it does not already exist. In effect, it\n+will create a new goroutine to own that value and attach it at the proper point in the tree. The\n+channel passed in will receive a boolean; true for successful insert and false if the value already\n+existed.\n+\n+Internally, an insert command message is sent through the tree. The manager will create the root if\n+needed. From there, the nodes will pass down to the left for values less than them and to the right\n+for greater until the child channel is nil. It then spawns a child goroutine to own the value and\n+return true to the caller. If the value is equal ot the node's value at any point, it will return a\n+false to the caller.\n+\n+## Contains\n+\n+The `Contains` function tells whether a value exists in the set. This is safe to use concurrently.\n+The caller's channel with either get a true if the value exists or a false if it doesn't. It does\n+not modify the tree at all. THe internals are very simple, with a contains command getting sent down\n+the tree until a node can respond difinitively.\n+\n+## Delete\n+\n+`Delete` is by far the most complicated operation. It has a couple simple cases. If the value isn't\n+equal it will pick the proper side. If that side is nil, a false will be sent back to the caller.\n+\n+If the value is equal, things get interesting. The node has to figure out how to extract itself from\n+the tree. In a normal binary tree, this is relatively simple because you can hold on to references\n+to everything required in one place. In this kind of tree, we need to be able to pull this off with\n+just messages.\n+\n+There's three sub-cases here:\n+\n+1. No children (easy)\n+1. One child (medium)\n+1. Two children (hard)\n+\n+### No children\n+\n+In this case, the node needs to be able to inform its parent that it will be going away. To do this\n+there is a special kind of message to replace a child channel. This is where the handle to the\n+parent node comes in handy. In order to disconnect from the parent node, the child will send a\n+command to replace itself with nil. In order to tell which child is to be replaced at the parent,\n+each node on the way down will modify the command with direction it is sending downward. The only\n+thing left at that point is to return.\n+\n+### One child.\n+\n+This is very similar to the case above, but instead of sending nil as a replacement it send the only\n+child it has to the parent and then returns.\n+\n+### Two children\n+\n+Ah, this one is fun. In order to remove itself, the node in every case has to find a suitable\n+replacement. When there are two children, there are two choices: the left subtree's maximum or the\n+right subtree's minimum. To simplify things, I elected to always find the minimum value of the right\n+subtree.\n+\n+To do this, there's a special internal-only command to find the minimum value. This command is first\n+passed right by the node being deleted and then left as far as possible. At the end of the leftward\n+movement, it has one or zero children and can be deleted according to the first two cases above.\n+\n+Now in order to delete the initial requested value out of the tree, I cheat a little bit here. It\n+was probably possible to surgically move the minimum right subtree value in to place, but it's far\n+easier to just return the found value and delete that minimum value node. Then we take the value and\n+replace the value in the running node that was to be deleted. We move only the value instead of the\n+goroutine connections but gain the same effect.\n+\n+While the goroutine is performing this operation it is blocking on a response from the minimum value\n+search, so no other commands will end up coming through. This should guarantee that we don't end up\n+in some weird inconsistent state, but like I said before I haven't done any testing for high\n+concurrency situations.\n+\n+## Print\n+\n+The print command prints the tree in an in-order representation left to right, but puts newlines\n+between each noe and prints the number of levels down in spaces before each number. This output","path":"content/advent-2016/goroutree.md","position":209,"original_position":209,"commit_id":"e29f19ffd7b0aec6129f828abbab68b4d3e15824","original_commit_id":"e29f19ffd7b0aec6129f828abbab68b4d3e15824","user":{"login":"bradleyfalzon","id":1834577,"avatar_url":"https://avatars.githubusercontent.com/u/1834577?v=3","gravatar_id":"","url":"https://api.github.com/users/bradleyfalzon","html_url":"https://github.com/bradleyfalzon","followers_url":"https://api.github.com/users/bradleyfalzon/followers","following_url":"https://api.github.com/users/bradleyfalzon/following{/other_user}","gists_url":"https://api.github.com/users/bradleyfalzon/gists{/gist_id}","starred_url":"https://api.github.com/users/bradleyfalzon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bradleyfalzon/subscriptions","organizations_url":"https://api.github.com/users/bradleyfalzon/orgs","repos_url":"https://api.github.com/users/bradleyfalzon/repos","events_url":"https://api.github.com/users/bradleyfalzon/events{/privacy}","received_events_url":"https://api.github.com/users/bradleyfalzon/received_events","type":"User","site_admin":false},"body":"Should `noe` be `node`?","created_at":"2016-12-13T08:03:55Z","updated_at":"2016-12-13T08:06:41Z","html_url":"https://github.com/gopheracademy/gopheracademy-web/pull/210#discussion_r92114654","pull_request_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls/210","_links":{"self":{"href":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls/comments/92114654"},"html":{"href":"https://github.com/gopheracademy/gopheracademy-web/pull/210#discussion_r92114654"},"pull_request":{"href":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls/210"}}},"pull_request":{"url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls/210","id":97701072,"html_url":"https://github.com/gopheracademy/gopheracademy-web/pull/210","diff_url":"https://github.com/gopheracademy/gopheracademy-web/pull/210.diff","patch_url":"https://github.com/gopheracademy/gopheracademy-web/pull/210.patch","issue_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/issues/210","number":210,"state":"open","locked":false,"title":"Adding first draft of Goroutree post","user":{"login":"ScottMansfield","id":184105,"avatar_url":"https://avatars.githubusercontent.com/u/184105?v=3","gravatar_id":"","url":"https://api.github.com/users/ScottMansfield","html_url":"https://github.com/ScottMansfield","followers_url":"https://api.github.com/users/ScottMansfield/followers","following_url":"https://api.github.com/users/ScottMansfield/following{/other_user}","gists_url":"https://api.github.com/users/ScottMansfield/gists{/gist_id}","starred_url":"https://api.github.com/users/ScottMansfield/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScottMansfield/subscriptions","organizations_url":"https://api.github.com/users/ScottMansfield/orgs","repos_url":"https://api.github.com/users/ScottMansfield/repos","events_url":"https://api.github.com/users/ScottMansfield/events{/privacy}","received_events_url":"https://api.github.com/users/ScottMansfield/received_events","type":"User","site_admin":false},"body":"cc @dgryski ","created_at":"2016-12-13T07:49:26Z","updated_at":"2016-12-13T08:06:41Z","closed_at":null,"merged_at":null,"merge_commit_sha":"998b25d7d1ef839a745bb7437e68a51eb4af5329","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls/210/commits","review_comments_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls/210/comments","review_comment_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls/comments{/number}","comments_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/issues/210/comments","statuses_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/statuses/e29f19ffd7b0aec6129f828abbab68b4d3e15824","head":{"label":"ScottMansfield:ScottMansfield-Goroutree","ref":"ScottMansfield-Goroutree","sha":"e29f19ffd7b0aec6129f828abbab68b4d3e15824","user":{"login":"ScottMansfield","id":184105,"avatar_url":"https://avatars.githubusercontent.com/u/184105?v=3","gravatar_id":"","url":"https://api.github.com/users/ScottMansfield","html_url":"https://github.com/ScottMansfield","followers_url":"https://api.github.com/users/ScottMansfield/followers","following_url":"https://api.github.com/users/ScottMansfield/following{/other_user}","gists_url":"https://api.github.com/users/ScottMansfield/gists{/gist_id}","starred_url":"https://api.github.com/users/ScottMansfield/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScottMansfield/subscriptions","organizations_url":"https://api.github.com/users/ScottMansfield/orgs","repos_url":"https://api.github.com/users/ScottMansfield/repos","events_url":"https://api.github.com/users/ScottMansfield/events{/privacy}","received_events_url":"https://api.github.com/users/ScottMansfield/received_events","type":"User","site_admin":false},"repo":{"id":75270215,"name":"gopheracademy-web","full_name":"ScottMansfield/gopheracademy-web","owner":{"login":"ScottMansfield","id":184105,"avatar_url":"https://avatars.githubusercontent.com/u/184105?v=3","gravatar_id":"","url":"https://api.github.com/users/ScottMansfield","html_url":"https://github.com/ScottMansfield","followers_url":"https://api.github.com/users/ScottMansfield/followers","following_url":"https://api.github.com/users/ScottMansfield/following{/other_user}","gists_url":"https://api.github.com/users/ScottMansfield/gists{/gist_id}","starred_url":"https://api.github.com/users/ScottMansfield/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScottMansfield/subscriptions","organizations_url":"https://api.github.com/users/ScottMansfield/orgs","repos_url":"https://api.github.com/users/ScottMansfield/repos","events_url":"https://api.github.com/users/ScottMansfield/events{/privacy}","received_events_url":"https://api.github.com/users/ScottMansfield/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ScottMansfield/gopheracademy-web","description":"Public Website for blog.GopherAcademy.com","fork":true,"url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web","forks_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/forks","keys_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/teams","hooks_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/hooks","issue_events_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/issues/events{/number}","events_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/events","assignees_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/assignees{/user}","branches_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/branches{/branch}","tags_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/tags","blobs_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/git/refs{/sha}","trees_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/statuses/{sha}","languages_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/languages","stargazers_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/stargazers","contributors_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/contributors","subscribers_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/subscribers","subscription_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/subscription","commits_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/commits{/sha}","git_commits_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/git/commits{/sha}","comments_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/comments{/number}","issue_comment_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/issues/comments{/number}","contents_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/contents/{+path}","compare_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/merges","archive_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/downloads","issues_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/issues{/number}","pulls_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/pulls{/number}","milestones_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/milestones{/number}","notifications_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/labels{/name}","releases_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/releases{/id}","deployments_url":"https://api.github.com/repos/ScottMansfield/gopheracademy-web/deployments","created_at":"2016-12-01T08:10:40Z","updated_at":"2016-12-01T08:10:44Z","pushed_at":"2016-12-13T07:49:06Z","git_url":"git://github.com/ScottMansfield/gopheracademy-web.git","ssh_url":"git@github.com:ScottMansfield/gopheracademy-web.git","clone_url":"https://github.com/ScottMansfield/gopheracademy-web.git","svn_url":"https://github.com/ScottMansfield/gopheracademy-web","homepage":"http://blog.gopheracademy.com","size":13703,"stargazers_count":0,"watchers_count":0,"language":"CSS","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"gopheracademy:master","ref":"master","sha":"84ec3e359c984cca91edb5cf6f150259e2c93e99","user":{"login":"gopheracademy","id":7428627,"avatar_url":"https://avatars.githubusercontent.com/u/7428627?v=3","gravatar_id":"","url":"https://api.github.com/users/gopheracademy","html_url":"https://github.com/gopheracademy","followers_url":"https://api.github.com/users/gopheracademy/followers","following_url":"https://api.github.com/users/gopheracademy/following{/other_user}","gists_url":"https://api.github.com/users/gopheracademy/gists{/gist_id}","starred_url":"https://api.github.com/users/gopheracademy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gopheracademy/subscriptions","organizations_url":"https://api.github.com/users/gopheracademy/orgs","repos_url":"https://api.github.com/users/gopheracademy/repos","events_url":"https://api.github.com/users/gopheracademy/events{/privacy}","received_events_url":"https://api.github.com/users/gopheracademy/received_events","type":"Organization","site_admin":false},"repo":{"id":23665322,"name":"gopheracademy-web","full_name":"gopheracademy/gopheracademy-web","owner":{"login":"gopheracademy","id":7428627,"avatar_url":"https://avatars.githubusercontent.com/u/7428627?v=3","gravatar_id":"","url":"https://api.github.com/users/gopheracademy","html_url":"https://github.com/gopheracademy","followers_url":"https://api.github.com/users/gopheracademy/followers","following_url":"https://api.github.com/users/gopheracademy/following{/other_user}","gists_url":"https://api.github.com/users/gopheracademy/gists{/gist_id}","starred_url":"https://api.github.com/users/gopheracademy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gopheracademy/subscriptions","organizations_url":"https://api.github.com/users/gopheracademy/orgs","repos_url":"https://api.github.com/users/gopheracademy/repos","events_url":"https://api.github.com/users/gopheracademy/events{/privacy}","received_events_url":"https://api.github.com/users/gopheracademy/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/gopheracademy/gopheracademy-web","description":"Public Website for blog.GopherAcademy.com","fork":false,"url":"https://api.github.com/repos/gopheracademy/gopheracademy-web","forks_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/forks","keys_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/teams","hooks_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/hooks","issue_events_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/issues/events{/number}","events_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/events","assignees_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/assignees{/user}","branches_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/branches{/branch}","tags_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/tags","blobs_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/git/refs{/sha}","trees_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/statuses/{sha}","languages_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/languages","stargazers_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/stargazers","contributors_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/contributors","subscribers_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/subscribers","subscription_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/subscription","commits_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/commits{/sha}","git_commits_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/git/commits{/sha}","comments_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/comments{/number}","issue_comment_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/issues/comments{/number}","contents_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/contents/{+path}","compare_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/merges","archive_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/downloads","issues_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/issues{/number}","pulls_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls{/number}","milestones_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/milestones{/number}","notifications_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/labels{/name}","releases_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/releases{/id}","deployments_url":"https://api.github.com/repos/gopheracademy/gopheracademy-web/deployments","created_at":"2014-09-04T14:18:26Z","updated_at":"2016-12-12T03:57:51Z","pushed_at":"2016-12-13T07:49:26Z","git_url":"git://github.com/gopheracademy/gopheracademy-web.git","ssh_url":"git@github.com:gopheracademy/gopheracademy-web.git","clone_url":"https://github.com/gopheracademy/gopheracademy-web.git","svn_url":"https://github.com/gopheracademy/gopheracademy-web","homepage":"http://blog.gopheracademy.com","size":13827,"stargazers_count":90,"watchers_count":90,"language":"CSS","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":102,"mirror_url":null,"open_issues_count":10,"forks":102,"open_issues":10,"watchers":90,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls/210"},"html":{"href":"https://github.com/gopheracademy/gopheracademy-web/pull/210"},"issue":{"href":"https://api.github.com/repos/gopheracademy/gopheracademy-web/issues/210"},"comments":{"href":"https://api.github.com/repos/gopheracademy/gopheracademy-web/issues/210/comments"},"review_comments":{"href":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls/210/comments"},"review_comment":{"href":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/gopheracademy/gopheracademy-web/pulls/210/commits"},"statuses":{"href":"https://api.github.com/repos/gopheracademy/gopheracademy-web/statuses/e29f19ffd7b0aec6129f828abbab68b4d3e15824"}}}}
{"actor":{"display_login":"bradleyfalzon"}}