How to create a native gem for ruby using C
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 andextconf.rbfor 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
guardandguard-shellas 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!