SlideShare a Scribd company logo
Writing a Gem
with native extensions
Tristan Penman
Melbourne Ruby Meetup, August 2018
A whirlwind tour
 What are they?
 Native extensions allow you to extend the functionality
available in Ruby by writing code in natively compiled
languages such as C, C++, or even Rust.
 This talk shows how native extensions can be used to
interact with a third-party library written in C.
 The final code from this talk can be found on Github:
 https://github.com/tristanpenman/simple-clipboard
Native extensions
A brief introduction
Native extensions
Examples
• Byebug - a debugger for Ruby, that uses Ruby's TracePoint API
for execution control and the Debug Inspector API for call
stack navigation. Written as a C extension for speed.
• nokogiri - an HTML and XML parser. Uses native libraries for
speed and ensure standards compliance.
• RMagick - bindings for the ImageMagick image manipulation
library.
• sqlite3 - bindings for the SQLite3 database engine.
libclipboard
 libclipboard is a cross-platform clipboard library
 Simple C-based API:
 clipboard_new - create a context through which to access
the user's clipboard
 clipboard_free - free any memory allocated by
clipboard_new
 clipboard_text - read the contents of the clipboard as text
 clipboard_set_text - replace the contents of the clipboard
with new text
libclipboard
git clone https://github.com/jtanx/libclipboard
cd libclipboard
mkdir build
cd build
cmake ..
make -j4
sudo make install
 Must be built from source
 Requires git, cmake, and a C++ compiler tool-chain:
simple_clipboard
 We will use a native extension to wrap libclipboard with a
Module that we can use in Ruby code:
module SimpleClipboard
def get_text
#TODO: Return current contents ofclipboard,ornil
raise NotImplementedError
end
def set_text(new_text)
#TODO: Update clipboard; return previous contents, ornil
raise NotImplementedError
end
end
Extending Ruby using C
 To create our native extension we need two files:
 extconf.rb
 simple_clipboard.c
Extending Ruby using C
require 'mkmf'
$LOCAL_LIBS << '-lclipboard'
if RUBY_PLATFORM =~ /darwin/
$LDFLAGS <<' -framework AppKit'
end
create_header
create_makefile 'simple_clipboard/simple_clipboard'
extconf.rb
Extending Ruby using C
simple_clipboard.c (1/2)
#include <ruby.h>
#include <libclipboard.h>
VALUEget_text(VALUE _self) {
VALUE result = Qnil;
char *text = clipboard_text(cb);
if (text) {
result =rb_str_new(text, strlen(text));
free(text);
}
return result;
}
// continued on next slide...
Extending Ruby using C
simple_clipboard.c (2/2)
// continued from previous slide
VALUEset_text(VALUE _self, VALUE str) {
// omitted, since it is similar to get_text
}
voidInit_simple_clipboard() {
VALUE m =rb_define_module("SimpleClipboard");
rb_define_module_function(m, "get_text", get_text, 0);
rb_define_module_function(m, "set_text", set_text, 1);
}
 Run 'ruby extconf.rb' to generate:
 Header file (extconf.h, which is redundant in this case)
 Makefile
 (also mkmf.log)
 Run ’make’ to compile the extension
 On Mac OS X, creates a .bundle file
 On Linux, creates a .so
 On Windows, creates a .dll
Extending Ruby using C
2.3.3:001> require './simple_clipboard'
=>true
2.3.3:002> SimpleClipboard.get_text
=>"Extending Ruby using C"
2.3.3:003> SimpleClipboard.set_text "Hello world"
=>"Extending Ruby using C"
2.3.3:004> SimpleClipboard.get_text
=>"Hello world"
Extending Ruby using C
IRB session
Extending Ruby using C
In a nutshell
 For an extension named 'xyz' we need:
 An extconf.rb file and source file called 'xyz.c'
 In the 'xyz.c' file, a function called Init_xyz
 A native extension is free to:
 define modules, classes and methods that operate on
Ruby values, via an opaque C datatype 'VALUE'
 Call existing Ruby code
 Crash the current process
 And more generally, call 'undefined behavior'
How to include C code in a gem
 Okay, safety be damned, we want performance…
or legacy functionality…
or something.
 lib/
 simple_clipboard/
 version.rb
 simple_clipboard.rb
 simple_clipboard.gemspec
How to include C code in a gem
Layout without a native extension
How to include C code in a gem
Layout with a native extension
 ext/
 simple_clipboard/
 extconf.rb
 simple_clipboard.c
 lib/
 simple_clipboard/
 version.rb
 simple_clipboard.rb
 simple_clipboard.gemspec
