Skip to content

Instantly share code, notes, and snippets.

@simi

simi/blog.md Secret

Created February 3, 2019 16:25
Show Gist options
  • Save simi/e5af930fbe5553f450ff6947c6e8313a to your computer and use it in GitHub Desktop.
Save simi/e5af930fbe5553f450ff6947c6e8313a to your computer and use it in GitHub Desktop.

Write complete library binding for Ruby from scratch (linux, macosx version)

Building native extensions. This could take a while...

Almost every Ruby programmer have seen this message during gem install or similar message Installing xyz 1.2.3 with native extensions during bundle install. For some programmers this message can increase heart-rate or induce a feeling of panic usually followed by praying to our god (Matz). Thousands of hours were spent on googling following error messages, looking up for missing platform-specific depenedencies, installing dev packages, looking up for right combination of install parameters or asking television oracle on the phone to finally find the solution and get one of those stafisfying messages -> Successfully installed xyz-1.2.3 or Bundle complete!.

Those problems are usually first introduction to native extensions and since it is not nice and friendly introduction users often tend to not be comfortable working with native extensions. In my starting serie of arcticles I would like to show ecosystem around Ruby native extensions giving warm introduction to anyone interested in how this part of Ruby ecosystem works. Currently 11 gems from top 100 downloaded gems are using Ruby native extension. Understanding native extensions under the hood will make it easier for you to solve unexpected problems and also it will open you new possibilities. For example with Ruby native extension you can use any library from huge amount of great C based libraries even there's no Ruby binding written already!

Let's start the hard way - leaving comfort Ruby world for a while

Have you ever heard about UUIDs? Do you know there are 5 versions of UUID? In this tutorial I'll show how to write a gem generating UUIDv4 as a string. UUID is described in RFC 4122, let's start with reading it...

Wait!

Isnt' there any library already doing this? Hmm. Libraries usually starts with 'lib', let's try to find out if there's not any libuuid already build for us. Quick Google search takes us to https://linux.die.net/man/3/libuuid. Following uuid_generate link. :thinking-face: This C library implements uuid_generate_random function which is probably what are we are looking for - UUIDv4 (random) generator.

Let's take this opportunity to leave a nice safe Ruby world for a while and start a new interesting experience in C world. Since we're programmers, we are lazy by definition. To make our lives a little easier we can google for libuuid example. Going thru that code we should be able to extract basic usage of uuig_generate_random function having almost zero C knowledge (as I have).

TODO: C reference links

https://gist.github.com/b0d91f551bfdfdbf41aeb16bb8617bd7

Let's compile it via gcc uuidgen.c -o uuidgen -luuid. The -luuid is short version of -l uuid telling compiler to link our compiled file to libuuid library.

If gcc is not recognized command on your system, you need to install it first. On Linux it will be packaged in your distribution repo. Just try apt-get install gcc on Ubuntu or dnf install gcc on Fedora. On Mac you can use Homebrew brew install gcc. On Windows gcc can be installed with Ruby itself as a part of devkit.

If you'll get error similar to /usr/bin/ld: cannot find -luuid you need to install libuuid library and dev headers to your system. On Linux it should be easy as installing dev package (for Ubuntu apt-get install uuid-dev, for Fedora dnf install libuuid-devel). On Mac you can use Homebrew brew install ossp-uuid. If you wonder what to do on Windows, it is probably possible to build libuuid as well in there. I would skip Windows compatibility here for now, but we will get to it later in following article. Don't worry!

Running ./uuid-gen should print random UUID to standard output of your console now.

https://gist.github.com/ba00d31fda6a5e46f672e44badaea22f

Also we can check for returned value of your new program.

https://gist.github.com/300f25edbc61cf0d7f8d409ba5d8959c

Finally we have some result! Isn't it exciting to get out of Ruby comfortable world for a while and take a sneak-peek into dangerous C code world? We did a great job. Now we know there's libuuid C library doing exactly what we were looking for. We know how to use it in C world and we also know how to install it on our system. All that covered by testing C code generating our desired UUIDv4 and printing it into console. There's huge change this library is well performant since it is written in C. But we have no evidence for this now. No worries, we will take a look at perfomance benchmarking later. All we need to do now is to wrap this into Ruby world. Once that's done, we can go back to our comfort Ruby world where we have great tools we understand including great benchmark-ips gem to take a look how we do compared with other possible ways of generating UUIDv4 in Ruby.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment