{"action":"created","comment":{"url":"https://api.github.com/repos/nitlang/nit/pulls/comments/63449725","id":63449725,"diff_hunk":"@@ -0,0 +1,427 @@\n+# This file is part of NIT ( http://www.nitlanguage.org ).\n+#\n+# Copyright 2016 Alexandre Terrasa <alexandre@moz-code.org>\n+#\n+# Licensed under the Apache License, Version 2.0 (the \"License\");\n+# you may not use this file except in compliance with the License.\n+# You may obtain a copy of the License at\n+#\n+#     http://www.apache.org/licenses/LICENSE-2.0\n+#\n+# Unless required by applicable law or agreed to in writing, software\n+# distributed under the License is distributed on an \"AS IS\" BASIS,\n+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+# See the License for the specific language governing permissions and\n+# limitations under the License.\n+\n+# Route handlers.\n+module pop_handlers\n+\n+import pop_routes\n+import json\n+\n+# Class handler for a route.\n+#\n+# **Routing** refers to determining how an application responds to a client request\n+# to a particular endpoint, which is a URI (or path) and a specific HTTP request\n+# method GET, POST, PUT or DELETE (other methods are not suported yet).\n+#\n+# Each route can have one or more handler methods, which are executed when the route is matched.\n+#\n+# Route handlers definition takes the following form:\n+#\n+# ~~~nitish\n+# class MyHandler\n+#\tsuper Handler\n+#\n+#\tredef fun METHOD(req, res) do end\n+# end\n+# ~~~\n+#\n+# Where:\n+# * `MyHandler` is the name of the handler you will add to the app.\n+# * `METHOD` can be replaced by `get`, `post`, `put` or `delete`.\n+#\n+# The following example responds with `Hello World!` to GET and POST requests:\n+#\n+# ~~~\n+# class MyHandler\n+#\tsuper Handler\n+#\n+#\tredef fun get(req, res) do res.send \"Got a GET request\"\n+#\tredef fun post(req, res) do res.send \"Got a POST request\"\n+# end\n+# ~~~\n+#\n+# To make your handler responds to a specific route, you have to add it to the app.\n+#\n+# Respond to POST request on the root route (`/`), the application's home page:\n+#\n+# ~~~\n+# var app = new App\n+# app.use(\"/\", new MyHandler)\n+# ~~~\n+#\n+# Respond to a request to the `/user` route:\n+#\n+# ~~~\n+# app.use(\"/user\", new MyHandler)\n+# ~~~\n+class Handler\n+\n+\t# Call `all(req, res)` if `route` matches `uri`.\n+\tprivate fun handle(route: AppRoute, uri: String, req: HttpRequest, res: HttpResponse) do\n+\t\tif route.match(uri) then\n+\t\t\tif route isa AppParamRoute then\n+\t\t\t\treq.uri_params = route.parse_uri_parameters(uri)\n+\t\t\tend\n+\t\t\tall(req, res)\n+\t\tend\n+\tend\n+\n+\t# Handler to all kind of HTTP request methods.\n+\t#\n+\t# `all` is a special request handler, which is not derived from any\n+\t# HTTP method. This method is used to respond at a path for all request methods.\n+\t#\n+\t# In the following example, the handler will be executed for requests to \"/user\"\n+\t# whether you are using GET, POST, PUT, DELETE, or any other HTTP request method.\n+\t#\n+\t# ~~~\n+\t# class AllHandler\n+\t#\tsuper Handler\n+\t#\n+\t#\tredef fun all(req, res) do res.send \"Every request to the homepage\"\n+\t# end\n+\t# ~~~\n+\t#\n+\t# Using the `all` method you can also implement other HTTP request methods.\n+\t#\n+\t# ~~~\n+\t# class MergeHandler\n+\t#\tsuper Handler\n+\t#\n+\t#\tredef fun all(req, res) do\n+\t#\t\tif req.method == \"MERGE\" then\n+\t#\t\t\t# handle that method\n+\t#\t\telse super # keep handle GET, POST, PUT and DELETE methods\n+\t#\tend\n+\t# end\n+\t# ~~~\n+\tfun all(req: HttpRequest, res: HttpResponse) do\n+\t\tif req.method == \"GET\" then\n+\t\t\tget(req, res)\n+\t\telse if req.method == \"POST\" then\n+\t\t\tpost(req, res)\n+\t\telse if req.method == \"PUT\" then\n+\t\t\tput(req, res)\n+\t\telse if req.method == \"DELETE\" then\n+\t\t\tdelete(req, res)\n+\t\telse\n+\t\t\tres.status_code = 405\n+\t\tend\n+\tend\n+\n+\t# GET handler.\n+\t#\n+\t# Exemple of route responding to GET requests.\n+\t# ~~~\n+\t# class GetHandler\n+\t#\tsuper Handler\n+\t#\n+\t#\tredef fun get(req, res) do res.send \"GETrequest received\"\n+\t# end\n+\t# ~~~\n+\tfun get(req: HttpRequest, res: HttpResponse) do end\n+\n+\t# POST handler.\n+\t#\n+\t# Exemple of route responding to POST requests.\n+\t# ~~~\n+\t# class PostHandler\n+\t#\tsuper Handler\n+\t#\n+\t#\tredef fun post(req, res) do res.send \"POST request received\"\n+\t# end\n+\t# ~~~\n+\tfun post(req: HttpRequest, res: HttpResponse) do end\n+\n+\t# PUT handler.\n+\t#\n+\t# Exemple of route responding to PUT requests.\n+\t# ~~~\n+\t# class PutHandler\n+\t#\tsuper Handler\n+\t#\n+\t#\tredef fun put(req, res) do res.send \"PUT request received\"\n+\t# end\n+\t# ~~~\n+\tfun put(req: HttpRequest, res: HttpResponse) do end\n+\n+\t# DELETE handler.\n+\t#\n+\t# Exemple of route responding to PUT requests.\n+\t# ~~~\n+\t# class DeleteHandler\n+\t#\tsuper Handler\n+\t#\n+\t#\tredef fun delete(req, res) do res.send \"DELETE request received\"\n+\t# end\n+\t# ~~~\n+\tfun delete(req: HttpRequest, res: HttpResponse) do end\n+end\n+\n+# Static files server.\n+#\n+# To serve static files such as images, CSS files, and JavaScript files, use the\n+# Popcorn built-in handler `StaticHandler`.\n+#\n+# Pass the name of the directory that contains the static assets to the StaticHandler\n+# init method to start serving the files directly.\n+# For example, use the following code to serve images, CSS files, and JavaScript files\n+# in a directory named `public`:\n+#\n+# ~~~\n+# var app = new App\n+# app.use(\"/\", new StaticHandler(\"public/\"))\n+# ~~~\n+#\n+# Now, you can load the files that are in the `public` directory:\n+#\n+# ~~~raw\n+# http://localhost:3000/images/trollface.jpg\n+# http://localhost:3000/css/style.css\n+# http://localhost:3000/js/app.js\n+# http://localhost:3000/hello.html\n+# ~~~\n+#\n+# Popcorn looks up the files relative to the static directory, so the name of the\n+# static directory is not part of the URL.\n+# To use multiple static assets directories, add the `StaticHandler` multiple times:\n+#\n+# ~~~\n+# app.use(\"/\", new StaticHandler(\"public/\"))\n+# app.use(\"/\", new StaticHandler(\"files/\"))\n+# ~~~\n+#\n+# Popcorn looks up the files in the order in which you set the static directories\n+# with the `use` method.\n+#\n+# To create a virtual path prefix (where the path does not actually exist in the file system)\n+# for files that are served by the `StaticHandler`, specify a mount path for the\n+# static directory, as shown below:\n+#\n+# ~~~\n+# app.use(\"/static/\", new StaticHandler(\"public/\"))\n+# ~~~\n+#\n+# Now, you can load the files that are in the public directory from the `/static`\n+# path prefix.\n+#\n+# ~~~raw\n+# http://localhost:3000/static/images/trollface.jpg\n+# http://localhost:3000/static/css/style.css\n+# http://localhost:3000/static/js/app.js\n+# http://localhost:3000/static/hello.html\n+# ~~~\n+#\n+# However, the path that you provide to the `StaticHandler` is relative to the\n+# directory from where you launch your app.\n+# If you run the app from another directory, it’s safer to use the absolute path of\n+# the directory that you want to serve.\n+class StaticHandler\n+\tsuper Handler\n+\n+\t# Static files directory to serve.\n+\tvar static_dir: String\n+\n+\tprivate var file_server: FileServer is lazy do\n+\t\tvar srv = new FileServer(static_dir)\n+\t\tsrv.show_directory_listing = false\n+\t\treturn srv\n+\tend\n+\n+\tredef fun handle(route, uri, req, res) do\n+\t\tvar answer = file_server.answer(req, route.uri_root(uri))\n+\t\tif answer.status_code == 200 then\n+\t\t\tres.status_code = answer.status_code\n+\t\t\tres.header.add_all answer.header\n+\t\t\tres.files.add_all answer.files\n+\t\t\tres.send\n+\t\telse if answer.status_code != 404 then\n+\t\t\tres.status_code = answer.status_code\n+\t\tend\n+\tend\n+end\n+\n+# Mountable routers\n+#\n+# Use the `Router` class to create modular, mountable route handlers.\n+# A Router instance is a complete middleware and routing system; for this reason,\n+# it is often referred to as a “mini-app”.\n+#\n+# The following example creates a router as a module, loads a middleware handler in it,\n+# defines some routes, and mounts the router module on a path in the main app.\n+#\n+# ~~~\n+# class AppHome\n+#\tsuper Handler\n+#\n+#\tredef fun get(req, res) do res.send \"Site Home\"\n+# end\n+#\n+# class UserLogger\n+#\tsuper Handler\n+#\n+#\tredef fun all(req, res) do print \"User logged\"\n+# end\n+#\n+# class UserHome\n+#\tsuper Handler\n+#\n+#\tredef fun get(req, res) do res.send \"User Home\"\n+# end\n+#\n+# class UserProfile\n+#\tsuper Handler\n+#\n+#\tredef fun get(req, res) do res.send \"User Profile\"\n+# end\n+#\n+# var user_router = new Router\n+# user_router.use(\"/*\", new UserLogger)\n+# user_router.use(\"/\", new UserHome)\n+# user_router.use(\"/profile\", new UserProfile)\n+#\n+# var app = new App\n+# app.use(\"/\", new AppHome)\n+# app.use(\"/user\", user_router)\n+# ~~~\n+#\n+# The app will now be able to handle requests to /user and /user/profile, as well\n+# as call the `Time` middleware handler that is specific to the route.\n+class Router\n+\tsuper Handler\n+\n+\t# List of handlers to match with requests.\n+\tprivate var handlers = new Map[AppRoute, Handler]\n+\n+\t# Register a `handler` for a route `path`.\n+\t#\n+\t# Route paths are matched in registration order.\n+\tfun use(path: String, handler: Handler) do\n+\t\tvar route\n+\t\tif handler isa Router or handler isa StaticHandler then\n+\t\t\troute = new AppGlobRoute(path)\n+\t\telse if path.has_suffix(\"*\") then\n+\t\t\troute = new AppGlobRoute(path)\n+\t\telse\n+\t\t\troute = new AppParamRoute(path)\n+\t\tend\n+\t\thandlers[route] = handler\n+\tend\n+\n+\tredef fun handle(route, uri, req, res) do\n+\t\tif not route.match(uri) then return\n+\t\tfor hroute, handler in handlers do\n+\t\t\thandler.handle(hroute, route.uri_root(uri), req, res)\n+\t\t\tif res.sent then break\n+\t\tend\n+\tend\n+end\n+\n+# Popcorn application.\n+#\n+# The `App`  is the main point of the application.\n+# It acts as a `Router` that holds the top level route handlers.\n+#\n+# Here an example to create a simple web app with Popcorn:\n+#\n+# ~~~\n+# import popcorn\n+#\n+# class HelloHandler\n+#\tsuper Handler\n+#\n+#\tredef fun get(req, res) do res.html \"<h1>Hello World!</h1>\"\n+# end\n+#\n+# var app = new App\n+# app.use(\"/\", new HelloHandler)\n+# # app.listen(\"localhost\", 3000)\n+# ~~~\n+#\n+# The Popcorn app listens on port 3000 for connections.\n+# The app responds with \"Hello World!\" for request to the root URL (`/`) or **route**.\n+# For every other path, it will respond with a **404 Not Found**.\n+#\n+# The `req` (request) and `res` (response) parameters are the same that nitcorn provides\n+# so you can do anything else you would do in your route without Popcorn involved.\n+#\n+# Run the app with the following command:\n+#\n+# ~~~bash\n+# nitc app.nit && ./app\n+# ~~~\n+#\n+# Then, load [http://localhost:3000](http://localhost:3000) in a browser to see the output.\n+class App\n+\tsuper Router\n+end\n+\n+redef class HttpResponse\n+\n+\t# Was this request sent by a handler?\n+\tvar sent = false\n+\n+\tprivate fun check_sent do\n+\t\tif sent then print \"Warning: Headers already sent!\"\n+\tend\n+\n+\t# Write data in body response and send it.\n+\tfun send(raw_data: nullable Writable, status: nullable Int) do\n+\t\tif raw_data != null then\n+\t\t\tbody += raw_data.write_to_string\n+\t\tend\n+\t\tif status != null then\n+\t\t\tstatus_code = status\n+\t\telse\n+\t\t\tstatus_code = 200\n+\t\tend\n+\t\tcheck_sent\n+\t\tsent = true\n+\tend\n+\n+\t# Write data as HTML and set the right content type header.\n+\tfun html(html: nullable Writable, status: nullable Int) do\n+\t\theader[\"Content-Type\"] = media_types[\"html\"].as(not null)\n+\t\tsend(html, status)\n+\tend\n+\n+\t# Write data as JSON and set the right content type header.\n+\tfun json(json: nullable Jsonable, status: nullable Int) do\n+\t\theader[\"Content-Type\"] = media_types[\"json\"].as(not null)\n+\t\tif json == null then\n+\t\t\tsend(null, status)\n+\t\telse\n+\t\t\tsend(json.to_json, status)\n+\t\tend\n+\tend\n+\n+\t# Redirect response to `location`\n+\tfun redirect(location: String, status: nullable Int) do\n+\t\theader[\"Location\"] = location\n+\t\tif status != null then\n+\t\t\tstatus_code = status\n+\t\telse\n+\t\t\tstatus_code = 302\n+\t\tend\n+\t\tcheck_sent\n+\t\tsent = true\n+\tend\n+\n+\t# TODO template, access app,\n+\tfun error(status: Int) do\n+\t\thtml(\"TODO\")","path":"lib/popcorn/pop_handlers.nit","position":425,"original_position":425,"commit_id":"9d9139de6f109e2fe016c1b49f53a6559008aaf1","original_commit_id":"5cbf497e6b8db8c71558de7969c0cfa989affa44","user":{"login":"privat","id":135828,"avatar_url":"https://avatars.githubusercontent.com/u/135828?v=3","gravatar_id":"","url":"https://api.github.com/users/privat","html_url":"https://github.com/privat","followers_url":"https://api.github.com/users/privat/followers","following_url":"https://api.github.com/users/privat/following{/other_user}","gists_url":"https://api.github.com/users/privat/gists{/gist_id}","starred_url":"https://api.github.com/users/privat/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/privat/subscriptions","organizations_url":"https://api.github.com/users/privat/orgs","repos_url":"https://api.github.com/users/privat/repos","events_url":"https://api.github.com/users/privat/events{/privacy}","received_events_url":"https://api.github.com/users/privat/received_events","type":"User","site_admin":false},"body":"html(\"TODO\", status)?","created_at":"2016-05-17T00:37:28Z","updated_at":"2016-05-17T00:37:28Z","html_url":"https://github.com/nitlang/nit/pull/2094#discussion_r63449725","pull_request_url":"https://api.github.com/repos/nitlang/nit/pulls/2094","_links":{"self":{"href":"https://api.github.com/repos/nitlang/nit/pulls/comments/63449725"},"html":{"href":"https://github.com/nitlang/nit/pull/2094#discussion_r63449725"},"pull_request":{"href":"https://api.github.com/repos/nitlang/nit/pulls/2094"}}},"pull_request":{"url":"https://api.github.com/repos/nitlang/nit/pulls/2094","id":70276942,"html_url":"https://github.com/nitlang/nit/pull/2094","diff_url":"https://github.com/nitlang/nit/pull/2094.diff","patch_url":"https://github.com/nitlang/nit/pull/2094.patch","issue_url":"https://api.github.com/repos/nitlang/nit/issues/2094","number":2094,"state":"open","locked":false,"title":"Popcorn","user":{"login":"Morriar","id":583144,"avatar_url":"https://avatars.githubusercontent.com/u/583144?v=3","gravatar_id":"","url":"https://api.github.com/users/Morriar","html_url":"https://github.com/Morriar","followers_url":"https://api.github.com/users/Morriar/followers","following_url":"https://api.github.com/users/Morriar/following{/other_user}","gists_url":"https://api.github.com/users/Morriar/gists{/gist_id}","starred_url":"https://api.github.com/users/Morriar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Morriar/subscriptions","organizations_url":"https://api.github.com/users/Morriar/orgs","repos_url":"https://api.github.com/users/Morriar/repos","events_url":"https://api.github.com/users/Morriar/events{/privacy}","received_events_url":"https://api.github.com/users/Morriar/received_events","type":"User","site_admin":false},"body":"# Popcorn\r\n\r\n**Why endure plain corn when you can pop it?!**\r\n\r\nPopcorn is a minimal yet powerful nit web application framework that provides cool\r\nfeatures for lazy developpers.\r\n\r\nPopcorn is built over nitcorn to provide a clean and user friendly interface\r\nwithout all the boiler plate code.\r\n\r\n## What does it taste like?\r\n\r\nSet up is quick and easy as 10 lines of code.\r\nCreate a file `app.nit` and add the following code:\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass HelloHandler\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do res.html \"<h1>Hello World!</h1>\"\r\nend\r\n\r\nvar app = new App\r\napp.use(\"/\", new HelloHandler)\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\nThe Popcorn app listens on port 3000 for connections.\r\nThe app responds with \"Hello World!\" for requests to the root URL (`/`) or **route**.\r\nFor every other path, it will respond with a **404 Not Found**.\r\n\r\nThe `req` (request) and `res` (response) parameters are the same that nitcorn provides\r\nso you can do anything else you would do in your route without Popcorn involved.\r\n\r\nRun the app with the following command:\r\n\r\n~~~bash\r\nnitc app.nit && ./app\r\n~~~\r\n\r\nThen, load [http://localhost:3000](http://localhost:3000) in a browser to see the output.\r\n\r\nThis is why we love popcorn!\r\n\r\n## Basic routing\r\n\r\n**Routing** refers to determining how an application responds to a client request\r\nto a particular endpoint, which is a URI (or path) and a specific HTTP request\r\nmethod GET, POST, PUT or DELETE (other methods are not suported yet).\r\n\r\nEach route can have one or more handler methods, which are executed when the route is matched.\r\n\r\nRoute handlers definition takes the following form:\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass MyHandler\r\n\tsuper Handler\r\n\r\n\tredef fun METHOD(req, res) do end\r\nend\r\n~~~\r\n\r\nWhere:\r\n* `MyHandler` is the name of the handler you will add to the app.\r\n* `METHOD` can be replaced by `get`, `post`, `put` or `delete`.\r\n\r\nThe following example responds with `Hello World!` to GET and POST requests:\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass MyHandler\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do res.send \"Got a GET request\"\r\n\tredef fun post(req, res) do res.send \"Got a POST request\"\r\nend\r\n~~~\r\n\r\nTo make your handler responds to a specific route, you have to add it to the app.\r\n\r\nRespond to POST request on the root route (`/`), the application's home page:\r\n\r\n~~~nit\r\nvar app = new App\r\napp.use(\"/\", new MyHandler)\r\n~~~\r\n\r\nRespond to a request to the `/user` route:\r\n\r\n~~~nit\r\napp.use(\"/user\", new MyHandler)\r\n~~~\r\n\r\nFor more details about routing, see the routing section.\r\n\r\n## Serving static files with Popcorn\r\n\r\nTo serve static files such as images, CSS files, and JavaScript files, use the\r\nPopcorn built-in handler `StaticHandler`.\r\n\r\nPass the name of the directory that contains the static assets to the StaticHandler\r\ninit method to start serving the files directly.\r\nFor example, use the following code to serve images, CSS files, and JavaScript files\r\nin a directory named `public`:\r\n\r\n~~~nit\r\napp.use(\"/\", new StaticHandler(\"public/\"))\r\n~~~\r\n\r\nNow, you can load the files that are in the `public` directory:\r\n\r\n~~~raw\r\nhttp://localhost:3000/images/trollface.jpg\r\nhttp://localhost:3000/css/style.css\r\nhttp://localhost:3000/js/app.js\r\nhttp://localhost:3000/hello.html\r\n~~~\r\n\r\nPopcorn looks up the files relative to the static directory, so the name of the\r\nstatic directory is not part of the URL.\r\nTo use multiple static assets directories, add the `StaticHandler` multiple times:\r\n\r\n~~~nit\r\napp.use(\"/\", new StaticHandler(\"public/\"))\r\napp.use(\"/\", new StaticHandler(\"files/\"))\r\n~~~\r\n\r\nPopcorn looks up the files in the order in which you set the static directories\r\nwith the `use` method.\r\n\r\nTo create a virtual path prefix (where the path does not actually exist in the file system)\r\nfor files that are served by the `StaticHandler`, specify a mount path for the\r\nstatic directory, as shown below:\r\n\r\n~~~nit\r\napp.use(\"/static/\", new StaticHandler(\"public/\"))\r\n~~~\r\n\r\nNow, you can load the files that are in the public directory from the `/static`\r\npath prefix.\r\n\r\n~~~raw\r\nhttp://localhost:3000/static/images/trollface.jpg\r\nhttp://localhost:3000/static/css/style.css\r\nhttp://localhost:3000/static/js/app.js\r\nhttp://localhost:3000/static/hello.html\r\n~~~\r\n\r\nHowever, the path that you provide to the `StaticHandler` is relative to the\r\ndirectory from where you launch your app.\r\nIf you run the app from another directory, it’s safer to use the absolute path of\r\nthe directory that you want to serve.\r\n\r\n## Advanced Routing\r\n\r\n**Routing** refers to the definition of application end points (URIs) and how\r\nthey respond to client requests. For an introduction to routing, see the Basic routing\r\nsection.\r\n\r\nThe following code is an example of a very basic route.\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass HelloHandler\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do res.send \"Hello World!\"\r\nend\r\n\r\nvar app = new App\r\napp.use(\"/\", new HelloHandler)\r\n~~~\r\n\r\n### Route methods\r\n\r\nA **route method** is derived from one of the HTTP methods, and is attached to an\r\ninstance of the Handler class.\r\n\r\nThe following code is an example of routes that are defined for the GET and the POST\r\nmethods to the root of the app.\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass GetPostHandler\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do res.send \"GET request to the homepage\"\r\n\tredef fun post(req, res) do res.send \"POST request to the homepage\"\r\nend\r\n\r\nvar app = new App\r\napp.use(\"/\", new GetPostHandler)\r\n~~~\r\n\r\nPopcorn supports the following routing methods that correspond to HTTP methods:\r\nget, post, put, and delete.\r\n\r\nThe request query string is accessed through the `req` parameter:\r\n\r\n~~~nit\r\nimport popcorn\r\nimport template\r\n\r\nclass QueryStringHandler\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do\r\n\t\tvar tpl = new Template\r\n\t\ttpl.addn \"URI: {req.uri}\"\r\n\t\ttpl.addn \"Query string: {req.query_string}\"\r\n\t\tfor name, arg in req.get_args do\r\n\t\t\ttpl.addn \"{name}: {arg}\"\r\n\t\tend\r\n        res.send tpl\r\n\tend\r\nend\r\n\r\nvar app = new App\r\napp.use(\"/\", new QueryStringHandler)\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\nPost parameters can also be accessed through the `req` parameter:\r\n\r\n~~~nit\r\nimport popcorn\r\nimport template\r\n\r\nclass PostHandler\r\n\tsuper Handler\r\n\r\n\tredef fun post(req, res) do\r\n\t\tvar tpl = new Template\r\n\t\ttpl.addn \"URI: {req.uri}\"\r\n\t\ttpl.addn \"Body: {req.body}\"\r\n\t\tfor name, arg in req.post_args do\r\n\t\t\ttpl.addn \"{name}: {arg}\"\r\n\t\tend\r\n        res.send tpl\r\n\tend\r\nend\r\n\r\nvar app = new App\r\napp.use(\"/\", new PostHandler)\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\nThere is a special routing method, `all(res, req)`, which is not derived from any\r\nHTTP method. This method is used to respond at a path for all request methods.\r\n\r\nIn the following example, the handler will be executed for requests to \"/user\"\r\nwhether you are using GET, POST, PUT, DELETE, or any other HTTP request method.\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass AllHandler\r\n\tsuper Handler\r\n\r\n\tredef fun all(req, res) do res.send \"Every request to the homepage\"\r\nend\r\n~~~\r\n\r\nUsing the `all` method you can also implement other HTTP request methods.\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass MergeHandler\r\n\tsuper Handler\r\n\r\n\tredef fun all(req, res) do\r\n\t\tif req.method == \"MERGE\" then\r\n\t\t\t# handle that method\r\n\t\telse super # keep handle GET, POST, PUT and DELETE methods\r\n\tend\r\nend\r\n~~~\r\n\r\n### Route paths\r\n\r\n**Route paths**, in combination with a request handlers, define the endpoints at\r\nwhich requests can be made.\r\nRoute paths can be strings, parameterized strings or glob patterns.\r\nQuery strings such as `?q=foo`are not part of the route path.\r\n\r\nPopcorn uses the `Handler::match(uri)` method to match the route paths.\r\n\r\nHere are some examples of route paths based on strings.\r\n\r\nThis route path will match requests to the root route, `/`.\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass MyHandler\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do res.send \"Got a GET request\"\r\nend\r\n\r\nvar app = new App\r\napp.use(\"/\", new MyHandler)\r\n~~~\r\n\r\nThis route path will match requests to `/about`.\r\n\r\n~~~nit\r\napp.use(\"/about\", new MyHandler)\r\n~~~\r\n\r\nThis route path will match requests to `/random.text`.\r\n\r\n~~~nit\r\napp.use(\"/random.text\", new MyHandler)\r\n~~~\r\n\r\nDuring the query/response process, routes are matched by order of declaration\r\nthrough the `App::use` method.\r\n\r\nThe app declared in this example will try to match the routes in this order:\r\n\r\n1. `/`\r\n2. `/about`\r\n3. `/random.text`\r\n\r\n### Route parameters\r\n\r\n**Route parameters** are variable parts of a route path. They can be used to path\r\narguments within the URI.\u0013\r\nParameters in a route are prefixed with a colon `:` like in `:userId`, `:year`.\r\n\r\nThe following example declares a handler `UserHome` that responds with the `user`\r\nname.\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass UserHome\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do\r\n\t\tvar user = req.param(\"user\")\r\n\t\tif user != null then\r\n\t\t\tres.send \"Hello {user}\"\r\n\t\telse\r\n\t\t\tres.send(\"Nothing received\", 400)\r\n\t\tend\r\n\tend\r\nend\r\n\r\nvar app = new App\r\napp.use(\"/:user\", new UserHome)\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\nThe `UserHome` handler listen to every path matching `/:user`. This can be `/Morriar`,\r\n`/10`, ... but not `/Morriar/profile` since route follow the strict matching rule.\r\n\r\n### Glob routes\r\n\r\n**Glob routes** are routes that match only on a prefix, thus accepting a wider range\r\nof URI.\r\nGlob routes end with the symbol `*`.\r\n\r\nHere we define a `UserItem` handler that will respond to any URI matching the prefix\r\n`/user/:user/item/:item`.\r\nNote that glob route are compatible with route parameters.\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass UserItem\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do\r\n\t\tvar user = req.param(\"user\")\r\n\t\tvar item = req.param(\"item\")\r\n\t\tif user == null or item == null then\r\n\t\t\tres.send(\"Nothing received\", 400)\r\n\t\telse\r\n\t\t\tres.send \"Here the item {item} of the use {user}.\"\r\n\t\tend\r\n\tend\r\nend\r\n\r\nvar app = new App\r\napp.use(\"/user/:user/item/:item/*\", new UserItem)\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\n## Response methods\r\n\r\nThe methods on the response object (`res`), can is used to manipulate the\r\nrequest-response cycle.\r\nIf none of these methods are called from a route handler, the client request will\r\nreceive a `404 Not found` error.\r\n\r\n* `res.html()` Send a HTML response.\r\n* `res.json()` Send a JSON response.\r\n* `res.redirect()` Redirect a request.\r\n* `res.send()` Send a response of various types.\r\n* `res.error()` Set the response status code and send its message as the response body.\r\n\r\n## Middlewares\r\n\r\n### Overview\r\n\r\n**Middleware** handlers are handlers that typically do not send `HttpResponse` responses.\r\nMiddleware handlers can perform the following tasks:\r\n\r\n* Execute any code.\r\n* Make changes to the request and the response objects.\r\n* End its action and pass to the next handler in the stack.\r\n\r\nIf a middleware handler makes a call to `res.send()`, it provoques the end the\r\nrequest-response cycle and the response is sent to the client.\r\n\r\n### Ultra simple logger example\r\n\r\nHere is an example of a simple “Hello World” Popcorn application.\r\nWe add a middleware handler to the application called MyLogger that prints a simple\r\nlog message in the app stdout.\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass MyLogger\r\n\tsuper Handler\r\n\r\n\tredef fun all(req, res) do print \"Request Logged!\"\r\nend\r\n\r\nclass HelloHandler\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do res.send \"Hello World!\"\r\nend\r\n\r\n\r\nvar app = new App\r\napp.use(\"/*\", new MyLogger)\r\napp.use(\"/\", new HelloHandler)\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\nBy using the `MyLogger` handler to the route `/*` we ensure that every requests\r\n(even 404 ones) pass through the middleware handler.\r\nThis handler just prints “Request Logged!” when a request is received.\r\n\r\nThe order of middleware loading is important: middleware functions that are loaded first are also executed first.\r\nIn the above example, `MyLogger` will be executed before `HelloHandler`.\r\n\r\n### Ultra cool, more advanced logger example\r\n\r\nNext, we’ll create a middleware handler called “LogHandler” that prints the requested\r\nuri, the response status and the time it took to Popcorn to process the request.\r\n\r\n~~~nit\r\nimport popcorn\r\nimport realtime\r\n\r\nredef class HttpRequest\r\n\t# Time that request was received by the Popcorn app.\r\n\tvar timer: nullable Clock = null\r\nend\r\n\r\nclass RequestTimeHandler\r\n\tsuper Handler\r\n\r\n\tredef fun all(req, res) do req.timer = new Clock\r\nend\r\n\r\nclass LogHandler\r\n\tsuper Handler\r\n\r\n\tredef fun all(req, res) do\r\n\t\tvar timer = req.timer\r\n\t\tif timer != null then\r\n\t\t\tprint \"{req.method} {req.uri} {res.color_status} ({timer.total})\"\r\n\t\telse\r\n\t\t\tprint \"{req.method} {req.uri} {res.color_status}\"\r\n\t\tend\r\n\tend\r\nend\r\n\r\nclass HelloHandler\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do res.send \"Hello World!\"\r\nend\r\n\r\nvar app = new App\r\napp.use(\"/*\", new RequestTimeHandler)\r\napp.use(\"/\", new HelloHandler)\r\napp.use(\"/*\", new LogHandler)\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\nFirst, we attach a new attribute `timer` to every `HttpRequest`.\r\nDoing so we can access our data from all handlers that import our module, directly\r\nfrom the `req` parameter.\r\n\r\nWe use the new middleware called `RequestTimeHandler` to initialize the request timer.\r\n\r\nFinally, our `LogHandler` will display a bunch of data and use the request `timer`\r\nto display the time it took to process the request.\r\n\r\nThe app now uses the `RequestTimeHandler` middleware for every requests received\r\nby the Popcorn app.\r\nThe page is processed the `HelloHandler` to display the index page.\r\nAnd, before every response is sent, the `LogHandler` is activated to display the\r\nlog line.\r\n\r\nBecause you have access to the request object, the response object, and all the\r\nPopcorn API, the possibilities with middleware functions are endless.\r\n\r\n### Built-in middlewares\r\n\r\nStarting with version 0.1, Popcorn provide a set of built-in middleware that can\r\nbe used to develop your app faster.\r\n\r\n* `RequestClock`: initializes requests clock.\r\n* `ConsoleLog`: displays resquest and response status in console (can be used with `RequestClock`).\r\n* `SessionInit`: initializes requests session (see the `Sessions` section).\r\n* `StaticServer`: serves static files (see the `Serving static files with Popcorn` section).\r\n* `Router`: a mountable mini-app (see the `Mountable routers` section).\r\n\r\n## Mountable routers\r\n\r\nUse the `Router` class to create modular, mountable route handlers.\r\nA Router instance is a complete middleware and routing system; for this reason,\r\nit is often referred to as a “mini-app”.\r\n\r\nThe following example creates a router as a module, loads a middleware handler in it,\r\ndefines some routes, and mounts the router module on a path in the main app.\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass AppHome\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do res.send \"Site Home\"\r\nend\r\n\r\nclass UserLogger\r\n\tsuper Handler\r\n\r\n\tredef fun all(req, res) do print \"User logged\"\r\nend\r\n\r\nclass UserHome\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do res.send \"User Home\"\r\nend\r\n\r\nclass UserProfile\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do res.send \"User Profile\"\r\nend\r\n\r\nvar user_router = new Router\r\nuser_router.use(\"/*\", new UserLogger)\r\nuser_router.use(\"/\", new UserHome)\r\nuser_router.use(\"/profile\", new UserProfile)\r\n\r\nvar app = new App\r\napp.use(\"/\", new AppHome)\r\napp.use(\"/user\", user_router)\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\nThe app will now be able to handle requests to /user and /user/profile, as well\r\nas call the `Time` middleware handler that is specific to the route.\r\n\r\n## Error handling\r\n\r\n**Error handling** is based on middleware handlers.\r\n\r\nDefine error-handling middlewares in the same way as other middleware handlers:\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nclass SimpleErrorHandler\r\n\tsuper Handler\r\n\r\n\tredef fun all(req, res) do\r\n\t\tif res.status_code != 200 then\r\n\t\t\tprint \"An error occurred! {res.status_code})\"\r\n\t\tend\r\n\tend\r\nend\r\n\r\nclass HelloHandler\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do res.send \"Hello World!\"\r\nend\r\n\r\nvar app = new App\r\napp.use(\"/\", new HelloHandler)\r\napp.use(\"/*\", new SimpleErrorHandler)\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\nIn this example, every non-200 response is caught by the `SimpleErrorHandler`\r\nthat print an error in stdout.\r\n\r\nBy defining multiple middleware error handlers, you can take multiple action depending\r\non the kind of error or the kind of interface you provide (HTML, XML, JSON...).\r\n\r\nHere an example of the 404 custom error page in HTML:\r\n\r\n~~~nit\r\nimport popcorn\r\nimport template\r\n\r\nclass HtmlErrorTemplate\r\n\tsuper Template\r\n\r\n\tvar status: Int\r\n\tvar message: nullable String\r\n\r\n\tredef fun rendering do add \"\"\"\r\n\t\t<!DOCTYPE html>\r\n\t\t<html>\r\n\t\t<head>\r\n\t\t\t<meta charset=\"utf-8\">\r\n\t\t\t<title>{{{message or else status}}}</title>\r\n\t\t</head>\r\n\t\t<body>\r\n\t\t<h1>{{{status}}} {{{message or else \"\"}}}</h1>\r\n\t\t</body>\r\n\t\t</html>\"\"\"\r\nend\r\n\r\nclass HtmlErrorHandler\r\n\tsuper Handler\r\n\r\n\tredef fun all(req, res) do\r\n\t\tif res.status_code != 200 then\r\n\t\t\tres.send(new HtmlErrorTemplate(res.status_code, \"An error occurred!\"))\r\n\t\tend\r\n\tend\r\nend\r\n\r\nvar app = new App\r\napp.use(\"/*\", new HtmlErrorHandler)\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\n## Sessions\r\n\r\n**Sessions** can be used thanks to the built-in `SessionMiddleware`.\r\n\r\nHere a simple example of login button that define a value in the `req` session.\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nredef class Session\r\n\tvar is_logged = false\r\nend\r\n\r\nclass AppLogin\r\n\tsuper Handler\r\n\r\n\tredef fun get(req, res) do\r\n\t\tres.html \"\"\"\r\n\t\t<p>Is logged: {{{req.session.as(not null).is_logged}}}</p>\r\n\t\t<form action=\"/\" method=\"POST\">\r\n\t\t\t<input type=\"submit\" value=\"Login\" />\r\n\t\t</form>\"\"\"\r\n\tend\r\n\r\n\tredef fun post(req, res) do\r\n\t\treq.session.as(not null).is_logged = true\r\n\t\tres.redirect(\"/\")\r\n\tend\r\nend\r\n\r\nvar app = new App\r\napp.use(\"/*\", new SessionInit)\r\napp.use(\"/\", new AppLogin)\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\nNotice the use of the `SessionInit` on the `/*` route. You must use the\r\n`SessionInit` first to initialize the request session.\r\nWithout that, your request session will be set to `null`.\r\nIf you don't use sessions in your app, you do not need to include that middleware.\r\n\r\n## Database integration\r\n\r\n### Mongo DB\r\n\r\nIf you want to persist your data, Popcorn works well with MongoDB.\r\n\r\nIn this example, we will show how to store and list user with a Mongo database.\r\n\r\nFirst let's define a handler that access the database to list all the user.\r\nThe mongo database reference is passed to the UserList handler through the `db` attribute.\r\n\r\nThen we define a handler that displays the user creation form on GET requests.\r\nPOST requests are used to save the user data.\r\n\r\n~~~nit\r\nimport popcorn\r\nimport mongodb\r\nimport template\r\n\r\nclass UserList\r\n\tsuper Handler\r\n\r\n\tvar db: MongoDb\r\n\r\n\tredef fun get(req, res) do\r\n\t\tvar users = db.collection(\"users\").find_all(new JsonObject)\r\n\r\n\t\tvar tpl = new Template\r\n\t\ttpl.add \"<h1>Users</h1>\"\r\n\t\ttpl.add \"<table>\"\r\n\t\tfor user in users do\r\n\t\t\ttpl.add \"\"\"<tr>\r\n\t\t\t\t<td>{{{user[\"login\"] or else \"null\"}}}</td>\r\n\t\t\t\t<td>{{{user[\"password\"] or else \"null\"}}}</td>\r\n\t\t\t</tr>\"\"\"\r\n\t\tend\r\n\t\ttpl.add \"</table>\"\r\n\t\tres.html tpl\r\n\tend\r\nend\r\n\r\nclass UserForm\r\n\tsuper Handler\r\n\r\n\tvar db: MongoDb\r\n\r\n\tredef fun get(req, res) do\r\n\t\tvar tpl = new Template\r\n\t\ttpl.add \"\"\"<h1>Add a new user</h1>\r\n\t\t<form action=\"/new\" method=\"POST\">\r\n\t\t\t<input type=\"text\" name=\"login\" />\r\n\t\t\t<input type=\"password\" name=\"password\" />\r\n\t\t\t<input type=\"submit\" value=\"save\" />\r\n\t\t</form>\"\"\"\r\n\t\tres.html tpl\r\n\tend\r\n\r\n\tredef fun post(req, res) do\r\n\t\tvar json = new JsonObject\r\n\t\tjson[\"login\"] = req.post_args[\"login\"]\r\n\t\tjson[\"password\"] = req.post_args[\"password\"]\r\n\t\tdb.collection(\"users\").insert(json)\r\n\t\tres.redirect \"/\"\r\n\tend\r\nend\r\n\r\nvar mongo = new MongoClient(\"mongodb://localhost:27017/\")\r\nvar db = mongo.database(\"mongo_example\")\r\n\r\nvar app = new App\r\napp.use(\"/\", new UserList(db))\r\napp.use(\"/new\", new UserForm(db))\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\n## Angular.JS integration\r\n\r\nLoving [AngularJS](https://angularjs.org/)? Popcorn is made for Angular and for you!\r\n\r\nUsing the StaticHandler with a glob route, you can easily redirect all HTTP requests\r\nto your angular controller:\r\n\r\n~~~nit\r\nimport popcorn\r\n\r\nvar app = new App\r\napp.use(\"/*\", new StaticHandler(\"my-ng-app/\"))\r\n# app.listen(\"localhost\", 3000)\r\n~~~\r\n\r\nSee the examples for a more detailed use case working with a JSON API.","created_at":"2016-05-16T23:13:35Z","updated_at":"2016-05-17T00:37:28Z","closed_at":null,"merged_at":null,"merge_commit_sha":"673a5e854b34587ad76d0ff875c65dfef519a201","assignee":{"login":"Morriar","id":583144,"avatar_url":"https://avatars.githubusercontent.com/u/583144?v=3","gravatar_id":"","url":"https://api.github.com/users/Morriar","html_url":"https://github.com/Morriar","followers_url":"https://api.github.com/users/Morriar/followers","following_url":"https://api.github.com/users/Morriar/following{/other_user}","gists_url":"https://api.github.com/users/Morriar/gists{/gist_id}","starred_url":"https://api.github.com/users/Morriar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Morriar/subscriptions","organizations_url":"https://api.github.com/users/Morriar/orgs","repos_url":"https://api.github.com/users/Morriar/repos","events_url":"https://api.github.com/users/Morriar/events{/privacy}","received_events_url":"https://api.github.com/users/Morriar/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/nitlang/nit/pulls/2094/commits","review_comments_url":"https://api.github.com/repos/nitlang/nit/pulls/2094/comments","review_comment_url":"https://api.github.com/repos/nitlang/nit/pulls/comments{/number}","comments_url":"https://api.github.com/repos/nitlang/nit/issues/2094/comments","statuses_url":"https://api.github.com/repos/nitlang/nit/statuses/9d9139de6f109e2fe016c1b49f53a6559008aaf1","head":{"label":"Morriar:popcorn","ref":"popcorn","sha":"9d9139de6f109e2fe016c1b49f53a6559008aaf1","user":{"login":"Morriar","id":583144,"avatar_url":"https://avatars.githubusercontent.com/u/583144?v=3","gravatar_id":"","url":"https://api.github.com/users/Morriar","html_url":"https://github.com/Morriar","followers_url":"https://api.github.com/users/Morriar/followers","following_url":"https://api.github.com/users/Morriar/following{/other_user}","gists_url":"https://api.github.com/users/Morriar/gists{/gist_id}","starred_url":"https://api.github.com/users/Morriar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Morriar/subscriptions","organizations_url":"https://api.github.com/users/Morriar/orgs","repos_url":"https://api.github.com/users/Morriar/repos","events_url":"https://api.github.com/users/Morriar/events{/privacy}","received_events_url":"https://api.github.com/users/Morriar/received_events","type":"User","site_admin":false},"repo":{"id":1456691,"name":"nit","full_name":"Morriar/nit","owner":{"login":"Morriar","id":583144,"avatar_url":"https://avatars.githubusercontent.com/u/583144?v=3","gravatar_id":"","url":"https://api.github.com/users/Morriar","html_url":"https://github.com/Morriar","followers_url":"https://api.github.com/users/Morriar/followers","following_url":"https://api.github.com/users/Morriar/following{/other_user}","gists_url":"https://api.github.com/users/Morriar/gists{/gist_id}","starred_url":"https://api.github.com/users/Morriar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Morriar/subscriptions","organizations_url":"https://api.github.com/users/Morriar/orgs","repos_url":"https://api.github.com/users/Morriar/repos","events_url":"https://api.github.com/users/Morriar/events{/privacy}","received_events_url":"https://api.github.com/users/Morriar/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/Morriar/nit","description":"Nit language","fork":true,"url":"https://api.github.com/repos/Morriar/nit","forks_url":"https://api.github.com/repos/Morriar/nit/forks","keys_url":"https://api.github.com/repos/Morriar/nit/keys{/key_id}","collaborators_url":"https://api.github.com/repos/Morriar/nit/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/Morriar/nit/teams","hooks_url":"https://api.github.com/repos/Morriar/nit/hooks","issue_events_url":"https://api.github.com/repos/Morriar/nit/issues/events{/number}","events_url":"https://api.github.com/repos/Morriar/nit/events","assignees_url":"https://api.github.com/repos/Morriar/nit/assignees{/user}","branches_url":"https://api.github.com/repos/Morriar/nit/branches{/branch}","tags_url":"https://api.github.com/repos/Morriar/nit/tags","blobs_url":"https://api.github.com/repos/Morriar/nit/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/Morriar/nit/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/Morriar/nit/git/refs{/sha}","trees_url":"https://api.github.com/repos/Morriar/nit/git/trees{/sha}","statuses_url":"https://api.github.com/repos/Morriar/nit/statuses/{sha}","languages_url":"https://api.github.com/repos/Morriar/nit/languages","stargazers_url":"https://api.github.com/repos/Morriar/nit/stargazers","contributors_url":"https://api.github.com/repos/Morriar/nit/contributors","subscribers_url":"https://api.github.com/repos/Morriar/nit/subscribers","subscription_url":"https://api.github.com/repos/Morriar/nit/subscription","commits_url":"https://api.github.com/repos/Morriar/nit/commits{/sha}","git_commits_url":"https://api.github.com/repos/Morriar/nit/git/commits{/sha}","comments_url":"https://api.github.com/repos/Morriar/nit/comments{/number}","issue_comment_url":"https://api.github.com/repos/Morriar/nit/issues/comments{/number}","contents_url":"https://api.github.com/repos/Morriar/nit/contents/{+path}","compare_url":"https://api.github.com/repos/Morriar/nit/compare/{base}...{head}","merges_url":"https://api.github.com/repos/Morriar/nit/merges","archive_url":"https://api.github.com/repos/Morriar/nit/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/Morriar/nit/downloads","issues_url":"https://api.github.com/repos/Morriar/nit/issues{/number}","pulls_url":"https://api.github.com/repos/Morriar/nit/pulls{/number}","milestones_url":"https://api.github.com/repos/Morriar/nit/milestones{/number}","notifications_url":"https://api.github.com/repos/Morriar/nit/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/Morriar/nit/labels{/name}","releases_url":"https://api.github.com/repos/Morriar/nit/releases{/id}","deployments_url":"https://api.github.com/repos/Morriar/nit/deployments","created_at":"2011-03-08T22:16:33Z","updated_at":"2015-06-06T03:02:03Z","pushed_at":"2016-05-16T23:09:41Z","git_url":"git://github.com/Morriar/nit.git","ssh_url":"git@github.com:Morriar/nit.git","clone_url":"https://github.com/Morriar/nit.git","svn_url":"https://github.com/Morriar/nit","homepage":"http://nitlanguage.org","size":113765,"stargazers_count":3,"watchers_count":3,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":2,"mirror_url":null,"open_issues_count":15,"forks":2,"open_issues":15,"watchers":3,"default_branch":"master"}},"base":{"label":"nitlang:master","ref":"master","sha":"17543d4946687dee470b1d2f813e29b16a87a90c","user":{"login":"nitlang","id":5420298,"avatar_url":"https://avatars.githubusercontent.com/u/5420298?v=3","gravatar_id":"","url":"https://api.github.com/users/nitlang","html_url":"https://github.com/nitlang","followers_url":"https://api.github.com/users/nitlang/followers","following_url":"https://api.github.com/users/nitlang/following{/other_user}","gists_url":"https://api.github.com/users/nitlang/gists{/gist_id}","starred_url":"https://api.github.com/users/nitlang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nitlang/subscriptions","organizations_url":"https://api.github.com/users/nitlang/orgs","repos_url":"https://api.github.com/users/nitlang/repos","events_url":"https://api.github.com/users/nitlang/events{/privacy}","received_events_url":"https://api.github.com/users/nitlang/received_events","type":"Organization","site_admin":false},"repo":{"id":328597,"name":"nit","full_name":"nitlang/nit","owner":{"login":"nitlang","id":5420298,"avatar_url":"https://avatars.githubusercontent.com/u/5420298?v=3","gravatar_id":"","url":"https://api.github.com/users/nitlang","html_url":"https://github.com/nitlang","followers_url":"https://api.github.com/users/nitlang/followers","following_url":"https://api.github.com/users/nitlang/following{/other_user}","gists_url":"https://api.github.com/users/nitlang/gists{/gist_id}","starred_url":"https://api.github.com/users/nitlang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nitlang/subscriptions","organizations_url":"https://api.github.com/users/nitlang/orgs","repos_url":"https://api.github.com/users/nitlang/repos","events_url":"https://api.github.com/users/nitlang/events{/privacy}","received_events_url":"https://api.github.com/users/nitlang/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/nitlang/nit","description":"Nit language","fork":false,"url":"https://api.github.com/repos/nitlang/nit","forks_url":"https://api.github.com/repos/nitlang/nit/forks","keys_url":"https://api.github.com/repos/nitlang/nit/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nitlang/nit/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nitlang/nit/teams","hooks_url":"https://api.github.com/repos/nitlang/nit/hooks","issue_events_url":"https://api.github.com/repos/nitlang/nit/issues/events{/number}","events_url":"https://api.github.com/repos/nitlang/nit/events","assignees_url":"https://api.github.com/repos/nitlang/nit/assignees{/user}","branches_url":"https://api.github.com/repos/nitlang/nit/branches{/branch}","tags_url":"https://api.github.com/repos/nitlang/nit/tags","blobs_url":"https://api.github.com/repos/nitlang/nit/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nitlang/nit/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nitlang/nit/git/refs{/sha}","trees_url":"https://api.github.com/repos/nitlang/nit/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nitlang/nit/statuses/{sha}","languages_url":"https://api.github.com/repos/nitlang/nit/languages","stargazers_url":"https://api.github.com/repos/nitlang/nit/stargazers","contributors_url":"https://api.github.com/repos/nitlang/nit/contributors","subscribers_url":"https://api.github.com/repos/nitlang/nit/subscribers","subscription_url":"https://api.github.com/repos/nitlang/nit/subscription","commits_url":"https://api.github.com/repos/nitlang/nit/commits{/sha}","git_commits_url":"https://api.github.com/repos/nitlang/nit/git/commits{/sha}","comments_url":"https://api.github.com/repos/nitlang/nit/comments{/number}","issue_comment_url":"https://api.github.com/repos/nitlang/nit/issues/comments{/number}","contents_url":"https://api.github.com/repos/nitlang/nit/contents/{+path}","compare_url":"https://api.github.com/repos/nitlang/nit/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nitlang/nit/merges","archive_url":"https://api.github.com/repos/nitlang/nit/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nitlang/nit/downloads","issues_url":"https://api.github.com/repos/nitlang/nit/issues{/number}","pulls_url":"https://api.github.com/repos/nitlang/nit/pulls{/number}","milestones_url":"https://api.github.com/repos/nitlang/nit/milestones{/number}","notifications_url":"https://api.github.com/repos/nitlang/nit/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nitlang/nit/labels{/name}","releases_url":"https://api.github.com/repos/nitlang/nit/releases{/id}","deployments_url":"https://api.github.com/repos/nitlang/nit/deployments","created_at":"2009-10-06T15:03:00Z","updated_at":"2016-04-17T19:33:08Z","pushed_at":"2016-05-17T00:34:20Z","git_url":"git://github.com/nitlang/nit.git","ssh_url":"git@github.com:nitlang/nit.git","clone_url":"https://github.com/nitlang/nit.git","svn_url":"https://github.com/nitlang/nit","homepage":"http://nitlanguage.org","size":132188,"stargazers_count":94,"watchers_count":94,"language":"C","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":50,"mirror_url":null,"open_issues_count":153,"forks":50,"open_issues":153,"watchers":94,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/nitlang/nit/pulls/2094"},"html":{"href":"https://github.com/nitlang/nit/pull/2094"},"issue":{"href":"https://api.github.com/repos/nitlang/nit/issues/2094"},"comments":{"href":"https://api.github.com/repos/nitlang/nit/issues/2094/comments"},"review_comments":{"href":"https://api.github.com/repos/nitlang/nit/pulls/2094/comments"},"review_comment":{"href":"https://api.github.com/repos/nitlang/nit/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/nitlang/nit/pulls/2094/commits"},"statuses":{"href":"https://api.github.com/repos/nitlang/nit/statuses/9d9139de6f109e2fe016c1b49f53a6559008aaf1"}}}}