#Boilerplate omitted
Gem::Specification.new do |s|
s.name ='simple_clipboard'
s.version =SimpleClipboard::VERSION
s.date ='2018-07-24'
s.summary ='Simple clipboardexample gem'
s.authors =['TristanPenman']
s.email ='tristan@tristanpenman.com'
s.licenses =['MIT']
#continued on next slide...
How to include C code in a gem
simple_clipboard.gemspec (1/2)
#continued from previous slide...
s.extensions =['ext/simple_clipboard/extconf.rb']
#Tell bundler where to findthe code forour gem
s.require_paths =['lib']
#Files toinclude in bundle
s.files =['ext/simple_clipboard/simple_clipboard.c',
'lib/simple_clipboard.rb',
'lib/simple_clipboard/version.rb']
end
How to include C code in a gem
simple_clipboard.gemspec (2/2)
How to include C code in a gem
 Run 'gem build simple_clipboard.gemspec':
 Does not actually compile native extension
 Creates 'simple_clipboard-0.0.1.gem'
 Run 'gem install simple_clipboard-0.0.1.gem'
 This is when bundler will build the native extension
 And this why, when things go wrong while building gems
such as nokigiri, that you can get very complex error
messages
$ gem install simple_clipboard-0.0.1.gem
How to include C code in a gem
Example
Building native extensions. This could take a while...
Successfully installed simple_clipboard-0.0.1
Parsing documentation forsimple_clipboard-0.0.1
Done installing documentation for simple_clipboard after 0 seconds
1 gem installed
2.3.3 :001 >require 'simple_clipboard'
=> true
2.3.3 :002 > SimpleClipboard.get_text
=> "Extending Ruby using C"
2.3.3 :003 > SimpleClipboard.set_text "Hello world"
=> "Extending Ruby using C"
2.3.3 :004 > SimpleClipboard.get_text
=> "Hello world"
Extending Ruby using C
IRB session
Testing native extensions
require "bundler/gem_tasks"
require "rspec/core/rake_task"
require 'rake/extensiontask'
desc "simple_clipboard unit tests"
RSpec::Core::RakeTask.new(:spec) do|t|
t.pattern ="spec/*_spec.rb"
t.verbose =true
End
#continued on next slide...
Rakefile (1/2)
Testing native extensions
#continued fromprevious slide
Rake::ExtensionTask.new do|ext|
ext.name ='simple_clipboard'
ext.source_pattern ="*.{c,h}"
ext.ext_dir ='ext/simple_clipboard'
ext.lib_dir ='lib/simple_clipboard'
ext.gem_spec =
Gem::Specification.load('simple_clipboard.gemspec')
end
#Default is tocompile native extension then runtests
task :default =>[:compile, :spec]
Rakefile (2/2)
Testing native extensions
1. Run 'rake' to compile and run tests
2. Run 'rake compile' to only compile native extension
 Compiles the native extension, then copies
simple_clipboard.[bundle|so|dll] file into 'lib/simple_clipboard'
3. Run 'rake spec' to only run tests
 Assumes that native extension (e.g. simple_clipboard.bundle) has
already been copied to 'lib/simple_clipboard' directory
Rake tasks
Resources
• Useful reference implementation of a
Gem with a native extension:
https://github.com/neilslater/ruby_nex_c
• Core documentation:
https://ruby-doc.org/core-
2.3.3/doc/extension_rdoc.html
(Beware the version number in this link)
• Pat Shaughnessy’s book:
Ruby Under a Microscope
Resources
• RubyGems documentation
https://guides.rubygems.org/gems-with-extensions/
• Aaron Bedra's Extending Ruby guide
http://aaronbedra.com/extending-ruby
• Chris Lalancette's in-depth series on writing Ruby extensions
in C, which covers numerous topics:
http://clalance.blogspot.com/2011/01/writing-ruby-
extensions-in-c-part-1.html
(12 parts in total)
Thanks for listening

More Related Content

PDF
Gradle in a Polyglot World
PDF
Building an Apache Sling Rendering Farm
PDF
MongoDB World 2018: Tutorial - Got Dibs? Building a Real-Time Bidding App wit...
ODP
Rapid JCR Applications Development with Sling
PDF
Middleware as Code with mruby
PDF
How to Begin to Develop Ruby Core
PDF
Large-scaled Deploy Over 100 Servers in 3 Minutes
PPTX
Extending Ruby using C++
Gradle in a Polyglot World
Building an Apache Sling Rendering Farm
MongoDB World 2018: Tutorial - Got Dibs? Building a Real-Time Bidding App wit...
Rapid JCR Applications Development with Sling
Middleware as Code with mruby
How to Begin to Develop Ruby Core
Large-scaled Deploy Over 100 Servers in 3 Minutes
Extending Ruby using C++

