{"action":"created","comment":{"url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/comments/49275782","id":49275782,"diff_hunk":"@@ -0,0 +1,345 @@\n+---\n+comments: true\n+date: 2016-01-08 10:00:00\n+layout: post\n+slug: snake-game-using-re-frame\n+title: Snake game using Clojurescript/re-frame\n+summary: Simple tutorial how to create Snake game using ClojureScript and reagent/re-frame\n+author: Dajana Štiberová\n+feed_group:  Clojure\n+description:  Simple tutorial how to create Snake game using ClojureScript and reagent/re-frame`\n+tags:\n+- Clojure\n+- ClojureScript\n+- Re-frame\n+- Reagent\n+---\n+\n+## Snake game\n+\n+\n+\n+I was thinking about writing tutorial to show people how to create simple interactive application using ClojureScript/re-frame. And then I realized that some arcade game would be great example. One of the first games I played was Snake. Most often secretly under the desk during the classes in school on nokia3310 :)\n+\n+So I decided to connect funny and useful things together, and result of that is this snake game tutorial.\n+\n+Our tutorial has 6 parts:\n+\n+1. Setting the project\n+2. Creating data\n+3. Static view\n+* Board\n+* Snake\n+* Point to catch\n+* Score\n+* GameOver\n+4. Make the snake moving\n+5. Actions to perform when the point is catched\n+* Creating point to catch in different place\n+* Make the snake longer\n+* Increase score\n+6. Collisions\n+* Snake collides with the border\n+* Snake collides with itself\n+7. Game over and replay\n+\n+\n+### 1.Setting the project\n+\n+Download and install [leiningen](http://leiningen.org/).\n+\n+Create project by running`lein new figwheel snake-game` in your console.\n+\n+In your project.clj add to `:dependencies` reagent `[reagent \"0.5.0\"]` and re-frame `[re-frame \"0.6.0\"]`\n+\n+[code](https://github.com/DajanaStiberova/snake-game/blob/master/project.clj)\n+\n+Rewrite your core.cljs (src/snake_game/core.cljs) to looks like bellow. In this part we are requiring everything we need for our project.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/35c39e99abca511b0036.js\"></script>\n+\n+You also need css. So you can copy it from [here](https://github.com/ScalaConsultants/snake-game/blob/master/resources/public/css/style.css) and paste it to resources/poblic/css/style.css\n+\n+[commit n.1](https://github.com/DajanaStiberova/snake-game/commit/fffb0eb6a5ce315d7fb4141450c4709adca972ad)\n+\n+Run `lein figwheel` and open [localhost:3449](http://localhost:3449/) in your browser\n+\n+### 2. Creating data\n+\n+So, it's time to create our data. Let's think about what data we need.\n+\n+1. We need a board where the snake is moving\n+2. We need a snake (his position and head direction)\n+3. Point to catch\n+4. Score\n+5. Game state (playing or game-over)\n+\n+**Initial state**\n+\n+- All data we need, are stored in initial state.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/64cd34c4d8550ba208a5.js\"></script>\n+\n+At this time every map value is nil. So let's fill the map with our data.\n+\n+**Board**\n+\n+- board has two values. x (width) and y (height). Our board's width is 35 and height is 25 - it's important to note, that those are board cells not screen pixels.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/b971b37e6259893eee1b.js\"></script>\n+\n+**Snake**\n+\n+- snake body: We will store snake body in vectors [[3 2][2 2] ..]. Every vector is pair of cell coordinates for particular body part. \n+- direction: Direction of the snake head. Direction will be stored in vector as well.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/d51b209f4fac547a0a46.js\"></script>\n+\n+**Point to catch**\n+\n+- our catch point should have random position which is not colliding with the snake body. We will need to write this function. This function will take snake and board as arguments and returns random free position. If there's no available position, the function will return nil.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/4ce13481641693d8168e.js\"></script>\n+\n+**Score**\n+\n+- default value of score is 0\n+\n+**Game State**\n+\n+- When we are starting the game, the state of the game is true. It will become false when the game is stopped for any reason.\n+\n+Now is our initial state filled and looks like this:\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/d61199d296561017f13f.js\"></script>\n+\n+[commit n.2](https://github.com/DajanaStiberova/snake-game/commit/9146f12c464a83036113a730f8484646bb220680)\n+\n+## 3. Static view\n+\n+We should talk a little bit about [re-frame](https://github.com/Day8/re-frame).<br>\n+In re-frame, we should make mutations to our db (or app-state, atom defined by re-frame) only through so called handlers. For simplicity, we can percieve handler as a function which takes current application state with some parameters and returns a new application state. This funcion is registred using some keyword (handler-identity) and the only way how to run that function (handler) is to dispatch event, where event is a vector containing handler identity and any number of additional arguments.\n+\n+We create our handler and then register it using re-frame/register-handler.\n+\n+Initailize handler is the first handler we need. Initialize handler will merge our initial state into application state.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/fcb001c1042d3811e09d.js\"></script>\n+\n+If you'll write `@re-frame.db/app-db` in your repl, you shold see application state. But at this time there's only empty map. That's because our :initialize handler wasn't called yet. We'll do this using :dispatch\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/fcdef3122bb98b4590d5.js\"></script>\n+\n+And now we need to just call the run function\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/b2ce9cd323c5c5ba4d58.js\"></script>\n+\n+You can try `@re-frame.db/app-db` in repl and you'll see that application state is now\n+```\n+{:board [35 25],\n+:snake {:direction [1 0],\n+:body [[3 2] [2 2] [1 2] [0 2]]},\n+```\n+```\n+:point [15 13],\n+:points 0,\n+:game true}\n+```\n+\n+But in your browser you see still nothing, right? So let's make it visible.\n+\n+We need to tell our application where we want to render it. We want to render it into div with id app in our HTML. For this action we will use reagent/render function. This fuction takes a main rendering function and DOM node. Wwe can get the dom node by using javascript interop (js/document.get..).\n+\n+We don't have main rendering function, but we will create it in the next step. But we know that name of this function will be game. So we will modify our run function to render our app.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/4b624fdf87395cf04991.js\"></script>\n+\n+Let's create our game (main rendering) fuction. This function just creates empty div this time. To represent HTML elements, we use plain Clojure datastructures (if you want to know more about this syntax, visit [Hiccup](https://github.com/weavejester/hiccup)).\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/ba7219c8106a1f210746.js\"></script>\n+\n+If you'll go to the developer console in your browser (localhost:3449) you'll see new empty div inside div with id app.\n+\n+It's time to render a board. To get the the board data from app state, we'll need to register a subscription. Subscription is a way, how we can access changing state from our db. As with handlers, each subscrition is registered using a keyword, which is board in this case. This subscription function takes a db as an argument, and any number of optional arguments. In the body of the function, we will dereference the db, extract :body and wrap it in the reaction `(reaction (:board @db))`\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/31da0bdb2b80e314178e.js\"></script>\n+\n+If you'll write `@(subscribe [:board])` into your repl, you'll se the board value `[35 25]`\n+\n+Now we can connect it to our view. We will display our board as table by creating render-board function. As you can see, we are using subscribe in our let block, to get the :board value.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/fc8ac5bb14d7fc15f543.js\"></script>\n+\n+And calling it in our main rendering function. Inside existing div, we will render game board.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/dd82f67fd533643678d2.js\"></script>\n+\n+After save, you can see an empty board in your browser.\n+\n+At this step we'll modify render-board function to show snake and point to catch as well.\n+\n+We need to create subscriptions for snake and point to catch.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/1c09e5416579f0a6094b.js\"></script>\n+\n+And modify our render-board function. We will additionally subscribe snake and point to catch, and based on their indexes, use appropriate CSS class on table cell.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/c24ae08c282d647f9483.js\"></script>\n+\n+The Score part is the simpliest part of the view. We need to make an subscription, and div, where the score will be located.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/6f3ef6ba51fe8d9dc5ec.js\"></script>\n+\n+And add score function to game function.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/d755f3faa376c30a8845.js\"></script>\n+\n+The last part of the view is game-over. This is the overlay when the :game in app-state is false. I'm sure you know what's coming. Subscription and fuction which render div with overlay. This function will have a simple condition. If the value of the :game is true, function returs empty div, if the state is false, the returned div will have class overlay (all classes are defined in our resources/public/css/style.css). In this div we will have h1 with symbol ↺ for refreshing game. We will also render h1 with symbol ↺ there, to provide clickable target for refreshing game.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/dd2653694c594f5f9f4f.js\"></script>\n+\n+And modify game function to render game-over.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/7a02f3006f50f5cf2c94.js\"></script>\n+\n+Now you can see full board with snake, point to catch and score in your browser.\n+\n+![static-part](http://i.imgur.com/adu7rFv.png?1)\n+\n+If you'll change the value of the :game to false in initial state, you'll see an game-over overlay.\n+\n+![static-game-over](http://i.imgur.com/bGioI8c.png?1)\n+\n+[commit n.3](https://github.com/DajanaStiberova/snake-game/commit/2e1bf3e217d26d5e6b11cda54e2bb40283a1f311)\n+\n+## 4.Make the snake moving\n+\n+To make our snake moving, we need to update his data in regular intervals. But first, we need to create a function, which animates the snake. This function will take the snake map,and creates a new snake map, where the body of the snake is moved one step. You can create this function by yourself or you can have a look at our implementation :)\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/9cd7dfc668c7af2f0b28.js\"></script>\n+\n+In a let block we define new position of the snake head by applying direction on a first snake segment (snake head). New coordinates of the next segment are the old coordinates of the previous segment.\n+\n+We know that if we want to make some mutations in our code, we will use handlers. We will register handler with name :next-state and we will use our move-snake function to update our snake. Before calling move-snake in the :next-state handler, we have to chceck, if the game's still going on (value of the :game in app state). If not, we won't do anything, and we will just return unchanged db.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/ead3a55af334d8ddaf36.js\"></script>\n+\n+You can try `(dispatch [:next-state])` in your repl, and you'll see the snake is rendered in new position. But we want to keep our snake movement going on. So we need to dispatch :next-state event peridicaly. For example 150ms. This inerval will reflect the speed of the \"game world\" and difficulty as well :)\n+Here we're using js interop again.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/8e40c0dc4460099d3901.js\"></script>\n+\n+Snake is moving, that's nice, but we need to control him.\n+\n+We will listen for specific [key codes](https://css-tricks.com/snippets/javascript/javascript-keycodes/): up (38), down (40), left (37), right (39) to determine that arrows were pressed.\n+\n+So we we want this keys to change the direction of snake. Natural way to express it, would be to just create the mapping from keycodes to correspondng direction vectors.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/29b393eba05f7f6932b9.js\"></script>\n+\n+When the key is pressed, we will lookup the corresoding direction vector from the key-code->move map. Then we'll just update snake with new direction.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/7f0e0440b1bc91f78c44.js\"></script>\n+\n+This whole key lookup and dispatch will be perform when the keydown event is detected.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/f9482091ed78721327e7.js\"></script>\n+\n+But there's one little problem. If we press the arrow, opposite to current snake direction (for example if snake's going to the right, and we'll press the left arrow), the snake will immediately goes throught itself. We cat either evaluate this as a collision and stop the game, or just ignore that move. I choosed the second option.\n+\n+I created a function change-snake-direction. This function takes a new direction and the current direction and compares them. If this new direction is perpendicular to the old direction, function returns new direction, if not it just returns the old direction.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/0b1fe4352c2ec34997aa.js\"></script>\n+\n+Of course we need to use this function in :change-direction handler.\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/9a08ca320042a32ff6c8.js\"></script>\n+\n+Now we can fully controll the snake movement, and the only things left are collision handling and point catching.\n+\n+[commit n.4](https://github.com/DajanaStiberova/snake-game/commit/be88f699196a882f65b40d54da71eeaec6ae5b7a)\n+\n+## 5.Actions to perform when the point is catched\n+\n+Actions when the snake catches the point:\n+\n+**Create point to catch on different place**\n+\n+We don't need a new function, we can use our rand-free-position function.\n+\n+**Make the snake longer**\n+\n+We need a new function grow-snake. This function will use last 2 parts of the snake, and based on this creates another part in the end of the snake.\n+We'll also create small helper function snake-tail, which computes x or y coordinate, based on last two values of said coordinate in snake body. \n+\n+For example, if this function gets 1 2 as an arguments, returns 3 ,if it gets 2 1 as an arguments it returns 0\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/0d6ede73d35350319d6f.js\"></script>\n+\n+We will use this function in our grow-snake function. This function will takes snake as an argument and based on the last two coordinates of snake body, we will create another coordinate and conj it to the snake.\n+<script src=\"https://gist.github.com/DajanaStiberova/857db40a6ba959797deb.js\"></script>\n+\n+**Increase score**\n+\n+Just simple inc on :points in db.\n+\n+**Putting actions together**\n+\n+For putting all 3 functions together, we will use process-move function. This function will take a whole db as an argument, and if the first snake part (head of the snake) has the same coordinate as the point, we will update every key we need (:snake (grow-snake) :points (inc) :point (rand-free-position))\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/bdee06cd01afb3abc87d.js\"></script>\n+\n+We need to call process-move function. The best time when to call this function is in :next-state handler,chained after move-snake call. So :next-state handler will look like this:\n+\n+<script src=\"https://gist.github.com/DajanaStiberova/4306ef43c2453faca05f.js\"></script>\n+\n+[commit n.5](https://github.com/DajanaStiberova/snake-game/commit/a5200e523f26186d6afd577d4eed7e920d135d32)\n+\n+## 6.Collisions\n+\n+We can play snake game, but it's impossible to lose the game, but very easy lost the snake from the screen :)","path":"_posts/2016-01-08-snake-game.md","position":300,"original_position":300,"commit_id":"bf574f64ea98a52b91500539c5c7853b3d8793ef","original_commit_id":"bf574f64ea98a52b91500539c5c7853b3d8793ef","user":{"login":"pjazdzewski1990","id":1136843,"avatar_url":"https://avatars.githubusercontent.com/u/1136843?v=3","gravatar_id":"","url":"https://api.github.com/users/pjazdzewski1990","html_url":"https://github.com/pjazdzewski1990","followers_url":"https://api.github.com/users/pjazdzewski1990/followers","following_url":"https://api.github.com/users/pjazdzewski1990/following{/other_user}","gists_url":"https://api.github.com/users/pjazdzewski1990/gists{/gist_id}","starred_url":"https://api.github.com/users/pjazdzewski1990/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pjazdzewski1990/subscriptions","organizations_url":"https://api.github.com/users/pjazdzewski1990/orgs","repos_url":"https://api.github.com/users/pjazdzewski1990/repos","events_url":"https://api.github.com/users/pjazdzewski1990/events{/privacy}","received_events_url":"https://api.github.com/users/pjazdzewski1990/received_events","type":"User","site_admin":false},"body":"`very easy lost the snake...` -> `very easy to loose the snake...`","created_at":"2016-01-10T13:21:08Z","updated_at":"2016-01-10T13:21:08Z","html_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/115#discussion_r49275782","pull_request_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/115","_links":{"self":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/comments/49275782"},"html":{"href":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/115#discussion_r49275782"},"pull_request":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/115"}}},"pull_request":{"url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/115","id":55549154,"html_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/115","diff_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/115.diff","patch_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/115.patch","issue_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/115","number":115,"state":"open","locked":false,"title":"snake game post","user":{"login":"DajanaStiberova","id":9211809,"avatar_url":"https://avatars.githubusercontent.com/u/9211809?v=3","gravatar_id":"","url":"https://api.github.com/users/DajanaStiberova","html_url":"https://github.com/DajanaStiberova","followers_url":"https://api.github.com/users/DajanaStiberova/followers","following_url":"https://api.github.com/users/DajanaStiberova/following{/other_user}","gists_url":"https://api.github.com/users/DajanaStiberova/gists{/gist_id}","starred_url":"https://api.github.com/users/DajanaStiberova/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DajanaStiberova/subscriptions","organizations_url":"https://api.github.com/users/DajanaStiberova/orgs","repos_url":"https://api.github.com/users/DajanaStiberova/repos","events_url":"https://api.github.com/users/DajanaStiberova/events{/privacy}","received_events_url":"https://api.github.com/users/DajanaStiberova/received_events","type":"User","site_admin":false},"body":"Please review","created_at":"2016-01-09T19:34:14Z","updated_at":"2016-01-10T13:21:08Z","closed_at":null,"merged_at":null,"merge_commit_sha":"d8d23318f0cf7207f3f3154aa981ee2460c34953","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/115/commits","review_comments_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/115/comments","review_comment_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/115/comments","statuses_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/statuses/bf574f64ea98a52b91500539c5c7853b3d8793ef","head":{"label":"ScalaConsultants:snake-game","ref":"snake-game","sha":"bf574f64ea98a52b91500539c5c7853b3d8793ef","user":{"login":"ScalaConsultants","id":6207831,"avatar_url":"https://avatars.githubusercontent.com/u/6207831?v=3","gravatar_id":"","url":"https://api.github.com/users/ScalaConsultants","html_url":"https://github.com/ScalaConsultants","followers_url":"https://api.github.com/users/ScalaConsultants/followers","following_url":"https://api.github.com/users/ScalaConsultants/following{/other_user}","gists_url":"https://api.github.com/users/ScalaConsultants/gists{/gist_id}","starred_url":"https://api.github.com/users/ScalaConsultants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScalaConsultants/subscriptions","organizations_url":"https://api.github.com/users/ScalaConsultants/orgs","repos_url":"https://api.github.com/users/ScalaConsultants/repos","events_url":"https://api.github.com/users/ScalaConsultants/events{/privacy}","received_events_url":"https://api.github.com/users/ScalaConsultants/received_events","type":"Organization","site_admin":false},"repo":{"id":15366666,"name":"ScalaConsultants.github.io","full_name":"ScalaConsultants/ScalaConsultants.github.io","owner":{"login":"ScalaConsultants","id":6207831,"avatar_url":"https://avatars.githubusercontent.com/u/6207831?v=3","gravatar_id":"","url":"https://api.github.com/users/ScalaConsultants","html_url":"https://github.com/ScalaConsultants","followers_url":"https://api.github.com/users/ScalaConsultants/followers","following_url":"https://api.github.com/users/ScalaConsultants/following{/other_user}","gists_url":"https://api.github.com/users/ScalaConsultants/gists{/gist_id}","starred_url":"https://api.github.com/users/ScalaConsultants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScalaConsultants/subscriptions","organizations_url":"https://api.github.com/users/ScalaConsultants/orgs","repos_url":"https://api.github.com/users/ScalaConsultants/repos","events_url":"https://api.github.com/users/ScalaConsultants/events{/privacy}","received_events_url":"https://api.github.com/users/ScalaConsultants/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io","description":"Scalac blog","fork":false,"url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io","forks_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/forks","keys_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/teams","hooks_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/hooks","issue_events_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/events","assignees_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/tags","blobs_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/languages","stargazers_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/stargazers","contributors_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/contributors","subscribers_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/subscribers","subscription_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/subscription","commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/merges","archive_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/downloads","issues_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/labels{/name}","releases_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/releases{/id}","created_at":"2013-12-21T22:28:57Z","updated_at":"2015-12-17T20:52:15Z","pushed_at":"2016-01-09T19:34:15Z","git_url":"git://github.com/ScalaConsultants/ScalaConsultants.github.io.git","ssh_url":"git@github.com:ScalaConsultants/ScalaConsultants.github.io.git","clone_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io.git","svn_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io","homepage":null,"size":9498,"stargazers_count":4,"watchers_count":4,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":9,"mirror_url":null,"open_issues_count":3,"forks":9,"open_issues":3,"watchers":4,"default_branch":"master"}},"base":{"label":"ScalaConsultants:master","ref":"master","sha":"31bfddab2327132881a86c1f80c3695f72fc8f0c","user":{"login":"ScalaConsultants","id":6207831,"avatar_url":"https://avatars.githubusercontent.com/u/6207831?v=3","gravatar_id":"","url":"https://api.github.com/users/ScalaConsultants","html_url":"https://github.com/ScalaConsultants","followers_url":"https://api.github.com/users/ScalaConsultants/followers","following_url":"https://api.github.com/users/ScalaConsultants/following{/other_user}","gists_url":"https://api.github.com/users/ScalaConsultants/gists{/gist_id}","starred_url":"https://api.github.com/users/ScalaConsultants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScalaConsultants/subscriptions","organizations_url":"https://api.github.com/users/ScalaConsultants/orgs","repos_url":"https://api.github.com/users/ScalaConsultants/repos","events_url":"https://api.github.com/users/ScalaConsultants/events{/privacy}","received_events_url":"https://api.github.com/users/ScalaConsultants/received_events","type":"Organization","site_admin":false},"repo":{"id":15366666,"name":"ScalaConsultants.github.io","full_name":"ScalaConsultants/ScalaConsultants.github.io","owner":{"login":"ScalaConsultants","id":6207831,"avatar_url":"https://avatars.githubusercontent.com/u/6207831?v=3","gravatar_id":"","url":"https://api.github.com/users/ScalaConsultants","html_url":"https://github.com/ScalaConsultants","followers_url":"https://api.github.com/users/ScalaConsultants/followers","following_url":"https://api.github.com/users/ScalaConsultants/following{/other_user}","gists_url":"https://api.github.com/users/ScalaConsultants/gists{/gist_id}","starred_url":"https://api.github.com/users/ScalaConsultants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ScalaConsultants/subscriptions","organizations_url":"https://api.github.com/users/ScalaConsultants/orgs","repos_url":"https://api.github.com/users/ScalaConsultants/repos","events_url":"https://api.github.com/users/ScalaConsultants/events{/privacy}","received_events_url":"https://api.github.com/users/ScalaConsultants/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io","description":"Scalac blog","fork":false,"url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io","forks_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/forks","keys_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/teams","hooks_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/hooks","issue_events_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/events","assignees_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/tags","blobs_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/languages","stargazers_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/stargazers","contributors_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/contributors","subscribers_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/subscribers","subscription_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/subscription","commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/merges","archive_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/downloads","issues_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/labels{/name}","releases_url":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/releases{/id}","created_at":"2013-12-21T22:28:57Z","updated_at":"2015-12-17T20:52:15Z","pushed_at":"2016-01-09T19:34:15Z","git_url":"git://github.com/ScalaConsultants/ScalaConsultants.github.io.git","ssh_url":"git@github.com:ScalaConsultants/ScalaConsultants.github.io.git","clone_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io.git","svn_url":"https://github.com/ScalaConsultants/ScalaConsultants.github.io","homepage":null,"size":9498,"stargazers_count":4,"watchers_count":4,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":9,"mirror_url":null,"open_issues_count":3,"forks":9,"open_issues":3,"watchers":4,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/115"},"html":{"href":"https://github.com/ScalaConsultants/ScalaConsultants.github.io/pull/115"},"issue":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/115"},"comments":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/issues/115/comments"},"review_comments":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/115/comments"},"review_comment":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/pulls/115/commits"},"statuses":{"href":"https://api.github.com/repos/ScalaConsultants/ScalaConsultants.github.io/statuses/bf574f64ea98a52b91500539c5c7853b3d8793ef"}}}}