I’ve been building a native gem, read this blog post to check out how this works nowadays.

It’s not that complicated albeit a bit unclear in the beginning. It all boils down to the following folder structure.

├── Gemfile
├── Guardfile
├── Rakefile
├── ext
│   └── my_native_gem
│       ├── extconf.rb
│       └── my_native_gem.c
├── lib
├── my_native_gem.gemspec
└── test
    └── test_my_native_gem.rb
  • The ./ext/ directory hosts your c files and extconf.rb for makefile generation.
  • The lib directory will be used for placing your compiled gem in
  • ./test/ for your tests

Create a .gemspec with native support

Begin by setting up your .gemspec. Here you specify the extensions in the s.extensions. It’s your regular gemspec except for the s.extensions.

Gem::Specification.new do |s|
  s.name    = "my_native_gem"
  s.version = "0.0.0"
  s.summary = "Does native stuff"
  s.author  = "Emile Bosch"
  s.extensions << "ext/my_native_gem/extconf.rb"  # <= Native extension
  s.add_development_dependency "rake-compiler"
end

Setup the source and make sure we can compile

Now its time to add the source files. Start by creating an ./ext/my_native_gem directory.

Create a ./ext/my_native_gem/extconf.rb to tell ruby how to generate a Makefile

require 'mkmf'
dir_config('my_native_gem')
create_makefile('my_native_gem')

Create a a ./ext/my_native_gem/my_native_gem.c this holds your native gem code.

#include "ruby.h"

static VALUE hello_world(VALUE mod)
{
  return rb_str_new2("hello world");
}

void Init_my_native_gem()
{
  VALUE mHello = rb_define_module("Hello");
  rb_define_singleton_method(mHello, "SayHello", hello_world, 0);
}

Create a Rakefile with an extension task, this introduces the rake command rake compile to compile a native gem.

require 'rake/extensiontask'
Rake::ExtensionTask.new('my_native_gem')

You should now be able to run rake compile on the command line.

Time to add tests

Start of by creating a ./test directory.

require 'minitest/autorun'
require 'my_native_gem'

class My_Native_Gem_Test < MiniTest::Unit::TestCase
  def test_ok
    puts Hello.SayHello
  end
end

Extend your Rakefile to support test task.

require 'rake/testtask'

Rake::TestTask.new do |t|
  t.libs << "test"
  t.test_files = FileList['test/test*.rb']
  t.verbose = true
end

You should now be able to run rake test on the command line.

Watch for changes

  • Add guard and guard-shell as development dependency

Now add a Guardfile to watch for changes. Whenever something changes in the test directory we run the tests. And if something changes in the ext directory we compile the c code.

guard :shell do
  watch /test/ do |m|
  system('rake test')
  end
  watch /ext/ do |m|
    system('rake compile')
  end
end

You should now be able to run Guard on the command line. On see it compile and run tests on changes.

Ship and be happy

Just ship it. OMG shipitsquirrel will be so happy!