What's hot (20)

PDF
Day CRX Introduction
PDF
Middleware as Code with mruby
PDF
How DSL works on Ruby
PDF
Hijacking Ruby Syntax in Ruby
PDF
How to develop Jenkins plugin using to ruby and Jenkins.rb
PDF
Practical ngx_mruby
PDF
Experiments in Sharing Java VM Technology with CRuby
PDF
How to test code with mruby
ODP
Asynchronous I/O in NodeJS - new standard or challenges?
PDF
How to Begin Developing Ruby Core
PDF
Practical Testing of Ruby Core
KEY
Leave end-to-end testing to Capybara
PDF
The details of CI/CD environment for Ruby
PPS
Web Development in Perl
PDF
JRuby 9000 - Taipei Ruby User's Group 2015
PDF
mruby で mackerel のプラグインを作るはなし
PPT
Nodejs Event Driven Concurrency for Web Applications
PDF
Gate of Agile Web Development
PDF
RubyGems 3 & 4
PDF
Apache thrift-RPC service cross languages
Day CRX Introduction
Middleware as Code with mruby
How DSL works on Ruby
Hijacking Ruby Syntax in Ruby
How to develop Jenkins plugin using to ruby and Jenkins.rb
Practical ngx_mruby
Experiments in Sharing Java VM Technology with CRuby
How to test code with mruby
Asynchronous I/O in NodeJS - new standard or challenges?
How to Begin Developing Ruby Core
Practical Testing of Ruby Core
Leave end-to-end testing to Capybara
The details of CI/CD environment for Ruby
Web Development in Perl
JRuby 9000 - Taipei Ruby User's Group 2015
mruby で mackerel のプラグインを作るはなし
Nodejs Event Driven Concurrency for Web Applications
Gate of Agile Web Development
RubyGems 3 & 4
Apache thrift-RPC service cross languages
Ad

Similar to Writing a Gem with native extensions (20)

PDF
Ruby confhighlights
ODP
Ruby C extensions at the Ruby drink-up of Sophia, April 2012
PPTX
Introduction to Ruby Native Extensions and Foreign Function Interface
ODP
Build-a-Gem Workshop
ODP
Build-a-Gem Workshop
PDF
Writing a Ruby Gem for beginners
PDF
Gemification for Ruby 2.5/3.0
PDF
Gemification for Ruby 2.5/3.0
KEY
Week6
PDF
ruby-cocoa
PDF
ruby-cocoa
PPT
Ruby Projects and Libraries
PDF
Long journey of Ruby Standard library at RubyKaigi 2024
PPTX
RubyConfBD 2013 decouple, bundle and share with ruby gems
PDF
How to make a Ruby Gem - Austin on Rails, January 2014
PDF
HOW TO BUILD GEMS #shibuyarb
PDF
Gem That (2009)
KEY
Ruby v cpp_preso
KEY
Crate - ruby based standalone executables
KEY
Your fist RubyMotion Application
Ruby confhighlights
Ruby C extensions at the Ruby drink-up of Sophia, April 2012
Introduction to Ruby Native Extensions and Foreign Function Interface
Build-a-Gem Workshop
Build-a-Gem Workshop
Writing a Ruby Gem for beginners
Gemification for Ruby 2.5/3.0
Gemification for Ruby 2.5/3.0
Week6
ruby-cocoa
ruby-cocoa
Ruby Projects and Libraries
Long journey of Ruby Standard library at RubyKaigi 2024
RubyConfBD 2013 decouple, bundle and share with ruby gems
How to make a Ruby Gem - Austin on Rails, January 2014
HOW TO BUILD GEMS #shibuyarb
Gem That (2009)
Ruby v cpp_preso
Crate - ruby based standalone executables
Your fist RubyMotion Application
Ad

Recently uploaded (20)

