SlideShare ist ein Scribd-Unternehmen logo
1 von 27
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

Weitere ähnliche Inhalte

Was ist angesagt?

How to develop Jenkins plugin using to ruby and Jenkins.rb
How to develop Jenkins plugin using to ruby and Jenkins.rbHow to develop Jenkins plugin using to ruby and Jenkins.rb
How to develop Jenkins plugin using to ruby and Jenkins.rb
Hiroshi SHIBATA
 
Asynchronous I/O in NodeJS - new standard or challenges?
Asynchronous I/O in NodeJS - new standard or challenges?Asynchronous I/O in NodeJS - new standard or challenges?
Asynchronous I/O in NodeJS - new standard or challenges?
Dinh Pham
 

Was ist angesagt? (20)

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

Ähnlich wie Writing a Gem with native extensions

Ähnlich wie Writing a Gem with native extensions (20)

Ruby C extensions at the Ruby drink-up of Sophia, April 2012
Ruby C extensions at the Ruby drink-up of Sophia, April 2012Ruby C extensions at the Ruby drink-up of Sophia, April 2012
Ruby C extensions at the Ruby drink-up of Sophia, April 2012
 
introduction-infra-as-a-code using terraform
introduction-infra-as-a-code using terraformintroduction-infra-as-a-code using terraform
introduction-infra-as-a-code using terraform
 
Mac ruby deployment
Mac ruby deploymentMac ruby deployment
Mac ruby deployment
 
Infrastructureascode slideshare-160331143725
Infrastructureascode slideshare-160331143725Infrastructureascode slideshare-160331143725
Infrastructureascode slideshare-160331143725
 
Infrastructureascode slideshare-160331143725
Infrastructureascode slideshare-160331143725Infrastructureascode slideshare-160331143725
Infrastructureascode slideshare-160331143725
 
Infrastructure as code: running microservices on AWS using Docker, Terraform,...
Infrastructure as code: running microservices on AWS using Docker, Terraform,...Infrastructure as code: running microservices on AWS using Docker, Terraform,...
Infrastructure as code: running microservices on AWS using Docker, Terraform,...
 
DevOps Workflow: A Tutorial on Linux Containers
DevOps Workflow: A Tutorial on Linux ContainersDevOps Workflow: A Tutorial on Linux Containers
DevOps Workflow: A Tutorial on Linux Containers
 
K8s in 3h - Kubernetes Fundamentals Training
K8s in 3h - Kubernetes Fundamentals TrainingK8s in 3h - Kubernetes Fundamentals Training
K8s in 3h - Kubernetes Fundamentals Training
 
A New Chapter of Data Processing with CDK
A New Chapter of Data Processing with CDKA New Chapter of Data Processing with CDK
A New Chapter of Data Processing with CDK
 
Kubered -Recipes for C2 Operations on Kubernetes
Kubered -Recipes for C2 Operations on KubernetesKubered -Recipes for C2 Operations on Kubernetes
Kubered -Recipes for C2 Operations on Kubernetes
 
Ruby Meets Cocoa
Ruby Meets CocoaRuby Meets Cocoa
Ruby Meets Cocoa
 
The Hitchhiker's Guide to Faster Builds. Viktor Kirilov. CoreHard Spring 2019
The Hitchhiker's Guide to Faster Builds. Viktor Kirilov. CoreHard Spring 2019The Hitchhiker's Guide to Faster Builds. Viktor Kirilov. CoreHard Spring 2019
The Hitchhiker's Guide to Faster Builds. Viktor Kirilov. CoreHard Spring 2019
 
Devry gsp 215 week 7 i lab networking and a tiny web server new
Devry gsp 215 week 7 i lab networking and a tiny web server newDevry gsp 215 week 7 i lab networking and a tiny web server new
Devry gsp 215 week 7 i lab networking and a tiny web server new
 
Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014Dependencies Managers in C/C++. Using stdcpp 2014
Dependencies Managers in C/C++. Using stdcpp 2014
 
Build optimization mechanisms in GitLab and Docker
Build optimization mechanisms in GitLab and DockerBuild optimization mechanisms in GitLab and Docker
Build optimization mechanisms in GitLab and Docker
 
Toolbox of a Ruby Team
Toolbox of a Ruby TeamToolbox of a Ruby Team
Toolbox of a Ruby Team
 
stackconf 2022: Cluster Management: Heterogeneous, Lightweight, Safe. Pick Three
stackconf 2022: Cluster Management: Heterogeneous, Lightweight, Safe. Pick Threestackconf 2022: Cluster Management: Heterogeneous, Lightweight, Safe. Pick Three
stackconf 2022: Cluster Management: Heterogeneous, Lightweight, Safe. Pick Three
 
