 I'm going to talk about building CLI apps for everyone. So if you were looking at the board and you saw something about managing a community for Ruby Association, this is not that talk. Fortunately, he couldn't make it. So yeah, so I'm Terence Lee. I go by hone02 on Twitter. I come from Austin, Texas, which is only an hour and a half north of here. We have some awesome tacos. If you're ever in town and more than happy to take you out for some tacos, I know some people have collected on that. There's also a torches tacos, I think in San Antonio, halfway between here and the airport. That's what I've heard. They have awesome tacos. I, with a few fellows here in, Davey, who is not here at RubyConf, we run this weird conference called Keeper Rewired, which is in Austin, Texas, just happened last month. Stay tuned for year three next year. You probably hassle or any of how great or terrible the conference was. He was there. I'm also a big proponent of Ruby Karaoke. If you did not go to Ruby Karaoke at RubyConf, you missed out on a really awesome magical experience. This was us singing Bohemian Rhapsody, which is a classic of course. It really started, I was inspired by Charlie Nutter back in last year during RubyConf Taiwan. He karaokeed for a marathon like seven hours. I only managed to do like five, but it was a really great experience. This is him singing a duet with Plexus, which is always a good time. I have to give a shout out to PJ Haggerty, who cannot make it here, but he has been a huge proponent of Ruby Karaoke and has helped organize a ton of them, so much that I've actually organized none of them this year, but there's been one in every single Ruby conference I've been to, which has been pretty great. So I work at Heroku, I do Ruby things there with Richard Schneemann, who's sitting over there. He was the, he had that picture on the first slide, drinking that medium-sized margarita. Everything's larger in Texas, apparently. So on to the actual talk. So I'm here to talk to you about packaging in Ruby and why we should be building things in Ruby. And in order to get a sense of how this works, I'm gonna talk about the Heroku tool belt and the story behind it and where we started and where it's going today. And so when we first started, we were just a Ruby gem. So in order to get started, you just ran gem install Heroku. And I think even some people today are still using the Heroku gem, even though you're not supposed to. It's been deprecated. And at the time, it was a great decision because Heroku was just targeting Ruby customers. That was the only thing we supported on the platform. And so plugging into the Ruby gem's ecosystem made it really easy and convenient to get up and running. We didn't have to deal with package management. We didn't have to figure out how to deal with all this stuff because Ruby gem's right had a specification. And if you're a Ruby developer, you already had Ruby gems installed to already build your Rails and other applications. So that was a great point for us because it allowed us to move quickly and not have to deal with many other things. But the downside over time that we learned was that it required people to actually have Ruby installed on their system, which as we moved to be a more polyglot platform that became an issue, I've talked to some other people who have, like there's the SAS project, which requires you when it first got started to have Ruby installed to do like SAS stuff, which was a huge barred entry for front-end developers who may not be familiar with getting Ruby up and running. If you ever helped out out at a Rails Girls or Rails Bridge, I'm sure you're very familiar with how painful this process can be. And the other thing, even if you were a Ruby developer, we couldn't even guarantee the version of Ruby you're running was the same one. So we had to essentially support many versions of Ruby. On OSX, it was famous that they had Ruby 187 as the default version of Ruby for a long time. And even after Ruby 187 was deprecated and no security updates or anything were being added to it, we still had to make sure our gem was backwards compatible. So we couldn't take advantage of any of the new features and syntaxes just because we had to make sure it would work. Which made debugging and kind of maintaining it a pain in the butt. So then we moved on to essentially a package up installer where we would package up the Ruby runtime into this thing called the tool belt. And it wasn't, and we would take the gem and basically have the gem and the runtime with it. And so we would have to build that for every single platform that we wanted to run. I don't know if any of you have tried to package up Ruby for Windows, but it is not very fun. We had like a special box that would do that. And even for OSX, we had like a Mac mini when we first started this, like under someone's desk at the office that was just there for package up like OSX Ruby's. And we've automated a lot of that stuff since then, but it is still a lot more work than just pushing up a new gem, right? Like if you're just updating a gem, it is, you can just run rake release or some other task. And it's like very simple to get versions and stuff out. Now it's a much more involved process. And with the new like OSX signing stuff, like we have to have certificates and other things in place. So then there was this HK project that was started internally by an employee. And it was basically rewriting the entire CLI inside of Go. And one of the big motivating factors for doing this was that speed was, speed is a feature, like fast as a feature. And so at the time, if when this was benchmarked, running Heroku version on the Ruby gem or the tool belt took almost two seconds. And the Go equivalent was 16 milliseconds. And so you're talking about like orders of magnitude of difference here. Admittedly, the Go version did a lot less than the tool belt version, but I think it does show some baseline on performance difference there. And I think a lot of this can be attributed to the fact that having to load a bunch of these things inside of Ruby at runtime can be a slow process. If you haven't heard of this before, but require is pretty slow. There is a performance impact for doing that. And so especially for a command line application, you want to split up your application as it gets bigger, because you don't want to have like the monolithic Perl like 23,000 lines of Perl code that you're trying to parse through for a single command. So you end up splitting it, but then you have to do all these hacks, which we've done in the tool belt to only load the files that we need to at the time that they're needed, all throughout the code base as the default version of Ruby for a modular and actually maintainable. The second thing that was really appealing about Go for us when we were doing this was that you could statically link each binary, which meant that we could build a single file that we could distribute to every single operating system that we cared to support, which meant for Windows and OSX and Linux, like it was one file, we could just package that up and then give it to someone and they could install it on their system. And it wasn't this like folder of files and things plus some runtime and path environment variables to make sure that all that stuff is set up. And if you had an existing Ruby install, we had to make sure we weren't like munging and overriding stuff while you were running the tool belt that it would pick up other things that you already had set. So I don't think the Heroku tool belt story is unique. I know HashiCorp has had a similar story. Their Vagrant project, a lot of their CLIs were originally written in Ruby. Vagrant is still written in Ruby, but they went from like being a Ruby gem to not being a Ruby gem anymore last year to just packaging up itself and then all their newer CLI stuff is written and go for a lot of these same reasons. If you do want to package stuff up in Ruby though, there's a project called Travelling in Ruby by the Fusion guys, probably most well known for mod Ruby and Fusion passenger. And the nice thing here is that you don't actually have to rewrite your entire application. You can still use Ruby and it kind of handles the tool belt part of packing up a runtime having extensions that you're gonna use. If you have native extensions like Nogigiri for instance. But the downside is that you're actually restricted to all the runtimes and extensions that they have pre-compiled and the exact versions that they have compiled. So if you want to use something else you're kind of out there on your own to try to make all that work if you want to support Windows and Linux and OS X all at the same time. And when I was in Portugal, I actually ended up talking to SF Eric and he was telling me about this crystal project. So besides go, there's also crystal which is a statically typed compiled Ruby like language that allows you to build things. And after kind of seeing this over the last few years and we have a ton of Ruby application or Ruby CLI is like an ecosystem for it with like option parsers and GLI and Thor and things like that. And there was a lot of CLIs that originated in the Ruby ecosystem and it's a little sad to see them all go away. And I think a big part of that is because our packaging story isn't great for packaging job MRI, right? Like to deploy a Rails application we have these Chef scripts that go and like clone your repo, run bungler and then like do all this setup stuff to hopefully make it all work. And instead of using other languages I wanted to continue to build things in Ruby which is kind of the inspiration behind all this stuff. And I gave a version of this talk at Rocky Mountain Ruby and Steve gave a kind of really poignant tweet in response that I think applies heavily here. There's nothing wrong with using other languages like Go and Crystal are both great languages in their own right. And if you are happy using that stuff like you should continue to go ahead and do so. This isn't a language bashing talk. But I want to continue to build things in Ruby and I wanted to find a way we can shore up the weaknesses for continuing to do stuff in Ruby where people want to choose to do so. And I think picking Ruby shouldn't be a technical limitation of why you're working on your project that you have to migrate everything off of it. So how do we make packaging possible if you want to continue using Ruby? So Zachary Scott and I started a project called MRuby CLI back this early summer. We spent a bunch of time trying to solve this problem. And we had a few design goals in mind. So if we're gonna write a thing for Rubyists, you should be writing most of your code in Ruby. I think that's a pretty important point. And so inside of MRuby CLI, you have an MRuby lib directory and then you can essentially just have a bunch of RB files which are Ruby files. And you can have subfolders in them like you would in a Ruby gem. You can put anything, you can put all these RB files in any kind of organizational structure that you want as long as it resides in this folder. The next thing is that performance is a feature and I learned that through the HK story as I saw that develop. And what I really mean by that is actually startup performance. So generally like when people do these benchmarks, you take it with a grand assault but for command line applications it's actually really pertinent, right? Like how fast can you boot up the Ruby runtime and get something out on the screen because most command line applications are hopefully gonna be like this quick thing where you're interacting with it relatively quickly and you want to be able to get that fast feedback loop. So inside of MRuby, I'm sure most of you have written this application before where you literally just print hello world to the screen. So on Ruby 222, which is what I tested this on, it took 40 milliseconds to boot up which is not super slow and that's totally tolerable and it's pretty respectable to deal with that I wouldn't mind writing command line applications in this but hopefully like as you start to add require and other things this will definitely slow down. And so when I did this in MRuby CLI as we started to build out this project, the simple hello world application was three milliseconds. So it's an order of magnitude faster and to me that gives you a lot of headroom to do a lot more interesting things inside of the project and still get that really fast like iterative kind of workflow that you would want out of a CLI. Like if you're building a CLI as a product of your company and people are running a bunch of commands, like you definitely want to have that kind of loop and feedback system. So like I was saying earlier, there's a number require inside of MRuby which means that as you continue to add files into it you're not gonna get that kind of runtime slowdown that you would in MRuby itself. So as the CLI apps get bigger, it's only the performance differential for the startup time is only to get bigger. And the third design goal that we had in mind was that like the go thing that made that so attractive is that the product and artifact that we have to produce is this single binary that people can ship because that's a very attractive system and not having to figure out some way to package all this stuff up and set up all these environment variables and things like if you just have a single binary that has all the runtime and things included, you can just ship that to the customer and they can get that up and running relatively quickly. So inside of MRuby CLI, we have a bunch of different platforms and you essentially when you run the build, you see the build name and then it produces a single binary. So for the Hello World thing, this is the build summary that you get. And we have one for OSX as well as Windows so you can cross compile for both, for all Linux OSX and Windows which is a really great experience. And to give you a sense of how big these files are that include this MRuby runtime, the OSX binary is only 421 kilobytes. So not the smallest thing but not terribly large either. And the final thing was in order to actually get any traction on this, the setup has to be really simple to get up and running. In order to do all this cross building stuff like Go does, like you have to set up all these tools and we wanted to make that as simple as possible because we knew that MRuby CLI would be a real underdog compared to all the other solutions out there. So we're leveraging Docker. We built a container. If you're not familiar with Docker, it's basically a Linux containerization thing that allows us to provide a simple setup for people who are interested in trying this out. So we have a tag pushed up onto Docker Hub that has basically all the tools and things you need. So in order to get started, all you need is Docker and the MRuby CLI binary. You don't have to have Ruby install. You don't have to have any, you don't have to have GCC or NEC compiler and none of the cross compiling tools for OSX and nothing for Windows either. So this allowed us as well to only target like one platform to then cross build for everything and not have to kind of handle that three by three matrix of how you compile from one thing to the next thing. So the simplest hello world example of what this flow looks like is when you have MRuby CLI on the path, you pass this stash set up and you name the thing like in Rails new of what you want to call it. So hello. And then once you CD into that directory, you have a bunch of files and you run this compile task that through Docker compose and it will, you go and grab some coffee, wait for it to compile and then you can run a shell or just execute the actual target build that is specific to the platform you're actually building this on and when you run hello, you see hello world. So that's kind of the workflow of like how you would get something up and running from nothing. And so when we actually run the setup like in Rails, we produce a bunch of files so you don't have to write them yourself and this allows you to get up and running relatively quickly and basically for the rest of the talk we're going to kind of go through like what is generated and kind of how all this stuff works and fits together. And EmRuby CLI itself is actually just this idea and really just that Rails like template generator and that's all it is and it pieces all this stuff together and most of the legwork has actually been done by EmRuby itself and the entire build system and everything involved with it. So in order to really understand how any of this works, we have to take a step back and just talk about EmRuby. So what is EmRuby? EmRuby is this embeddable Ruby that is meant to be lightweight because it isn't being embedded in multiple different architectures and types and whatnot. So it's meant to have quick boot up times and lightweight in memory. And people ask me all the time like, okay this is really cool project like where is it actually being used? And I was talking to Matt yesterday at our buff and asking about it and they use EmRuby in Japan, there's a company that produces routers and they use it to basically extend like the firmware. So you can write EmRuby code to extend the firmware stuff that you're using and not have to say drop down to see your assembly or whatever the firmware is being written in, which is really cool and it makes it really powerful and flexible. And here's some of the contributors that have been working on EmRuby. So what are the big differences between EmRuby and MRI? Like most of you are probably not super familiar with EmRuby and it's maybe your first time really like looking into it seriously. So since EmRuby is being built and targeted for different architectures besides just x86, like it can compile to Android and iOS as well as it's often used for like IoT type of things as well. And Raspberry Pi or Arduino, stuff like that. That means that we can't guarantee that there is actually a file system there or any like the OS level things that we're used to having inside of MRI itself. So that means no file, no socket, no input output kind of thing. It's not thread safe because threads are an operating system level like feature. So that also means there's no threading or forking as well. And the syntax itself is a subset of Ruby 1.9 with maybe some stuff from Ruby 2 like string freeze, which got added recently. But on the flip side, it is an actual Ruby language. So that means stuff like procs and blocks that we're used to having, DHH freedom patching that he talked about a few years ago and metaprogramming and literals, like things that we're used to having inside of Ruby are there and they work like you would expect inside of Ruby. So in order to understand how like all this kind of comes together, we have to look at how you actually run and Ruby code, like I write a Ruby file, like how do I get it to actually print something to the screen or do what our command line application does. So the simplest way, like inside of MRI, you can actually, when you build a Ruby, you can get an M Ruby binary, which is just the interpreter. So you can pass that she prints stuff out. You can also take a Ruby file and then pass it in as an argument to the M Ruby binary and it print and it just goes through and interprets the code. And we also provide an IRB binary as well, so you can get all that REPL goodness that was really exciting when I first got started in Ruby. You can do that as well and M Ruby. But where it starts to get really exciting is that there's an M Ruby bytecode compiler. So you can take any M Ruby script that you're writing and if you use the M Ruby bytecode compiler, it actually takes the code that you've written and puts it into M Ruby bytecode and you get this dot M Ruby file that you have. And once you have that, you can use the M Ruby interpreter pass the dash B flag to specify that this is a bytecode representation and run the code. So it means at run time, you do not actually have to go and do that like compilation process. And if you take this a step further, you can actually take that and embedded inside of a C file and get M Ruby bytecode compiler to actually produce C code with an array of all the stuff that you need. And then you can go and write a main function that invokes all this stuff and then you can go ahead and basically compile down a static file. So if we take this to its natural conclusion, you can actually produce a single binary with all this stuff. And so with M Ruby C Lies, since we wanted to focus on people producing Ruby code and not writing a bunch of C, I assume many Rubyists are not familiar with writing a bunch of C. C is not my best language. We produce a basically a C wrapper script for you that takes all the arguments that you need from the C code and passes it down to M Ruby. So inside of your Ruby code, you have access to all the arguments and you can just write all of your stuff inside of Ruby and not have to write any of the C bits. So the only contract we actually have is that you have to define, we call out to this underscore underscore main underscore underscore method and the argv is a Ruby representation of the arg array that is being passed inside of the C code. So like I was saying before, we have an mrblib folder that has all the Ruby code and again, like it's really important that we're letting people write stuff in Ruby. And one of the awesome things about M Ruby is that all the stuff in it is all the whole entire build system is built on top of Ruby itself. So the entire build system uses rake, there's a bunch of rake tasks and stuff. So in order to compile and test and do things like you can just run a rake task and that's the same for M Ruby CLI, we also just leverage the rake system. And so one of the, so since this stuff has been built, one of the unique things about it is there's this buildconfig.rb file that basically helps you configure what you're building. So you can essentially create a new build for the targets that you want to build for. And in our case, we generate one for OSX and this is an example of what it would look like to do a new cross build. You don't have to actually write in this stuff out, like we generate all this stuff by default, we generate that buildconfig and we populate with all these things. So in general, you don't really have to be touching this file unless you want to be overriding stuff, like you have specific requirements or whatnot or you don't care about compiling to some operating system, you can just delete this or comment it out. And so when we run Compile, we see that we get all that stuff built. And there's also this system called MRBGems, which is probably the closest equivalent to what MRI has for RubyGems. And inside of this MRBGem or break, it's similar to the RubyGem specification. If you've ever written a RubyGem, probably a lot of this stuff looks familiar to you. You specify all the metadata. And where it starts to get really interesting is this add dependency, like this looks different than actually inside of MRI itself. So it takes two arguments, there's the name and basically where this gem comes from. Since there's not exactly a central repository like RubyGems.org that's really standardized as a service where you can push up new releases and stuff, you actually have to tell MRB where to get this stuff from. So there's three different kinds of locations that it supports out of the box. There's core, so inside of the MRB directory itself, there's an MRBGems folder inside of it. There's a bunch of MRBGems that you can use. And the reason that it's split out is because you're trying to, again, have a small footprint, which is great for CLI development. So we only wanna be pulling in the things that we want to be using. So there's many extensions that we're probably familiar to having inside of Ruby for array and enumerators and things like that. And if we want to actually leverage that, you would essentially pull that in as a dependency. So we can really limit what we're actually pulling in and not have the entire kitchen sink. So that's all bundled in and maintained by the MRB team. There's MGM, which is the closest equivalent to RubyGems.org, but it's not quite the same thing. Essentially, it's a repository on GitHub. And when you want to add something to it, you send a pull request that adds your MRB gem into this system and it basically clones it and then there are essentially just other GitHub repos for the most part, but it's a system that allows you not having to know where all this stuff lives and you can just specify the MGM list. Of course, you can also just use GitHub itself, which means if you want to start testing a thing that you didn't feel should be on MGM yet because it's not solid, but you need to test and build it. Or if you want to fork a gem, right, and you want to do that, there's an easy way to add that there. And so one of the things inside of Build Config, like I was saying, overriding stuff, one of the things you can do is actually override any dependencies that you have conflicts for. So if a MRB gem has another dependency but you need to fix some bug there, inside of an MRI, like in Bundler, you probably would fork that thing on GitHub. You'd have to fork the parent thing to have the gem spec point to your other thing and then make sure all that stuff's included. Inside of MRB, we can actually just specify this bottom line here, the conf.gem, and it will use that over anything else that is specified inside of MRB gem rake and all the other MRB gem rakes that you're pulling in for the other MRB gems. Since we're Ruby-esque, we need to test stuff. So there is unit testing through Mtest and that is a gem that can get pulled in and then you can essentially have a folder of test files that are all unit tested. One thing to note is that since these are unit tests, they're running inside of MRB so you're limited to the MRB syntax and code and the dependencies that you're actually pulling in so you can't pull in extra stuff if you don't add it in. But it does allow you to write really fast unit tests. So this is an example of what that looks like. It looks probably pretty familiar to most people who just run any other unit tests. The next thing is that we actually also support bin tests and this is built into MRB and this actually uses MRI so you see those require files which should give you that hint and it just leverages open three and it's a framework for kind of wrapping all that stuff around it and basically you just want to invoke the binary that you're producing and just test input outputs there. But since you have Ruby to the entire MRI system to your disposal, you can do a lot more fancier things inside your integration test for setup and whatnot. So this is an example of what an integration test would look like inside of MRB. So this all sounds great but what are the gotchas that you're gonna be facing when you want to build something like this? So the first thing is that MRI has a rich like standard library out there. I mean in AJA's keynote, you saw like Renda and Tuple Space and stuff like that. None of that stuff is really available inside of MRB and even the stuff that is is fairly trimmed down in order by design to keep it lightweight and small. So if you wanted to port something over to MRB, it's not as easy because there's many, even if it had no dependencies inside of MRI, it probably leverages a ton of the standard library which may or may not be working 100% inside of MRB. And that also means you can't leverage RubyGems, right? Like that entire ecosystem of RubyGems that is out there, I think and I was told it's like 7,000 gems. Like none of that can be directly just like pull this thing in for RubyGems and expect it to work. So that's a huge bummer. And in order to support all this cross compiling stuff, the MRB gems that have native extensions and C and whatnot need to support cross compiling in order to get it to work across multiple operating systems. So as ZZAC and I have gone through and gotten stuff to work for MRB CLI and the MRB CLI projects that we've been building, we've gone and started adding support for cross compilation. So like Lib YAML and the XML parsing stuff with, or no, the regex stuff with Onigurama. We've gotten all that stuff to cross compile and work on Linux OSX and Windows fine. So this is definitely like in a huge uphill battle because I think a lot of the gems are essentially built for the use case of the other. And so they don't need to support all these other platforms. But there is, I did wanna talk about one success story that we've had with MRB CLI. So there's this project called JRuby Launcher inside the JRuby ecosystem. And normally when you download JRuby, you get like either a bash file that boots it up on a Unix system or you get a bat file on Windows. And they essentially had to maintain both of these things, which can be kind of a huge pain in the butt. And so they have this gem that you can gem install called JRuby Launcher that is the C++ code that can be used to basically launch your thing. And they could have a unified code base for all the offering systems they wanted to support. And so inside of this JRuby Launcher code, you have a bunch of C++ and header files. And just to pull like one of these files, there's this copyright from Sun Microsystems at the top of like the main JRuby CPP file. And its last copyright was 2010. It's kind of old. And Sun Microsystems doesn't even exist anymore as a company, so there's that. And if we look at like some of these commits, like the last, some of these files, like that CPP file hasn't been touched in six years. So you can imagine how fun it must be to maintain this thing. And so a coworker of mine, Joe Cutner, started this project called MJ Ruby. And essentially it's this, the entire JRuby Launchery written on top of MRuby CLI. So inside of it, he essentially rebuilt all this stuff using Ruby code, a mix of Ruby and C code. And he built some libJVM bindings inside of MRuby to actually like interact with the JVM itself. But all the option parsing and stuff is written in Ruby, which is definitely much nicer for a Rubyist to do than to do all that string manipulation in C++. And one of the also great things about it is that he has unit tests, so this is a test folder of all unit tests to test like all the option parsing. I don't believe the JRuby Launcher code has any unit tests. They're all like integration tests. But in MRuby CLI, we can have both. So it's really great to see projects like that come up and especially for a project like JRuby, like I'm sure a lot more people will be willing to get involved in a, in MJ Ruby than JRuby Launcher, like to actually contribute back and be able to understand and see what's going on. So what can you do with MRuby CLI? Like how do you fit into this ecosystem of stuff that we're building? So one of the things is, like I said, we started early this summer, which means the community's really young. MRuby itself is only like five or so years old, I think, since its inception publicly. But that also means that like it's, although it doesn't have a ton of commits that are flowing through all the time, like the MRuby GitHub, MRuby's on GitHub and we support pull requests and things on like the MRI project, which is still on Subversion. So for a lot of Rubyists, that this is a lot more familiar to work with. And as you can see, like Matz does read the pull requests and comments on stuff, which is great to be able to interact with Matz and other people on this project. So it's a fairly active project, but it's not so trafficked that like you can't follow through. Like there's only a few commits that usually get into master a day, if that. And the people who are active on the project are fairly active. MRuby 120 just got released this morning by Matz and I prepped a release this morning as well for MRuby CLI. So before we used to depend on master on MRuby, which was not the greatest thing because if something in master came out, it could potentially break your build, which was not ideal for me. So now that we dependent on a lot of new features, but now that 120 was out, we can now lock it down to the release of 120, which means future versions will be a lot more stable. So you can go to the MRuby CLI project and if you go to the releases page, there's a binary for every single or for release OSX Linux and Windows 32 and 64 bit binaries that you can download and use and you just need to put it on your path and it will be a self-contained thing that works. So now that you've seen all this stuff, like how do we actually go and build a binary? So like I was talking about downloading, go download the MRuby CLI binary. If you don't have any of the Docker stuff set up, you need to install the Docker toolbox to get Docker and Docker compose. And then MRuby CLI setup will go through and generate a new application and then go ahead and modify it. So like one of the things that I got, I think like one of the first C++ intro CS classes I took, the assignments was like make a calculator, like make addition and all that stuff work or maybe build the HP calculator that Aaron was talking about in his talk, all that string manipulation and C++. And then you just run this Docker compose compile command and it should go and fetch the containers for you from Docker hub if you don't have it, so you don't need a separate thing for that. And then you basically just like rinse and repeat this cycle of modification and recompilation. And you'll see that once you have it actually compiled the first time, like subsequent compiles are actually relatively quick and fast. And I'd love to actually hear about anything that people build. We have a few projects that people have been working on, but no matter how small or if it's large that you're building, I would love to engage and talk and see what works or what doesn't work for you. On this project, this is a huge passion project of mine and I really enjoy working on it, so definitely love to engage there. And I appreciate the fact that our community as a whole is really awesome. So we have stuff like Friday hugs, things like Ruby Karaoke, the fact that our community enjoys doing all this stuff. I wanna continue to build things in Ruby and I hope you do and I hope we can help remove technical limitations of not choosing Ruby as the technology for doing stuff. And so let's go out there and build things in Ruby. Thank you. Is it, how much time do I have? So the question was, is the byte code spec published anywhere? I don't believe it is, but Matt's would definitely know better. I don't know if he's here. I don't think he is. You should ask him during the Q and A in the, I guess at the keynote, closing keynote since he's not here. Sorry. So the question was the stuff you penned on in the tests if you're able to separate that from the final binary. So there's actually, the test build is a specific separate build that's specialized inside of MRuby. So the tests that you write are not actually in the final build that you produce. And then there's a add test dependency that got added recently in one, two. And so that allows you to add dependencies inside of your MRuby project. In general, that won't get produced in the final build. There were, that feature is like relatively new. So I ran into some issues with it, but supposedly it's fixed now. And yeah, so you should be able to have like a test specific thing that runs that doesn't include, that isn't included. And oftentimes, so the first build you have is usually a host build. That's the thing that you run on your local machine. And in that build, I have a debug and other options set. So you can see like the stack trace and backtrace of stuff. And normally all those things, so then even if the target build was the same as my host, I would have a separate target build that didn't include all that stuff. So the question was, is the Roku tool belt gonna use this? The answer is no. I'm not involved with the Roku tool belt heavily anymore. And that ship kind of has sailed already with the go stuff. Like if you're already happy using some other language there probably isn't a huge reason to then port your entire project back over. Like a reason might be maybe you are in a company that has a bunch of people that are Rubyists and it would be easier for other people to get involved. But we have someone who's in charge of the CLI and they do both Ruby and go. So they're comfortable and just continuing to work on the go stuff. So the question was, when you package up an M Ruby gem, it looks very similar to a Ruby gem. And is there any compatibility between the two? So I mean, I think even the found names aren't the same between the gem spec. Like it's blah blah dot gem spec. And then in Ruby gems, it's mrbgem dot rake. And it's the same name for all the mrb gems. I mean, I imagine you could probably produce a package that had both of those things. But I would imagine you would run into a lot of issues for most people that there's many more libraries that you need inside of an M Ruby project. But I'm sure that you could conceive something that the mrb gem dot rake pulled stuff in that was missing, that wasn't in your gem spec. But I haven't seen anyone actually use that. But I don't think it's out of the realm of possibility. Cool, thanks everyone. Thank you.