PDF
KodekX | Application Modernization Development
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PPTX
sap open course for s4hana steps from ECC to s4
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Electronic commerce courselecture one. Pdf
PDF
cuic standard and advanced reporting.pdf
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
PPT
Teaching material agriculture food technology
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
Approach and Philosophy of On baking technology
PPTX
Spectroscopy.pptx food analysis technology
PDF
Encapsulation theory and applications.pdf
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
KodekX | Application Modernization Development
Per capita expenditure prediction using model stacking based on satellite ima...
“AI and Expert System Decision Support & Business Intelligence Systems”
sap open course for s4hana steps from ECC to s4
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
20250228 LYD VKU AI Blended-Learning.pptx
Diabetes mellitus diagnosis method based random forest with bat algorithm
Electronic commerce courselecture one. Pdf
cuic standard and advanced reporting.pdf
Understanding_Digital_Forensics_Presentation.pptx
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
Network Security Unit 5.pdf for BCA BBA.
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
Teaching material agriculture food technology
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Approach and Philosophy of On baking technology
Spectroscopy.pptx food analysis technology
Encapsulation theory and applications.pdf
Advanced methodologies resolving dimensionality complications for autism neur...

Writing a Gem with native extensions

  • 1. Writing a Gem with native extensions Tristan Penman Melbourne Ruby Meetup, August 2018 A whirlwind tour
  • 2.  What are they?  Native extensions allow you to extend the functionality available in Ruby by writing code in natively compiled languages such as C, C++, or even Rust.  This talk shows how native extensions can be used to interact with a third-party library written in C.  The final code from this talk can be found on Github:  https://github.com/tristanpenman/simple-clipboard Native extensions A brief introduction
  • 3. Native extensions Examples • Byebug - a debugger for Ruby, that uses Ruby's TracePoint API for execution control and the Debug Inspector API for call stack navigation. Written as a C extension for speed. • nokogiri - an HTML and XML parser. Uses native libraries for speed and ensure standards compliance. • RMagick - bindings for the ImageMagick image manipulation library. • sqlite3 - bindings for the SQLite3 database engine.
  • 4. libclipboard  libclipboard is a cross-platform clipboard library  Simple C-based API:  clipboard_new - create a context through which to access the user's clipboard  clipboard_free - free any memory allocated by clipboard_new  clipboard_text - read the contents of the clipboard as text  clipboard_set_text - replace the contents of the clipboard with new text
  • 5. libclipboard git clone https://github.com/jtanx/libclipboard cd libclipboard mkdir build cd build cmake .. make -j4 sudo make install  Must be built from source  Requires git, cmake, and a C++ compiler tool-chain:
  • 6. simple_clipboard  We will use a native extension to wrap libclipboard with a Module that we can use in Ruby code: module SimpleClipboard def get_text #TODO: Return current contents ofclipboard,ornil raise NotImplementedError end def set_text(new_text) #TODO: Update clipboard; return previous contents, ornil raise NotImplementedError end end
  • 7. Extending Ruby using C  To create our native extension we need two files:  extconf.rb  simple_clipboard.c
  • 8. Extending Ruby using C require 'mkmf' $LOCAL_LIBS << '-lclipboard' if RUBY_PLATFORM =~ /darwin/ $LDFLAGS <<' -framework AppKit' end create_header create_makefile 'simple_clipboard/simple_clipboard' extconf.rb
  • 9. Extending Ruby using C simple_clipboard.c (1/2) #include <ruby.h> #include <libclipboard.h> VALUEget_text(VALUE _self) { VALUE result = Qnil; char *text = clipboard_text(cb); if (text) { result =rb_str_new(text, strlen(text)); free(text); } return result; } // continued on next slide...
  • 10. Extending Ruby using C simple_clipboard.c (2/2) // continued from previous slide VALUEset_text(VALUE _self, VALUE str) { // omitted, since it is similar to get_text } voidInit_simple_clipboard() { VALUE m =rb_define_module("SimpleClipboard"); rb_define_module_function(m, "get_text", get_text, 0); rb_define_module_function(m, "set_text", set_text, 1); }
  • 11.  Run 'ruby extconf.rb' to generate:  Header file (extconf.h, which is redundant in this case)  Makefile  (also mkmf.log)  Run ’make’ to compile the extension  On Mac OS X, creates a .bundle file  On Linux, creates a .so  On Windows, creates a .dll Extending Ruby using C
  • 12. 2.3.3:001> require './simple_clipboard' =>true 2.3.3:002> SimpleClipboard.get_text =>"Extending Ruby using C" 2.3.3:003> SimpleClipboard.set_text "Hello world" =>"Extending Ruby using C" 2.3.3:004> SimpleClipboard.get_text =>"Hello world" Extending Ruby using C IRB session
  • 13. Extending Ruby using C In a nutshell  For an extension named 'xyz' we need:  An extconf.rb file and source file called 'xyz.c'  In the 'xyz.c' file, a function called Init_xyz  A native extension is free to:  define modules, classes and methods that operate on Ruby values, via an opaque C datatype 'VALUE'  Call existing Ruby code  Crash the current process  And more generally, call 'undefined behavior'
  • 14. How to include C code in a gem  Okay, safety be damned, we want performance… or legacy functionality… or something.
  • 15.  lib/  simple_clipboard/  version.rb  simple_clipboard.rb  simple_clipboard.gemspec How to include C code in a gem Layout without a native extension
  • 16. How to include C code in a gem Layout with a native extension  ext/  simple_clipboard/  extconf.rb  simple_clipboard.c  lib/  simple_clipboard/  version.rb  simple_clipboard.rb  simple_clipboard.gemspec
  • 17. #Boilerplate omitted Gem::Specification.new do |s| s.name ='simple_clipboard' s.version =SimpleClipboard::VERSION s.date ='2018-07-24' s.summary ='Simple clipboardexample gem' s.authors =['TristanPenman'] s.email ='tristan@tristanpenman.com' s.licenses =['MIT'] #continued on next slide... How to include C code in a gem simple_clipboard.gemspec (1/2)
  • 18. #continued from previous slide... s.extensions =['ext/simple_clipboard/extconf.rb'] #Tell bundler where to findthe code forour gem s.require_paths =['lib'] #Files toinclude in bundle s.files =['ext/simple_clipboard/simple_clipboard.c', 'lib/simple_clipboard.rb', 'lib/simple_clipboard/version.rb'] end How to include C code in a gem simple_clipboard.gemspec (2/2)
  • 19. How to include C code in a gem  Run 'gem build simple_clipboard.gemspec':  Does not actually compile native extension  Creates 'simple_clipboard-0.0.1.gem'  Run 'gem install simple_clipboard-0.0.1.gem'  This is when bundler will build the native extension  And this why, when things go wrong while building gems such as nokigiri, that you can get very complex error messages
  • 20. $ gem install simple_clipboard-0.0.1.gem How to include C code in a gem Example Building native extensions. This could take a while... Successfully installed simple_clipboard-0.0.1 Parsing documentation forsimple_clipboard-0.0.1 Done installing documentation for simple_clipboard after 0 seconds 1 gem installed
  • 21. 2.3.3 :001 >require 'simple_clipboard' => true 2.3.3 :002 > SimpleClipboard.get_text => "Extending Ruby using C" 2.3.3 :003 > SimpleClipboard.set_text "Hello world" => "Extending Ruby using C" 2.3.3 :004 > SimpleClipboard.get_text => "Hello world" Extending Ruby using C IRB session
  • 22. Testing native extensions require "bundler/gem_tasks" require "rspec/core/rake_task" require 'rake/extensiontask' desc "simple_clipboard unit tests" RSpec::Core::RakeTask.new(:spec) do|t| t.pattern ="spec/*_spec.rb" t.verbose =true End #continued on next slide... Rakefile (1/2)
  • 23. Testing native extensions #continued fromprevious slide Rake::ExtensionTask.new do|ext| ext.name ='simple_clipboard' ext.source_pattern ="*.{c,h}" ext.ext_dir ='ext/simple_clipboard' ext.lib_dir ='lib/simple_clipboard' ext.gem_spec = Gem::Specification.load('simple_clipboard.gemspec') end #Default is tocompile native extension then runtests task :default =>[:compile, :spec] Rakefile (2/2)
  • 24. Testing native extensions 1. Run 'rake' to compile and run tests 2. Run 'rake compile' to only compile native extension  Compiles the native extension, then copies simple_clipboard.[bundle|so|dll] file into 'lib/simple_clipboard' 3. Run 'rake spec' to only run tests  Assumes that native extension (e.g. simple_clipboard.bundle) has already been copied to 'lib/simple_clipboard' directory Rake tasks
  • 25. Resources • Useful reference implementation of a Gem with a native extension: https://github.com/neilslater/ruby_nex_c • Core documentation: https://ruby-doc.org/core- 2.3.3/doc/extension_rdoc.html (Beware the version number in this link) • Pat Shaughnessy’s book: Ruby Under a Microscope
  • 26. Resources • RubyGems documentation https://guides.rubygems.org/gems-with-extensions/ • Aaron Bedra's Extending Ruby guide http://aaronbedra.com/extending-ruby • Chris Lalancette's in-depth series on writing Ruby extensions in C, which covers numerous topics: http://clalance.blogspot.com/2011/01/writing-ruby- extensions-in-c-part-1.html (12 parts in total)