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!
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.