5 Things I Wish I Knew About Gitlab CI
5 Things I Wish I Knew About Gitlab CI5 Things I Wish I Knew About Gitlab CI
5 Things I Wish I Knew About Gitlab CI
 
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ BuilderA Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
A Check of the Open-Source Project WinSCP Developed in Embarcadero C++ Builder
 
ABCs of docker
ABCs of dockerABCs of docker
ABCs of docker
 

Kürzlich hochgeladen

Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider  Progress from Awareness to Implementation.pptxTales from a Passkey Provider  Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
FIDO Alliance
 

Kürzlich hochgeladen (20)

State of the Smart Building Startup Landscape 2024!
State of the Smart Building Startup Landscape 2024!State of the Smart Building Startup Landscape 2024!
State of the Smart Building Startup Landscape 2024!
 
Simplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdf
Simplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdfSimplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdf
Simplified FDO Manufacturing Flow with TPMs _ Liam at Infineon.pdf
 
Top 10 CodeIgniter Development Companies
Top 10 CodeIgniter Development CompaniesTop 10 CodeIgniter Development Companies
Top 10 CodeIgniter Development Companies
 
The Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and InsightThe Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and Insight
 
Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider  Progress from Awareness to Implementation.pptxTales from a Passkey Provider  Progress from Awareness to Implementation.pptx
Tales from a Passkey Provider Progress from Awareness to Implementation.pptx
 
Vector Search @ sw2con for slideshare.pptx
Vector Search @ sw2con for slideshare.pptxVector Search @ sw2con for slideshare.pptx
Vector Search @ sw2con for slideshare.pptx
 
The Metaverse: Are We There Yet?
The  Metaverse:    Are   We  There  Yet?The  Metaverse:    Are   We  There  Yet?
The Metaverse: Are We There Yet?
 
Long journey of Ruby Standard library at RubyKaigi 2024
Long journey of Ruby Standard library at RubyKaigi 2024Long journey of Ruby Standard library at RubyKaigi 2024
Long journey of Ruby Standard library at RubyKaigi 2024
 
Continuing Bonds Through AI: A Hermeneutic Reflection on Thanabots
Continuing Bonds Through AI: A Hermeneutic Reflection on ThanabotsContinuing Bonds Through AI: A Hermeneutic Reflection on Thanabots
Continuing Bonds Through AI: A Hermeneutic Reflection on Thanabots
 
Observability Concepts EVERY Developer Should Know (DevOpsDays Seattle)
Observability Concepts EVERY Developer Should Know (DevOpsDays Seattle)Observability Concepts EVERY Developer Should Know (DevOpsDays Seattle)
Observability Concepts EVERY Developer Should Know (DevOpsDays Seattle)
 
Generative AI Use Cases and Applications.pdf
Generative AI Use Cases and Applications.pdfGenerative AI Use Cases and Applications.pdf
Generative AI Use Cases and Applications.pdf
 
WebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM PerformanceWebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM Performance
 
Design Guidelines for Passkeys 2024.pptx
Design Guidelines for Passkeys 2024.pptxDesign Guidelines for Passkeys 2024.pptx
Design Guidelines for Passkeys 2024.pptx
 
ERP Contender Series: Acumatica vs. Sage Intacct
ERP Contender Series: Acumatica vs. Sage IntacctERP Contender Series: Acumatica vs. Sage Intacct
ERP Contender Series: Acumatica vs. Sage Intacct
 
The Value of Certifying Products for FDO _ Paul at FIDO Alliance.pdf
The Value of Certifying Products for FDO _ Paul at FIDO Alliance.pdfThe Value of Certifying Products for FDO _ Paul at FIDO Alliance.pdf
The Value of Certifying Products for FDO _ Paul at FIDO Alliance.pdf
 
Working together SRE & Platform Engineering
Working together SRE & Platform EngineeringWorking together SRE & Platform Engineering
Working together SRE & Platform Engineering
 
2024 May Patch Tuesday
2024 May Patch Tuesday2024 May Patch Tuesday
2024 May Patch Tuesday
 
Collecting & Temporal Analysis of Behavioral Web Data - Tales From The Inside
Collecting & Temporal Analysis of Behavioral Web Data - Tales From The InsideCollecting & Temporal Analysis of Behavioral Web Data - Tales From The Inside
Collecting & Temporal Analysis of Behavioral Web Data - Tales From The Inside
 
Event-Driven Architecture Masterclass: Integrating Distributed Data Stores Ac...
Event-Driven Architecture Masterclass: Integrating Distributed Data Stores Ac...Event-Driven Architecture Masterclass: Integrating Distributed Data Stores Ac...
Event-Driven Architecture Masterclass: Integrating Distributed Data Stores Ac...
 
TopCryptoSupers 12thReport OrionX May2024
TopCryptoSupers 12thReport OrionX May2024TopCryptoSupers 12thReport OrionX May2024
TopCryptoSupers 12thReport OrionX May2024
 

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)