{"action":"created","comment":{"url":"https://api.github.com/repos/auth0/blog/pulls/comments/128388287","pull_request_review_id":51060944,"id":128388287,"diff_hunk":"@@ -0,0 +1,316 @@\n+---\n+layout: post\n+title: \"The Highs & Lows of Crystal - an intro to Crystal Lang\"\n+description: \"Crystal Language claims to be as fast as C and as easy to learn as Ruby.  Here, I put it to the test and give my 2 cents!\"\n+date: 2017-06-21 15:54\n+category: Technical Guide\n+author:\n+  name: \"Robin Percy\"\n+  url: \"https://twitter.com/rbin\"\n+  mail: \"robin@percy.pw\"\n+  avatar: \"https://secure.gravatar.com/avatar/685342d5e7f42c3ab8d251d7d4a53308?s=100&d=mm&r=g\"\n+tags:\n+- Crystal Language\n+- Low-Level Programming\n+- Systems Programming\n+---\n+\n+\n+The Highs and Lows of Crystal  (An Introduction to the Language)\n+====================================\n+\n+Crystal is a statically-typed, compiled, systems programming language with the aims of being as fast as c/c++, while having a syntax as readable as Ruby.  This article is an introduction to the Crystal Language, through the eyes of a polyglot programmer.  Being a former programmer of both C and Ruby, I have been able to explore the ins-and-outs of Crystal with an objective mindset and give an unbiased opinion on its features; from it's low-level primitives to its familiar syntax, and much inbetween.\n+\n+I first came across Crystal when I saw [@sferik](https://twitter.com/sferik)  giving a talk on it in Poland back in 2015.  [Video here.](https://www.youtube.com/watch?v=Ysm4IU4aWoQ)  It was a great talk, and sparked my interest to Crystal right there and then.  When I initially checked out Crystal, I thought it looked awesome but I was too busy with all the other languages I was using on a daily basis to be able to focus my time properly on it.\n+\n+Alongside being too busy, I couldn't really see why I'd use Crystal instead of using C/Erlang/Go/Ruby - languages that I already knew.  What I can say with confidence now though; is that whilst those languages may all be able to achieve the same end-goal they're all *better* in their own, different ways entirely.\n+\n+When I want to build distributed apps, like my *Fist/Bump Heartbeat Monitor* - I use Erlang/Elixir.  When I want to build an API backend I use Golang.  When I want to spend the day with my brains scattered, and most probably in tears - I use C.  For readibility and demonstrations, I use Ruby.  When it comes to writing low-level systems such as daemons and obtuse Kernels, while it would be most performant to turn to C - it'd also take me a **LONG** time to achieve relatively little and the aforementioned tears would be very-likely flowing.  *This is where Crystal comes in.*\n+\n+Having a syntax *very* similar to Ruby means that the familiarity of Crystal is incredibly enticing.  Since the world is obsessed with web apps now, let's take a look at the code required to build a minimal web server in both Crystal and Ruby.\n+\n+In **Ruby**, using *Sinatra*, the code is as follows:\n+\n+~~~ ruby\n+require \"sinatra\"\n+ \n+set :logging, false\n+ \n+get \"/\" do\n+  content_type \"text/plain\"\n+  \"Hello, Auth0!\"\n+end\n+~~~\n+\n+Now in **Crystal**, check out the equivalent code:\n+\n+~~~ ruby\n+require \"kemal\"\n+ \n+logging false\n+ \n+get \"/\" do |ctx|\n+  ctx.response.content_type = \"text/plain\"\n+  \"Hello, Auth0!\"\n+end\n+ \n+Kemal.run\n+~~~\n+\n+The fact that you can use Ruby syntax highlighting natively for Crystal says everything!  Coming from Ruby to Crystal is a remarkably easy adaptation.  The fact is; one copy and paste code from Ruby to Crystal and 90% of the time it will run with no errors.  The creators of Crystal understand that Ruby is undoubtedly the most visually appealing language, and therefore built Crystal to take as much influence as possible from a design perspective.  You can even run Crystal programs using the `Ruby` shell command and vice versa since the syntax is valid for both languages!\n+\n+\n+## Binding C\n+\n+One of the big selling points for Crystal is the ease with which you can interface with C libraries.  \"Crystal allows you to bind to existing C libraries without writing a single line in C.  Additionally, it provides some conveniences like `out` and `to_unsafe` so writing bindings is as painless as possible.\"\n+\n+Let's build a simple script in C that says \"hi!\".  We'll then write a Crystal app to bind to our C library.  This is a great start point for anyone who wants to know about binding C in Crystal.\n+\n+First off, let's create a project with Crystal's scaffolding tool (I'll cover this feature later).  Run:\n+\n+\t$ crystal init app sayhi_c\n+\n+Then head into the directory `sayhi_c/src/sayhi_c` and let's create a file `sayhi.c` with the following contents:\n+\n+~~~ c\n+#include <stdio.h>\n+\n+void hi(const char * name){\n+  printf(\"Hi %s!\\n\", name);\n+}\n+~~~\n+\n+Now we need to compile our C file into an object.  On Ubuntu or Mac using `gcc` we can run:\n+\n+\t$ gcc -c sayhi.c -o sayhi.o\n+\n+Using the `-o` flags allow us to create an Object filetype.  Once we've got our Object file, we can bind it from within our Crystal app.  Open up our `sayhi_c.cr` file, and have it reflect the following:\n+\n+~~~ ruby\n+require \"./sayhi_c/*\"\n+\n+@[Link(ldflags: \"#{__DIR__}/sayhi_c/sayhi.o\")]\n+\n+lib Say\n+  fun hi(name : LibC::Char*) : Void\n+end\n+\n+\n+Say.hi(\"Auth0\")\n+~~~ \n+\n+I'll mention now that there are no implicit type conversions except `to_unsafe` - [explained here](https://crystal-lang.org/docs/syntax_and_semantics/c_bindings/to_unsafe.html) when invoking a C function: you must pass the exact type that is expected.\n+\n+Also worth noting at this point is that since we have built our C file into an object file, we can include it in the project directory and link from there.  When we want to link dynamic libraries or installed C packages, we can just link them without including a path.\n+\n+So, if we build our project file and run it, we get the following:\n+\n+~~~ bash\n+$ crystal build --release src/sayhi_c.cr\n+\n+$ ./sayhi_c\n+\n+ > Hi Auth0!\n+~~~\n+\n+It's really easy to bind to C in Crystal, and is definitely one of the features that attracts me most to the language.  I'm really looking forward to writing a C binding for a useful library and being able to utilise this functionality in production!\n+\n+\n+## Concurrency Primitives\n+\n+One of my favourite parts of *Golang* is the `goroutine` threading system.  Working in the Database industry, I got a real passion for concurrency &amp; parallelism and when looking to a new language, one of the first things I explore are the concurrency primitives.  In Crystal, we can use the `Spawn` functionality in a very similar way to `Goroutines` in Golang, `core.async` in Clojure, or the lightweight threading in Elixir/Erlang.\n+\n+For a simple test, I wrote two quick scripts to test the Spawn functionality in Crystal alongside Ruby.  We all know that Ruby is **not** a great language for threading, so I'm interested to see how much better Crystal is in small experiments.  Let's take the following example in ***Ruby***:\n+\n+~~~ ruby\n+1000.times.map do\n+  Thread.new do\n+    puts \"Hello?\"\n+  end\n+end.each(&:join)\n+~~~\n+\n+Running this from terminal I got the following results:\n+\n+~~~ bash\n+$ time ruby spawntest.rb\n+\n+real\t0m0.288s\n+user\t0m0.132s\n+sys\t0m0.116s\n+~~~\n+\n+I ran this little script on one of my ancient laptops that runs only 2gb of RAM and a terrible, *terrible* processor.  Now, porting this script to ***Crystal***, we can write:\n+\n+~~~ ruby\n+channel = Channel(String).new\n+1000.times do\n+  spawn {\n+    channel.send \"Hello?\"\n+  }\n+  puts channel.receive\n+end\n+~~~\n+\n+Running this script with the `crystal` command, I got the following results:\n+\n+~~~ bash\n+$ time crystal spawntest.cr\n+\n+real\t0m1.129s\n+user\t0m0.952s\n+sys\t0m0.276s\n+~~~\n+\n+Hmmmm, very interesting indeed!  Well, seen as Crystal is a compiled language and meant to be used to build small binaries that are easily distributed, it'd be a good idea to compile this small script and use *that* data instead!  I compiled the script using the `--release` flag - this tells the Crystal compiler to optimise the bytecode.\n+\n+~~~ bash\n+$ crystal build --release spawntest.cr\n+\n+$ time ./spawntest\n+\n+real\t0m0.008s\n+user\t0m0.004s\n+sys\t\t0m0.000s\n+~~~\n+\n+As you can see, this result is markedly different.  Using the `--release` flag when building the Crystal executable cuts out a lot of bloating and optimises the executable to be as efficient as possible.  Obviously, the above test is a very naive use of the Spawn functionality, and unfortunately, I haven't had the opportunity to test in a load-heavy production environment. But soon I fully intend to, and I'll write another article benchmarking this in detail when I have a good usecase and get the chance to!\n+\n+\n+## Built-in Tooling in Crystal\n+\n+One of the things I like most about Crystal is the excellent built-in tooling available.  When I look at new languages, especially relatively immature languages; it's always very reassuring when the language has extensive built-in tooling available to help developers stay productive &amp; happy!  In Crystal, there are a bunch of tools that make hacking around in the language super fun, but also help us to stay on the right track with semantics etc.\n+\n+#### Testing\n+\n+If you're coming from a Ruby/Rails background, I think you'll be ***very*** happy with the built-in testing framework that ships with Crystal.  It's rather reminiscent of *RSpec*, and will be really easy to use for anyone coming from a similar background.  Even if you're not from a Ruby/Rails background, it's a great testing tool and is super effective.\n+\n+Using the `Greeter` demo app from the Crystal docs, we could write our Specs as follows:\n+\n+~~~ ruby\n+require \"spec\"\n+require \"../lib/greeter\" # demo greeter class\n+\n+describe Greeter do\n+  describe \"#shout\" do\n+    it \"returns upcased string\" do\n+      Greeter.new.shout('hello auth0').should eq \"HELLO AUTH0\"\n+    end\n+  end\n+\n+  describe \".hello\" do\n+    it \"returns a static Hello string\" do\n+      Greeter.hello.should eq \"Hello\"\n+    end\n+  end\n+end\n+~~~\n+\n+As you can see, this spec should look *very* familiar to any Rubyists, and is becoming the more preferred syntax for application testing across many languages - there being RSpec clone libraries across most languages now!\n+\n+#### Project Scaffold\n+\n+Much the same as Elixir having the `Mix` manager, and Erlang the `Rebar` manager, Crystal has it's own built-in project scaffolder &amp; package manager.  I'd recommend using this at all times to ensure sematics are followed.  We can use it with the following:\n+\n+~~~ bash\n+$ crystal init lib my_cool_lib\n+      create  my_cool_lib/.gitignore\n+      create  my_cool_lib/LICENSE\n+      create  my_cool_lib/README.md\n+      create  my_cool_lib/.travis.yml\n+      create  my_cool_lib/shard.yml\n+      create  my_cool_lib/src/my_cool_lib.cr\n+      create  my_cool_lib/src/my_cool_lib/version.cr\n+      create  my_cool_lib/spec/spec_helper.cr\n+      create  my_cool_lib/spec/my_cool_lib_spec.cr\n+Initialized empty Git repository in ~/my_cool_lib/.git/\n+~~~\n+\n+#### Sharding\n+\n+No - not creating Database Shards (luckily)!  Shards are Crystal's packages distributed in the same way as Ruby Gems, Elixir Libs or Golang packages.  Each application we create contains a file in the root directory named `shard.yml`.  This file contains project details and external dependencies.  The `shard.yml` file in my `sayhi_c` app above looks like this:\n+\n+~~~ yml\n+name: sayhi_c\n+version: 0.1.0\n+\n+authors:\n+  - Robin Percy <robin@percy.pw>\n+\n+targets:\n+  sayhi_c:\n+    main: src/sayhi_c.cr\n+\n+crystal: 0.22.0\n+\n+license: MIT\n+~~~\n+\n+The app I built has no dependencies to use, but if we want to include external packages we can do so by adding them at the bottom of the file:\n+\n+~~~ yml\n+dependencies:\n+  github:\n+    github: felipeelias/crystal-github\n+    version: ~> 0.1.0\n+~~~\n+\n+#### Documentation & Formatting\n+\n+Crystal has a great inbuilt tool for generating documentation and formatting files.  The documentation that is generated is excellent - built in html/css and almost instantly ready to deploy.","path":"_posts/2017-06-21-an-introduction-to-crystal-lang.markdown","position":260,"original_position":260,"commit_id":"a985c6428e84e67ad37d3b92d8ec3021c494429e","original_commit_id":"a985c6428e84e67ad37d3b92d8ec3021c494429e","user":{"login":"unicodeveloper","id":2946769,"avatar_url":"https://avatars1.githubusercontent.com/u/2946769?v=4","gravatar_id":"","url":"https://api.github.com/users/unicodeveloper","html_url":"https://github.com/unicodeveloper","followers_url":"https://api.github.com/users/unicodeveloper/followers","following_url":"https://api.github.com/users/unicodeveloper/following{/other_user}","gists_url":"https://api.github.com/users/unicodeveloper/gists{/gist_id}","starred_url":"https://api.github.com/users/unicodeveloper/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/unicodeveloper/subscriptions","organizations_url":"https://api.github.com/users/unicodeveloper/orgs","repos_url":"https://api.github.com/users/unicodeveloper/repos","events_url":"https://api.github.com/users/unicodeveloper/events{/privacy}","received_events_url":"https://api.github.com/users/unicodeveloper/received_events","type":"User","site_admin":false},"body":"inbuilt => built-in","created_at":"2017-07-19T22:52:53Z","updated_at":"2017-07-19T23:04:01Z","html_url":"https://github.com/auth0/blog/pull/884#discussion_r128388287","pull_request_url":"https://api.github.com/repos/auth0/blog/pulls/884","_links":{"self":{"href":"https://api.github.com/repos/auth0/blog/pulls/comments/128388287"},"html":{"href":"https://github.com/auth0/blog/pull/884#discussion_r128388287"},"pull_request":{"href":"https://api.github.com/repos/auth0/blog/pulls/884"}}},"pull_request":{"url":"https://api.github.com/repos/auth0/blog/pulls/884","id":127077009,"html_url":"https://github.com/auth0/blog/pull/884","diff_url":"https://github.com/auth0/blog/pull/884.diff","patch_url":"https://github.com/auth0/blog/pull/884.patch","issue_url":"https://api.github.com/repos/auth0/blog/issues/884","number":884,"state":"open","locked":false,"title":"An Introduction to the Crystal Language","user":{"login":"rbnpercy","id":29288325,"avatar_url":"https://avatars0.githubusercontent.com/u/29288325?v=4","gravatar_id":"","url":"https://api.github.com/users/rbnpercy","html_url":"https://github.com/rbnpercy","followers_url":"https://api.github.com/users/rbnpercy/followers","following_url":"https://api.github.com/users/rbnpercy/following{/other_user}","gists_url":"https://api.github.com/users/rbnpercy/gists{/gist_id}","starred_url":"https://api.github.com/users/rbnpercy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rbnpercy/subscriptions","organizations_url":"https://api.github.com/users/rbnpercy/orgs","repos_url":"https://api.github.com/users/rbnpercy/repos","events_url":"https://api.github.com/users/rbnpercy/events{/privacy}","received_events_url":"https://api.github.com/users/rbnpercy/received_events","type":"User","site_admin":false},"body":"The Highs & Lows of Crystal Lang - an introduction from a polyglot Dev;  Tech Article.","created_at":"2017-06-22T20:09:26Z","updated_at":"2017-07-19T23:04:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"89701e1a81c7e86281223f5178199d7500b85063","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/auth0/blog/pulls/884/commits","review_comments_url":"https://api.github.com/repos/auth0/blog/pulls/884/comments","review_comment_url":"https://api.github.com/repos/auth0/blog/pulls/comments{/number}","comments_url":"https://api.github.com/repos/auth0/blog/issues/884/comments","statuses_url":"https://api.github.com/repos/auth0/blog/statuses/a985c6428e84e67ad37d3b92d8ec3021c494429e","head":{"label":"rbnpercy:rbnpercy-crystal-lang-intro","ref":"rbnpercy-crystal-lang-intro","sha":"a985c6428e84e67ad37d3b92d8ec3021c494429e","user":{"login":"rbnpercy","id":29288325,"avatar_url":"https://avatars0.githubusercontent.com/u/29288325?v=4","gravatar_id":"","url":"https://api.github.com/users/rbnpercy","html_url":"https://github.com/rbnpercy","followers_url":"https://api.github.com/users/rbnpercy/followers","following_url":"https://api.github.com/users/rbnpercy/following{/other_user}","gists_url":"https://api.github.com/users/rbnpercy/gists{/gist_id}","starred_url":"https://api.github.com/users/rbnpercy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rbnpercy/subscriptions","organizations_url":"https://api.github.com/users/rbnpercy/orgs","repos_url":"https://api.github.com/users/rbnpercy/repos","events_url":"https://api.github.com/users/rbnpercy/events{/privacy}","received_events_url":"https://api.github.com/users/rbnpercy/received_events","type":"User","site_admin":false},"repo":{"id":94218448,"name":"blog","full_name":"rbnpercy/blog","owner":{"login":"rbnpercy","id":29288325,"avatar_url":"https://avatars0.githubusercontent.com/u/29288325?v=4","gravatar_id":"","url":"https://api.github.com/users/rbnpercy","html_url":"https://github.com/rbnpercy","followers_url":"https://api.github.com/users/rbnpercy/followers","following_url":"https://api.github.com/users/rbnpercy/following{/other_user}","gists_url":"https://api.github.com/users/rbnpercy/gists{/gist_id}","starred_url":"https://api.github.com/users/rbnpercy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rbnpercy/subscriptions","organizations_url":"https://api.github.com/users/rbnpercy/orgs","repos_url":"https://api.github.com/users/rbnpercy/repos","events_url":"https://api.github.com/users/rbnpercy/events{/privacy}","received_events_url":"https://api.github.com/users/rbnpercy/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/rbnpercy/blog","description":"auth0 blog","fork":true,"url":"https://api.github.com/repos/rbnpercy/blog","forks_url":"https://api.github.com/repos/rbnpercy/blog/forks","keys_url":"https://api.github.com/repos/rbnpercy/blog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rbnpercy/blog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rbnpercy/blog/teams","hooks_url":"https://api.github.com/repos/rbnpercy/blog/hooks","issue_events_url":"https://api.github.com/repos/rbnpercy/blog/issues/events{/number}","events_url":"https://api.github.com/repos/rbnpercy/blog/events","assignees_url":"https://api.github.com/repos/rbnpercy/blog/assignees{/user}","branches_url":"https://api.github.com/repos/rbnpercy/blog/branches{/branch}","tags_url":"https://api.github.com/repos/rbnpercy/blog/tags","blobs_url":"https://api.github.com/repos/rbnpercy/blog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rbnpercy/blog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rbnpercy/blog/git/refs{/sha}","trees_url":"https://api.github.com/repos/rbnpercy/blog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rbnpercy/blog/statuses/{sha}","languages_url":"https://api.github.com/repos/rbnpercy/blog/languages","stargazers_url":"https://api.github.com/repos/rbnpercy/blog/stargazers","contributors_url":"https://api.github.com/repos/rbnpercy/blog/contributors","subscribers_url":"https://api.github.com/repos/rbnpercy/blog/subscribers","subscription_url":"https://api.github.com/repos/rbnpercy/blog/subscription","commits_url":"https://api.github.com/repos/rbnpercy/blog/commits{/sha}","git_commits_url":"https://api.github.com/repos/rbnpercy/blog/git/commits{/sha}","comments_url":"https://api.github.com/repos/rbnpercy/blog/comments{/number}","issue_comment_url":"https://api.github.com/repos/rbnpercy/blog/issues/comments{/number}","contents_url":"https://api.github.com/repos/rbnpercy/blog/contents/{+path}","compare_url":"https://api.github.com/repos/rbnpercy/blog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rbnpercy/blog/merges","archive_url":"https://api.github.com/repos/rbnpercy/blog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rbnpercy/blog/downloads","issues_url":"https://api.github.com/repos/rbnpercy/blog/issues{/number}","pulls_url":"https://api.github.com/repos/rbnpercy/blog/pulls{/number}","milestones_url":"https://api.github.com/repos/rbnpercy/blog/milestones{/number}","notifications_url":"https://api.github.com/repos/rbnpercy/blog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rbnpercy/blog/labels{/name}","releases_url":"https://api.github.com/repos/rbnpercy/blog/releases{/id}","deployments_url":"https://api.github.com/repos/rbnpercy/blog/deployments","created_at":"2017-06-13T13:53:27Z","updated_at":"2017-06-13T13:53:31Z","pushed_at":"2017-07-10T16:08:30Z","git_url":"git://github.com/rbnpercy/blog.git","ssh_url":"git@github.com:rbnpercy/blog.git","clone_url":"https://github.com/rbnpercy/blog.git","svn_url":"https://github.com/rbnpercy/blog","homepage":"https://auth0.com/blog","size":18890,"stargazers_count":0,"watchers_count":0,"language":"HTML","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"auth0:master","ref":"master","sha":"c21a3c236088317a3ec22e85a66dd59c8421469d","user":{"login":"auth0","id":2824157,"avatar_url":"https://avatars1.githubusercontent.com/u/2824157?v=4","gravatar_id":"","url":"https://api.github.com/users/auth0","html_url":"https://github.com/auth0","followers_url":"https://api.github.com/users/auth0/followers","following_url":"https://api.github.com/users/auth0/following{/other_user}","gists_url":"https://api.github.com/users/auth0/gists{/gist_id}","starred_url":"https://api.github.com/users/auth0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/auth0/subscriptions","organizations_url":"https://api.github.com/users/auth0/orgs","repos_url":"https://api.github.com/users/auth0/repos","events_url":"https://api.github.com/users/auth0/events{/privacy}","received_events_url":"https://api.github.com/users/auth0/received_events","type":"Organization","site_admin":false},"repo":{"id":7830124,"name":"blog","full_name":"auth0/blog","owner":{"login":"auth0","id":2824157,"avatar_url":"https://avatars1.githubusercontent.com/u/2824157?v=4","gravatar_id":"","url":"https://api.github.com/users/auth0","html_url":"https://github.com/auth0","followers_url":"https://api.github.com/users/auth0/followers","following_url":"https://api.github.com/users/auth0/following{/other_user}","gists_url":"https://api.github.com/users/auth0/gists{/gist_id}","starred_url":"https://api.github.com/users/auth0/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/auth0/subscriptions","organizations_url":"https://api.github.com/users/auth0/orgs","repos_url":"https://api.github.com/users/auth0/repos","events_url":"https://api.github.com/users/auth0/events{/privacy}","received_events_url":"https://api.github.com/users/auth0/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/auth0/blog","description":"auth0 blog","fork":false,"url":"https://api.github.com/repos/auth0/blog","forks_url":"https://api.github.com/repos/auth0/blog/forks","keys_url":"https://api.github.com/repos/auth0/blog/keys{/key_id}","collaborators_url":"https://api.github.com/repos/auth0/blog/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/auth0/blog/teams","hooks_url":"https://api.github.com/repos/auth0/blog/hooks","issue_events_url":"https://api.github.com/repos/auth0/blog/issues/events{/number}","events_url":"https://api.github.com/repos/auth0/blog/events","assignees_url":"https://api.github.com/repos/auth0/blog/assignees{/user}","branches_url":"https://api.github.com/repos/auth0/blog/branches{/branch}","tags_url":"https://api.github.com/repos/auth0/blog/tags","blobs_url":"https://api.github.com/repos/auth0/blog/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/auth0/blog/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/auth0/blog/git/refs{/sha}","trees_url":"https://api.github.com/repos/auth0/blog/git/trees{/sha}","statuses_url":"https://api.github.com/repos/auth0/blog/statuses/{sha}","languages_url":"https://api.github.com/repos/auth0/blog/languages","stargazers_url":"https://api.github.com/repos/auth0/blog/stargazers","contributors_url":"https://api.github.com/repos/auth0/blog/contributors","subscribers_url":"https://api.github.com/repos/auth0/blog/subscribers","subscription_url":"https://api.github.com/repos/auth0/blog/subscription","commits_url":"https://api.github.com/repos/auth0/blog/commits{/sha}","git_commits_url":"https://api.github.com/repos/auth0/blog/git/commits{/sha}","comments_url":"https://api.github.com/repos/auth0/blog/comments{/number}","issue_comment_url":"https://api.github.com/repos/auth0/blog/issues/comments{/number}","contents_url":"https://api.github.com/repos/auth0/blog/contents/{+path}","compare_url":"https://api.github.com/repos/auth0/blog/compare/{base}...{head}","merges_url":"https://api.github.com/repos/auth0/blog/merges","archive_url":"https://api.github.com/repos/auth0/blog/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/auth0/blog/downloads","issues_url":"https://api.github.com/repos/auth0/blog/issues{/number}","pulls_url":"https://api.github.com/repos/auth0/blog/pulls{/number}","milestones_url":"https://api.github.com/repos/auth0/blog/milestones{/number}","notifications_url":"https://api.github.com/repos/auth0/blog/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/auth0/blog/labels{/name}","releases_url":"https://api.github.com/repos/auth0/blog/releases{/id}","deployments_url":"https://api.github.com/repos/auth0/blog/deployments","created_at":"2013-01-25T22:57:37Z","updated_at":"2017-07-18T19:25:09Z","pushed_at":"2017-07-19T22:14:36Z","git_url":"git://github.com/auth0/blog.git","ssh_url":"git@github.com:auth0/blog.git","clone_url":"https://github.com/auth0/blog.git","svn_url":"https://github.com/auth0/blog","homepage":"https://auth0.com/blog","size":19596,"stargazers_count":39,"watchers_count":39,"language":"HTML","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":70,"mirror_url":null,"open_issues_count":47,"forks":70,"open_issues":47,"watchers":39,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/auth0/blog/pulls/884"},"html":{"href":"https://github.com/auth0/blog/pull/884"},"issue":{"href":"https://api.github.com/repos/auth0/blog/issues/884"},"comments":{"href":"https://api.github.com/repos/auth0/blog/issues/884/comments"},"review_comments":{"href":"https://api.github.com/repos/auth0/blog/pulls/884/comments"},"review_comment":{"href":"https://api.github.com/repos/auth0/blog/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/auth0/blog/pulls/884/commits"},"statuses":{"href":"https://api.github.com/repos/auth0/blog/statuses/a985c6428e84e67ad37d3b92d8ec3021c494429e"}}}}
{"actor":{"display_login":"unicodeveloper"}}