Ruby-red onions: Peeling Back Ruby's Layers in C Extensions
This is a modal window.
The media could not be loaded, either because the server or network failed or because the format is not supported.
Formal Metadata
Title |
| |
Title of Series | ||
Number of Parts | 65 | |
Author | ||
License | CC Attribution - ShareAlike 3.0 Unported: You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this | |
Identifiers | 10.5446/37636 (DOI) | |
Publisher | ||
Release Date | ||
Language | ||
Producer |
Content Metadata
Subject Area | ||
Genre | ||
Abstract |
|
00:00
Field extensionFaculty (division)MathematicsBit rateImplementationExpert systemProcess (computing)Multiplication signKummer-TheorieLibrary (computing)Field extensionFaculty (division)HTTP cookieCASE <Informatik>Database1 (number)AlgorithmSurgeryFormal languageInterpreter (computing)Device driverAverageArmThermal expansionCodeCommunications protocolAuthenticationSoftware developerKerberos <Kryptologie>Ruby on RailsInternet service providerJava appletCausalityComputer animation
02:37
AuthenticationImplementationContext awarenessLibrary (computing)AuthenticationPrice indexEnterprise architectureDevice driverQuicksortWorkstation <Musikinstrument>MereologyExterior algebraProcess (computing)Projective planeKummer-TheorieCurveCodeKerberos <Kryptologie>ImplementationDatabaseSuite (music)Standard deviationOrder (biology)Computer animation
04:02
Wrapper (data mining)Computer clusterKummer-TheorieMultiplication signCodeWritingLibrary (computing)Kerberos <Kryptologie>AuthenticationSoftware developerInformation technology consultingSoftware bugField extensionAssembly languageReading (process)Computer animation
05:16
Lemma (mathematics)Personal identification numberLibrary (computing)Multiplication signProcess (computing)ECosCode
05:42
Revision controlCodeHigh-level programming languageWater vaporKummer-TheorieField extensionContext awarenessQuicksortLevel (video gaming)Computer animation
06:31
Kummer-TheorieField extensionObject (grammar)Source codeKummer-TheorieCodeAbstractionCore dumpNumberImplementationLimit (category theory)XML
07:15
Kummer-TheorieSource codeCore dumpField extensionSoftware developerState of matterCodeXML
07:39
Type theoryFingerprintExt functorSource codeVolumeTwin primeImplementationBlogSoftware developerCodeCore dumpSource code1 (number)WritingKummer-TheorieFormal languageReading (process)Field extensionSystem callGoodness of fitComputer animation
08:27
AbstractionLevel (video gaming)Kummer-TheorieFormal languageCodeLine (geometry)Virtual machineQuicksortVariable (mathematics)Type theoryTerm (mathematics)Object (grammar)XML
09:03
Type theoryObject (grammar)Term (mathematics)Object (grammar)Logical constantIntegerType theoryData structureElectronic mailing listConstructor (object-oriented programming)Variable (mathematics)Core dumpFlagCodeSymbol tableSocial classSoftware testingComputer animationXMLLecture/Conference
10:22
Data conversionType theoryObject (grammar)Generic programmingLatent heatType theoryData structureQuicksortLogical constantCodeMacro (computer science)Computer animation
10:57
Data typeMacro (computer science)Exception handlingFunction (mathematics)Type theoryObject (grammar)Symbol tableLogical constantIntegerMacro (computer science)Data structureException handlingFunctional (mathematics)Electronic mailing listGeneric programmingCorrespondence (mathematics)Type theoryExpected valueComputer animation
11:33
Data structureString (computer science)Symbol tableType theorySocial classPointer (computer programming)StrutMacro (computer science)String (computer science)IntegerObject (grammar)Correspondence (mathematics)Macro (computer science)Electronic mailing listType theorySymbol tableRepresentation (politics)Functional (mathematics)Data typeComputer animation
11:58
Data structureString (computer science)Symbol tableType theoryStrutPointer (computer programming)Macro (computer science)Data typeRepresentation (politics)Correspondence (mathematics)Pointer (computer programming)CASE <Informatik>Data structureComputer animation
12:27
Data structureString (computer science)Symbol tableType theoryFunction (mathematics)StrutData structureObject (grammar)Subject indexingElectronic mailing listString (computer science)IdentifiabilityPoisson-KlammerFunctional (mathematics)Type theoryLengthImplementationElement (mathematics)Correspondence (mathematics)Equivalence relationData typeVariable (mathematics)Tape driveFlagComputer animation
14:07
Function (mathematics)Variable (mathematics)Type theoryData structureObject (grammar)NumberMacro (computer science)LengthFunctional (mathematics)Group actionKummer-TheorieLibrary (computing)CodeSlide ruleString (computer science)Correspondence (mathematics)XMLUMLComputer animation
15:10
Context awarenessKummer-TheorieSet (mathematics)AuthenticationGeneric programmingSpherical capField extensionSpeicherbereinigungCellular automatonInstance (computer science)Object (grammar)CodeParameter (computer programming)Point (geometry)Library (computing)Reading (process)Social classMultilaterationExecution unitPointer (computer programming)Functional (mathematics)Context awarenessTape driveLine (geometry)Variable (mathematics)Forcing (mathematics)Semiconductor memoryInterpreter (computing)Kerberos <Kryptologie>Type theoryLatent heatComputer animation
18:28
Inclusion mapExt functorIntegrated development environmentLie groupScripting languageLibrary (computing)Process (computing)Revision controlComputer filePhysical systemKummer-TheorieComputing platformMultiplication signSlide ruleIntegrated development environmentFunctional (mathematics)MathematicsEmailXML
19:05
Cloud computingInstallation artPhysical systemScripting languageKummer-TheorieLibrary (computing)Computer fileEmailTask (computing)Physical systemWhiteboardWritingDialectSoftware testingCompilation albumThermal expansionComputer animation
19:46
Task (computing)Software testingKummer-TheorieSoftware testingTask (computing)CompilerBit rateIntegrated development environmentQuicksortConnectivity (graph theory)Computing platformAuthorizationComputer fileComputer animation
20:26
Computer fileInclusion mapKummer-TheorieKummer-TheorieMetadataComputer fileComputing platformJava appletContext awarenessField extensionInformationProcess (computing)Hydraulic jumpComputer animation
20:57
Java appletComputing platformComputer fileField extensionKummer-TheorieComputer fileField extensionEmailLine (geometry)Computing platformRevision controlProcess (computing)Adventure gameThermal expansionCodeComa BerenicesInterior (topology)Directory serviceComputer animation
21:47
CodeCloud computingCodeDirectory serviceKummer-TheorieSocial classCompilation albumComputer fileParameter (computer programming)Ext functorLogical constantBit ratePointer (computer programming)Module (mathematics)Functional (mathematics)Data structureLine (geometry)WritingCompilerAuthorizationNumberThermal expansionTask (computing)Endliche ModelltheorieEmailHydraulic jumpField extensionComa BerenicesLie groupMereologyPoint (geometry)Computer animation
24:40
Installation artSystem programmingLine (geometry)Computer filePhysical systemBuildingProcess (computing)Directory serviceEmailKummer-TheorieDevice driverWebsiteError messageCompilation albumDialectComa Berenices
25:55
Cloud computingPhysical systemInstallation artKummer-TheorieLimit (category theory)Computer fileEmailAuthenticationLibrary (computing)DialectWhiteboardNP-hardComputer animation
26:23
Kummer-TheorieKummer-TheorieAuthenticationKerberos <Kryptologie>Library (computing)Device driverThermal expansionComputer animationXML
26:57
Kummer-TheorieComputer programmingThermal expansionCASE <Informatik>AuthenticationKerberos <Kryptologie>Computer animationLecture/Conference
27:25
Installation artAuthenticationException handlingIntegrated development environmentKerberos <Kryptologie>Latent heatSoftware testingKummer-TheorieFormal languageCASE <Informatik>Device driverCodeReading (process)BitBit rateLecture/Conference
28:11
Kummer-TheorieComputer fileFormal languageBitEmailKummer-TheorieField extensionImplementationError messagePhysical systemWhiteboardLimit (category theory)Real numberProcess (computing)Computer animation
28:58
Faculty (division)SoftwareEvent horizonVideoconferencing
Transcript: English(auto-generated)
00:31
My name is Emily Stolfo, I work for MongoDB. I'm the Ruby driver to the database. It's the Mongo and BSON gems, if you've ever used them. I'll soon be working on Mongoid as well,
00:41
which if you're a Rails developer using MongoDB, you're probably familiar with. I am an adjunct faculty at Columbia, where I teach Ruby on Rails. I've recently moved to Berlin, so I actually haven't been teaching this semester, but I probably will come back in the spring and teach them more. And I'm gonna talk about peeling back Ruby's layers
01:02
in C extensions. So, who here has written a C extension? Okay, who's an expert on writing a C extension? Cause I'm not, so. If you're an expert, this might not be the talk for you. Who wants to write a C extension? Who's just, are you curious about writing a C extension, is that why you're in this talk?
01:22
Okay, so I had the experience of writing a C extension over the last year to provide Kerberos authentication support for our driver, and I learned a lot from the experience, both about Ruby itself, about the C interpreter, MRI, and about Ruby gems, what to do, what not to do.
01:42
And I'm gonna talk about what you need to know if you want to write a C extension for Ruby, and if you want to package it with your gem. What mistakes you can avoid that I've made. I think Ruby itself as a language is really unique, because it has these different implementations,
02:00
so it has the primary ones are like a C implementation, the MRI, and a Java implementation, so JRuby. And I think it's really unique and interesting, partially because it means that you can write these extensions that work with external libraries. So in the case of Kerberos, which is a certain kind of authentication protocol, there are external libraries that you use
02:22
to do all of the fancy math algorithms, and they're not implemented in Ruby, they're implemented in Java or C in this case, and you have to write some kind of glue that can go back and forth between your Ruby code and these external libraries. So once upon a time, in January 2013,
02:41
a ticket was created for the Ruby team at MongoDB, there were three of us at the time, saying implement G-SAPB Kerberos authentication support. And it was originally assigned to one of my colleagues, but it was a hot potato, we just sort of passed it around between the three of us for about a year, and nobody did anything about it, because nobody wanted to deal with this ticket.
03:02
So Kerberos authentication is something that's not usually popular amongst Rubyists, it's this kind of authentication that is used in pretty large enterprises, and it's usually some kind of policy where they say every kind of authentication with the database has to be done using Kerberos. And if you ever find a Ruby project
03:22
needing to use Kerberos authentication, it's usually in the context of one of these big enterprises as part of a suite of technologies that need to meet this certain policy. So it wasn't really a super high priority or a demanded feature of the Ruby driver, it was something we had to do in order to comply
03:41
with standardization across all drivers. So hence the resistance or just the lack of interest in implementing, the return on investment in writing this C extension to provide this authentication was not very high. So we spent about a year trying to figure out how we could avoid writing this C extension.
04:01
We researched a bunch of alternatives, which were, there was a gem called gstapi, which was, it wasn't a C extension for working with the Kerberos library, it was using something called RubyFFI, which allows you to write Ruby code that goes directly into C code, it doesn't really need the glue or the C extension glue in between.
04:22
And essentially this gem was doing the Kerberos authentication in Ruby code and calling into a C external library directly using this glue called RubyFFI. I tried using that and I got a bunch of segmentation faults and I consulted with our C developers and after some time
04:43
and them helping me read assembly code, we realized that it wasn't really a good use of our time to debug this, it would have been time better spent if I just wrote a C extension myself. The other thing I investigated was RubyFFI, which again was this way of just going directly from Ruby code into a C library
05:01
and skipping the whole extension writing itself. And that didn't really work out either, I got also a segmentation fault and I figured it would be time better spent if I actually just sucked it up and wrote the extension. So I did finally write it, learned a lot in the process,
05:22
wrote it in over the course of about a month, like August into September, and by that time the PHP team had implemented it also in C, so I was able to talk with them and learn about how they actually wrote the C code to use the Sazzle library, the Sazzle C library. And I released it the day before
05:41
by Ruko Barcelona RubyConf on September 9th, and on September 10th I had to yank it. So I'll tell you why I had to yank it towards the end and how you can avoid having to yank your gems depending on C extensions. So let's talk about what it means
06:01
to write a C extension, why you shouldn't be afraid of writing a C extension, and what you can get out of writing a C extension. So thinking about Ruby as an onion in the context of MRI, Ruby is a very high level language, pretty elegant, very expressive, but underneath all of that expression or that simplicity that we like,
06:21
there's a lot of C code. So we're sort of underwater in C code and Ruby is up there above water and there are these beautiful floats floating above us. So let's think about it almost like an onion in the sense that we have this core understanding of how Ruby objects work and how C works, and then we provide increasing numbers of abstractions
06:44
on top of the C code, and eventually we get up to a gem that's released that has an extension that we've written sort of at the core of this Ruby red onion. So what Ruby knowledge do you gain in writing a C extension? I'm gonna talk about that. I'm gonna talk about how you actually write the extension,
07:00
how you work with Ruby objects, how you go back and forth between C data and Ruby data, how do you package it with your gem, and finally, what are the Ruby gem limitations? So what did I learn after having made a mistake with packaging it with my gem? So starting with knowledge gained, resources. What can you use to learn about this
07:22
besides looking at source code? There are a bunch of extensions out there. I know like Nokogiri is one really known gem that has a C extension. You can look at other implementation, other C extensions, but the core documentation, in my opinion, that you should look at is the readme.ext
07:42
in the Ruby source code. So it's pretty straightforward. It's not very rich. It's just text, but it's really clear. It has an appendix at the end, and it tells you exactly what you need to do as a Ruby developer who's writing C code working with the Ruby implementation in C.
08:01
And the other thing is there are a lot of blogs out there as well that talk about how to write C extensions, and there are a couple really good ones over the last three or four years. None of them are, I wouldn't call, I wouldn't highly recommend any of them. I would highly recommend looking at this readme,
08:22
which is short and sweet, but really gets to the core of what you need to know. One line in it that I find sort of charming is when you're writing a C extension, you're adding new features to Ruby. So again, Ruby is nothing but a way to write code that is then implemented in another language.
08:42
So when you're writing code in that other language, it allows you to do other things in the higher level code. You're actually sort of adding features to that language. I think that's the sort of cool way of thinking about writing a C extension. You're sort of in the belly of the machine and playing around with how it works and providing new abstractions to the thing above you.
09:03
So the actual knowledge you'll gain in terms of how to go back and forth between Ruby and C and CRuby is, when you're going from Ruby to C, keep in mind that Ruby objects have types, Ruby variables don't have types,
09:21
C data doesn't have a type, and C variables do have types. So how do you go back and forth between these constructs or these data structures? In C code, when you're working with Ruby objects, you'll be handed this structure and you'll have no way of really knowing what type it is or how it'll behave
09:42
or what behaviors it can actually have, what features this object has, unless you check the actual type on that object. And there's a certain way to do that. Each Ruby object, when you're working with it in C code, will have this identifier, this integer flag on it that tells you how to work with it and how to convert into C data.
10:03
So there are 18 of them that correspond to the Ruby core objects, and you'll recognize these as nil, object, class, string, array, false, symbol, and the list goes on. And again, these are integers, these are constants that tell you what Ruby objects, what the object that you're working with,
10:20
how it should behave and how you can use it. So when you interact with these data types, when you have this Ruby object and you wanna convert it to C code, you first check the type and you get the integer, so that constant, and then you convert value into C data. So I didn't explain what value is. Value is sort of this generic C structure that doesn't really have a specific type
10:42
or specific object class, but that could be any one of those Ruby objects. So when you want to convert into C data or C data into Ruby, the go-between is this value generic structure. So how do you actually check the type? There are two main ways.
11:00
There's a macro called type, where you provide it this value, so this generic structure, this Ruby object, and it'll give you back the integer that corresponds to one of the constants in that list that I showed you in this list. And then the other way that you can check is using a function that takes in that value, that generic data structure, and a integer that you want to check
11:21
and will throw an exception if it's not that type that you're expecting. So that's how you check the type. So once you know the type, you'll know what to do with the type or how to convert into C data. And Ruby objects that are defined like a string or a false symbol, these have corresponding C data types.
11:43
So once you have this Ruby object, you check the type, you know what integer it is, what constant in that list it is, you can then convert it into C data. And the way you do that is by using macros like rstring. Actually, so on this slide, like rstring and rarray,
12:00
these are just examples, like each one of these has its own corresponding C data type. And rstring and rarray are the C representation of those Ruby objects, C data types. And so when you use something like rstring or rarray, these functions, they'll take in this generic value, the data structure,
12:20
and give you back a pointer to the corresponding C data type. So in this case, rstring, rarray will give you back a pointer to an rstring or rarray type. And then you can work directly with those data structures themselves if you want, but it's highly recommended to use functions instead. So there are helper functions for you like Ruby string set length,
12:41
which will take in the value data, and then a length, and set the length on that string. You could go into the structure itself and change it yourself, but that's not recommended because there are implementation details of that data type, that C data type, that you will avoid clashing with
13:03
or messing around with if you use the helper function. And then something like Ruby array entry, this is the equivalent of using brackets with an array, so if you provide a offset, so an index in the array, it'll return to you the object in the array at that offset. And so this is another way of accessing an element in the list.
13:22
So I can imagine like in a C data rarray, you can take that structure and access the list within that structure and get it the element you want, but it's much better to use this function because it knows the implementation details of the C data type rarray and can protect you from anything
13:40
that you might mess up or any misunderstanding that you have. So that's how you go from Ruby to C. Ruby, you have these objects, they have identifiers of this flag, this constant, it tells you how to convert it into C data. C data have corresponding types for Ruby objects that you're familiar with, and we'll talk about what happens
14:00
when you have your own custom Ruby objects and how you go back and forth with C data. So now we're gonna talk about going from C to Ruby. So how do you go from these variables that have types, these generic structures that don't have types, and convert them into Ruby objects that do have types or variables that don't have types?
14:20
You can take a structure of C data and cast it to a value and then convert it into some kind of, use a function or a macro to convert it into the corresponding Ruby object. You can use functions to create Ruby strings based off of C data given a certain length.
14:43
There are a number of things you can do, but I think the most interesting is wrapping C data in your own custom Ruby object, and that's what I'm going to show you in the next couple of slides. The first two are pretty straightforward. You can look at documentation for that, but the third one is where you running a C extension is the most valuable because you can take C data that you've gotten
15:03
based on interacting with an external library and turn it into something that your Ruby code can actually use and make use of. Encapsuling C data into Ruby objects. These are three lines taken from the C extensions that I wrote that pretty much show you what you would do if you wanted to create
15:21
a custom Ruby object in your C code. I create a variable of type value, so again, this generic data, C data, and then I wrap it using a function called data wrap struct. And what this does is it has, I don't know if this is a laser, maybe not, has four arguments.
15:40
So the first one is the class of the object, and I just use a generic Ruby object class. The second argument, I have null here, but what that is is a function you provide that tells the garbage collector when it's executing how to mark that object, or mark the object that that one points to. So if I had in this C data, if I had it pointing to other C objects
16:02
that I wanted to deal with somehow when the garbage collector came around and needed to mark this one object, I would put it inside that function. The third argument is another really interesting one that I actually do have a function for, sasocanfree. What that does is it tells the garbage collector how to free the pointer.
16:21
So when I'm in this Kerberos authentication C extension, I work with an external C library, I set some variables, I allocate memory, and then I create this Ruby object that's wrapped up and available for me in my Ruby code. When I free up that object in my Ruby code, I need to free up some other C stuff that I've done
16:41
that the Ruby interpreter has no idea about, and you do that in that function. So sasocanfree will take some C variables, some C memory that I've allocated, and free it up because the Ruby interpreter doesn't know about it. And then the last one is the actual C data that I'm wrapping. And then the third line is another really interesting one,
17:02
and this is how you tell your Ruby code how it can actually access this data now that you've wrapped in a nice Ruby understandable way. This code is written within a function that is called,
17:22
that can be called from my own Ruby code, an instance of a class that I've created, and I've actually defined that class inside my C code, and I'll show you later how I do that. And what this does is it takes that, it works with the external Kerberos library, it creates this object, this data, it wraps it up in Ruby, and then it says, here, I'm assigning it to this instance variable
17:42
that's available on this class to my Ruby code that I will be writing at some later point, or be using at some later point. And so it's pretty easy to understand. Ruby instance variable set, it'll set it on self because this method is an instance method being called on a specific Ruby object,
18:00
and it's setting this wrapped C data to the variable context, and then I can access it from my Ruby code. So again, that's encapsulating C data into a Ruby object, and that's that third thing you could do when you're going between C and Ruby that I find the most interesting, and it's most customizable, and this is where you can have the most fun in your C extension. Okay, so now we understand how to go from Ruby to C,
18:22
and from C to Ruby. How do we actually write this extension and include it in the context of our gem? So include an extension with your gem. There are four things you need to do, and I don't find the documentation for this to be that straightforward online, so maybe you could even use these slides
18:41
as a reference in the future if you need to do this. So the first one is you need to write a file called extconf.rb, which essentially is a script that runs, that checks the environment or your platform in the system, checks to see if the external library that you're dependent on is there if you are dependent on an external library, and it creates a makefile that can be used
19:02
to build and install the extension. And what it looks like is like this. Ours is actually pretty simple. It requires mkmf, which allows you to use these functions, which finds header. It looks for this header file on your system. It says you have this library.
19:20
If so, create a makefile, and then, either we'll look at a rake task later that will actually install the extension, or RubyGems will do it, or if it doesn't find the library, it'll abort. So it won't even get to this step of installing the extension, compiling and installing.
19:42
So that's a script that you have to write that's essential for having a C extension. The second thing, which isn't completely essential, but if you're ever going to test your extension, or do anything else with it, like either testing building the extension,
20:00
or actually just using it in your own Ruby test, you're gonna have to write a rate compile test. You'll probably use rate compiler, that's sort of the go-to gem for this. And you'll write a task that will build the extension, and install it for you so that you can actually use it in a testing environment. That's not that interesting to look at.
20:20
You can find really good documentation on rate compiler online, but you'll have to probably write this task yourself. Then the third thing is the gemspec. So gemspec is the file that you have inside your gem that has all the metadata about your gem. It says the authors, it says what to do based on different platforms, has all this information, and in the context of having an extension in your gem,
20:46
you're gonna have to tell, you're gonna have to specify in your gemspec that you have a C extension or a Java extension, so you have to check the platform in there and do something, depending on which one you want Ruby gems to look for. So the gemspec will look like this.
21:00
This is a simplified version of our gemspec. It says if the Ruby platform is Java, it's going to include the jar, so Java extensions function differently from C extensions. I'm not gonna explain it, but you already have the binary there, and you add it into your files that will be used when Ruby gems goes to install the gem.
21:22
But when you're doing C, you wanna add the header files, the C files, any, the Ruby file will be the exe.comp file that it'll be looking for in that directory, and then you tell it to run the exe.comp file when it wants to install the extension. As you just basically need to have those two lines inside your gemspec,
21:40
and then Ruby gems will know this gem has an extension, I need to run this file, create the makefile, et cetera, et cetera. And then finally, obviously, the C code. You need the C code in your gem directory. There are some conventions on where you're supposed to put that, and you can find that in documentation online also. But in looking at an example of some C code,
22:02
this is taken, again, from the C extension, and this is probably the most interesting or the most valuable part, thing that you need to know about running a C extension. When you want to use, so I wrote a class called C sazzle that I instantiate and use within my Ruby code
22:21
inside the gem, and when you write require sazzle, this is the code that actually runs. So this defines the class and defines the methods on that class and other behaviors. So in going line by line, the first line creates a structure that will hold the definition of this class.
22:43
So C gsapi auth. And then within this init sazzle, it'll, when you do require sazzle, it'll look for a function called init sazzle. So require x will look for init x. It starts going through these lines, and the first one, Ruby constant get, will look for the module Mongo,
23:01
and the second one will look for the module sazzle, and have a pointer each one of those modules, and then define the class under the, I think here I'm doing it under the sazzle module. So it defines this class gsapi auth under sazzle, and that's the same thing as actually just creating a file with Mongo module sazzle and then class gsapi auth.
23:23
And then the next couple lines define methods on that class. And what they do is they'll define it on that object, the pointer that I've defined on the top, and it'll give the second argument is the name of the method on that class, and then the third argument is the name of the C function that corresponds to that Ruby method
23:43
that will be called on that Ruby class. And then the last argument is the number of arguments that it should expect would be passed to that method. So this is just a little snippet of code from the thing that you definitely will write if you write a C extension. This is essential for defining anything that will be loaded when you say require x.
24:04
So this is a C code. So again, the four things you need to do when you package a C extension with your gem is the ext comp file, so that creates the make file. You need a rate compiler task, not necessarily, but you probably should if you want to test your gem. You need the C code and you need your gem spec,
24:23
which will tell Ruby gems that you have an extension and it needs to be installed when you install the gem. So that seems pretty straightforward. I understand how to work with the Ruby API. I know how to write C code. I've packaged it up in my gem, and so now I'm ready to release. What you need to know about releasing is,
24:43
back to this story of how I released this gem and I had to yank it. I released it again on September 9th, and then September 10th, I went to the airport. I was on my way to Barcelona for Barcelona RubyConf, and my flight was delayed by four hours. I took out my phone to check my email. There's no wifi in the airport because it was one of those tiny domestic airports,
25:01
and I see this ticket saying, Ruby driver 1.11 does not install in systems without libsazel. And all I had to do was look at the first couple of lines of the error, and I knew exactly what the problem is. So it says that 1.10 cannot be installed on Amazon Bamboo
25:20
because of the following error. It says, failed to build gem native extension, and then it shows the lines that happen when it goes through extconf and looks for the header files, and then tries to install the extension in the process of installing the gem. And so the one line that told me everything was, it says, sazel, down sort of towards the bottom,
25:41
fatal error, sazel.h, no such file or directory. So when I saw this, I knew that extconf was running, so that was fine. Like, RubyGems is finding everything just fine. I put all the files in the right place. What was happening was, in extconf, which I have on the next slide, it was going through this file, and it said, I don't have sazel.h.
26:01
I can't find this header file. I don't have this library, so I'm just gonna abort installation. And aborting installation does not mean aborting installation of the C extension. It means aborting installation of the gem itself. So you can't optionally install a C extension inside your gem with RubyGems,
26:21
which means that the RubyGems limitation is a gem extension cannot, I just said that. An extension dependency is a gem dependency. So remember in the beginning when I said, like, nobody really uses Kerberos authentication with Ruby? So I just released the RubyDriver that has a hard dependency on a Csazel library
26:42
that allows you to do Kerberos authentication, but nobody does Kerberos authentication with RubyDriver, so why am I requiring that? So quickly, I consulted a lot of people, and I realized that the only solution to this was to put the extension in a separate gem.
27:01
And so what you gain by doing this is you can have a dependency on the other gem, or you can programmatically require the other gem. In this case, the other gem was called Mongo Kerberos. And programmatically, if it can't require it, if it doesn't find it, then I can just say, you know what, you can't do Kerberos authentication.
27:20
But it doesn't affect the installation of the RubyDriver of the Mongo gem at all. So that's what we did. We put the C extension in its own gem called Mongo Kerberos, and we released it, it was pretty easy to do that, just a matter of moving directions around and creating another gem specification. And then as I said, programmatically, when someone would go to do authentication,
27:42
I would do require Csazel, and if it couldn't find it, that meant that the dependency on Mongo Kerberos was not met. And in that case, I knew that I could just throw an exception to say, hey, you can actually do Kerberos authentication. But that wouldn't affect the installation of the driver itself. And as you can see, we have 309 downloads,
28:02
and I can assure you 300 of those are our testing environment. But anyway, I learned a lot from that. And I hope that if you get the chance to write a C extension, it'll be a little bit more clear to you than it was to me when I started out groping around, doing things by trial and error.
28:21
Because now you know that you'll gain a lot of Ruby knowledge by writing a C extension, even though you're writing C code. You'll understand what the Ruby language is, or the C implementation of the Ruby language would actually is by doing this. You'll know actually how to write the extensions of those four things that you have to do. And you'll know the Ruby gem's limitations.
28:41
So you'll know that if you have a hard dependency on a header file on the system, that better be a hard dependency of the gem you're trying to install itself. Because if you abort in the extconf.rb file, you're going to abort the installation of your gem, not just the extension. And that's it. Thank you.