 talking about evaluating Ruby without Ruby. Let me introduce myself. My name is Takashi Kokubun, working at Cookbot, which is a company in Japan that's host recipe sharing service, cooking recipes. I'm working as developer productivity group at Cookbot Inc. The recipe sharing service is Rails application, and we provide your print environment for Rails applications. Today's topic is Ruby DSL, and I implemented the tool with M-Ruby and I'll show you why I did that. Introduction to M-Ruby and two ways to build portable serial CLI apps configured by Ruby DSL. First, talking about Ruby DSL. DSL is domain specific language, which is a language specialized to a particular application domain. Ruby DSL is a DSL defined in the terms of Ruby language. For example, this is Ruby DSL I'm talking about today, and this is first package is installs NGX, and this can work in both CentOS and Ubuntu. Next section is service NGX, and this enables NGX startup or server and start NGX process. As you can see, installing NGX and starting NGX is abstracted away for some distributions, and we can configure it with Ruby. It's boring if we write this in JSON, because you are Ruby, you are happy to write this configuration. So how is Ruby DSL implemented? Have you ever implemented Ruby DSL? Thank you. For example, if you want to evaluate this package NGX and returns package and NGX array, the simple implementation is like this. Defines DSL cross that has package method, and when package method is called, it stores the package name to result, and we can get results from the DSL's result method. The more complex case, if package has nested DSL like action, we will specify action install, specifying install action to NGX package. To implement this, you have to create package class, and you can evaluate the nested action API in package class, and you can do that by instance exec method. So how is Ruby DSL implemented? Usually, Ruby DSL is implemented with MRI, using instance evo or instance exec, etc. But we wanted to evaluate Ruby DSL without MRI. Why? Let's have a look. The second topic is our motivation to evaluate Ruby DSL without MRI. Ruby DSL use case at Cookpad. We developed development environment bootstrap script for Mac OS and Linux. We use not only Mac OS, but also Linux distributions to develop web applications. We wanted to use Ruby DSL to simply describe operations for multiple OS distributions, like I showed you before. For maintenance cost, we use the same Ruby DSL as the server provisioning tool we develop and use, which is called Itamae. Itamae means Chef of Sushi. So the DSL is inspired by Chef, the server provisioning tool. For example, this is DSL of Itamae, and first this describes installing Memcache D and PostgreSQL. If you write this DSL, you can install Memcache D and PostgreSQL by Itamae to Mac OS, and also you can install this to Ubuntu by the same DSL. But there is a problem. Our problem to bootstrap development environment using MRI is there are difficulties to use MRI on initial environment. For example, Mac OS, it ships Ruby 2.0.0. It is not maintained now, and which is shipped with the latest Mac OS. So we can't use maintained Ruby. And for Linux, most Linux distributions doesn't ship MRI out of the box. So we don't want to manually install MRI prior to bootstrap because it is a first step of bootstrapping development environment. And if you use MRI for users' environment, you have to care about how a gem is set up every time you run Ruby DSL to bootstrap development environment. Our Ruby on MRIs already installed is a gem installed to MRI currently selected by RBM. And packaging Ruby or gem commands to run just one gem is little overkill. It's cost to maintain. And how can we then, how can we leave them dependency of MRI? For the reasons I talked about, told you, we want to remove dependency of MRI. All these problems are resolved if it doesn't depend on MRI. And can we embed Ruby VM into our software? So let's talk about MRuby. This is, I'll talk about MRuby. MRuby is a lightweight implementation of Ruby and which is embeddable in hardware and software. If you embed MRuby to hardware, you can learn, you can compile Ruby scripts to very small binary and you can run it on a limited resource hardware. And if you embed software, you can learn Ruby scripts from your software. It is not written in Ruby. So today's topic is software embedding of MRuby. And if you embed MRuby, you can embed Ruby scripts from any software, even if it is not written in Ruby. And it allows us to build a software evaluating Ruby DSL and independent to MRI. As I showed you before, normal DSL, normal tool that runs Ruby DSL is implemented with MRI. But if you use MRuby, you can make it independent to MRI. So how to use it? How to embed and use MRuby? Firstly, you have to get from MRuby repository and just run make command. And by executing make command, live MRuby.a is generated in that directory, view the whole three directory. And it should be linked to your software to use MRuby. And then call MRuby's CAPI. Or use its binding for a language you use. If you use non-C language, you have to write a binding to call the CAPI. This is an example to embed MRuby to software and calling the CAPI. Firstly, you have to initialize MRuby VM by MRuby Open. And after the initialization, you can evaluate Ruby scripts like this. By calling MRuby RoleString API of MRuby, you can learn this hard-word join script. And you can get the result of Ruby script as a C string, an array of characters. And you can print the hard-word string in C language. So now we can embed MRuby into our software. Is it enough? No. Our use case is development environment bootstrap. And we should also care about dependencies other than MRI. So we want to build it as a single binary, which does not depend on other dynamic libraries. And there are some good ways to do that. I tried two ways to do that, so let's compare them. The main contents of today, building portable CRI apps configured by Ruby DSL. I introduced two ways to build portable CRI apps configured by Ruby DSL. The first way to do that is go MRuby. Using MRuby only for environment Ruby DSL and implement other parts in Go Rangage. The next is MRuby's CRI, which is introduced today by Eric. And this way, I implemented both Ruby DSL and others using MRuby. So I experimentally re-implemented the two I introduced before, which is configuration management to Itamae. So let's compare them. The first way to do that is go MRuby. Using Go, you can compile Go script into single binary. So embedding MRuby to Go Rangage, you can build single binary and you can run Ruby scripts from the single binary. So this example is using Go MRuby, and which is called Itamae Go. You can see that's a real example at my GitHub co-cocuments like Itamae's hyphen, Go. How to run Ruby scripts from Go? First, again, you get Chrome MRuby and make a review of it there. And if you want to use Ruby from Go MRuby, you have to copy the Ruby to Go MRuby's directory. And importing the directory and code MRuby APIs by Go Rangage. This is an example of using MRuby from Go Rangage. First, you have to initialize MRuby VM like the C Rangage. And MRuby road string is in Go, which is a road string in Go Rangage. You can do the same thing in C Rangage, a hardware join script. And you can evaluate that and get the Go string from the road string value API. So how to implement Ruby DSA in Go? If you use Ruby for all implementation, you don't care about converting the data structures. But to implement I operations in Go, it is necessary to get data for Go from an evaluation result of Ruby DSL. I implemented the method for the Ruby DSL in Go Rangage. First, define method. It is necessary to call define method. And first, define function named package. It is empty for now. And that returns new. And then for simple case of the DSL, we want to get the result of the Ruby DSL evaluation. And the result is the right package resource struct. We can get package resource struct and its name from this API. And the complex case, then if block is given to the package DSL, the argument is increased to 2. So by checking the number of arguments, the calling, initializing package class, and call the block by insensitive value, and calling the methods attributes and actions or any other attributes, you can get the result of Ruby DSL evaluation. The package is implemented in Ruby. You can define any class in Ruby by mrb-worldsling. So this package that knew was defined in Ruby. But as you can see, it was a little hard to maintain. Writing bridge to connect Ruby and Go words is really boring. And checking errors for all Ruby method calls is boring. Like all method calls in this example returns underscore. But in the real use case, you may check the all errors from all method chains. It's very boring. And you try to use three languages because to define package class, you have to write Ruby, and the conversion is Go language. And the base of mrb-gem is C. So you have to use three languages for maintaining this library. And you need to prepare environments for cross-compiling by yourself. The next example has a very useful environment for cross-compiling. But if you use Go mrb, there are so few examples using this. So this is how to cross-compile and linking the library to the Go script and cross-compiling it. But why did we need Go language? Why did I choose Go language was the purpose of that, it was to make it as a single binary and to implement IO operations for developing environment bootstrap. In this case, other parts of the Ruby DSR is all other parts are implemented in Go language. So it is very easy to implement IO operations because there are many standard libraries to implement IO operations in Go language. And so can't we achieve them without Go language? If you can implement IO operations in C, you can do that in mrb2. So try mrb-cli. The next example is using mrb-cli. So I implemented the next example called mItamai, which is alternative implementation of the server provisioning to Itamai and the former Itamai Go, which is actually maintained by me for now. And it is single binary and built with mrb-cli and written in pure Ruby because it uses mrb-cli and all of the parts are implemented in Ruby language. You can see that code in my GitHub. Then what is mrb-cli? mrb-cli is a cross-compiled platform to build CRI apps using mrb. You can see that on Terence with mrb-cli repository. And Eric gave a talk building maintainable command line tools with mrb this morning. If you missed it, watch the video reader. So how to use mrb-cli? It is very simple. Downloading mrb-cli command from the mrb-cli's releases page and generate boilerplate to implement the tool by mrb-cli-s application name. And change directory to that and docker-compose-run-compire. You can get binaries in that directory. So just executing these commands, by just executing these commands, you can build a single binary. That is not depend on mri and implement it with Ruby. How to implement Ruby DSL with mrb-cli? Which is almost the same way as mri, but something is different in mrb-cli. You got to add some mrb-gems to call methods which is not provided by default. In mrb, since mrb is very small implementation, some default features in CRB are omitted. I used mrb-eval for instance evil and mrb-object extension for instance exec. So what is mrb-gem? It is like Ruby-gem for mri. But unlike mrb-gem, it's compiled together when building mrb. So the docker-compose-run-compire, when you execute it, mrb-gem is compared into the same binary. So you can use it without adding dependency to the binary. Some of the features in mri are separated to mrb. So if you miss some of the features in mrb, you can search it from mrb-gem. To implement the server provisioning to since it was implemented originally in Ruby, I wanted to port existing standard libraries or Ruby-gems to mrb-gem. It's very easy when they are written in Ruby because most features required by Ruby programming except keyword arguments are available at existing mrb-gems. And I created some mrb-gems to implement in metamai, which is share words and hash gem. But if the methods are not implemented in Ruby, it is a little hard to use that. If the method is written in C, you have to write C to implement the mrb-gem. Some of IO operations in mrb already existed. But I wanted to use capture standard out and the standard error that exists data separately. There are no open3 library and counter spawn method. So it is very hard to achieve the capturing standard out and standard error and exist status at the same time. And it is necessary to write C rank is to implement counter spawn command, spawn method. This is the implementation of spawn method in my mrb-gem open3, mrb-open3. Firstly, this method, this function calls fork and it redirects standard out and standard error. So as you can see, this implementation is very limited use case of spawn method. Original spawn method has very, very, very various options. And it is hard to implement all of the features. So I implemented original spawn for this mrb-open3. And writing the methods in C langage, writing the methods in C langage, we could build open3 capture3 method using the API. As you can see, it calls out and error option in string. But the spawn method has only these methods, these options. So it was a little easier, but the spawn method is incomplete. So if you may not familiar with C langage, you think it's hard to implement apps with mrb-CRI. I think, yes, it's hard a little when the necessary mrb-gems are absent. But there are great merits to build apps using mrb-CRI. The first merit is you can distribute applications easily. That single binary are not depending on users' environment, like gem command or Ruby command, and which can be cross-compilerable easily by the Docker file provided by mrb-CRI. The next merit to use mrb-CRI is the startup of the command is very fast. If you use MRI to implement the original command, it takes 700 milliseconds to execute very simple version command. But if you re-implement it in mrb, since it's already compiled, it executes very fast. It takes only 23 milliseconds. It is 33 times faster than original MRI implementation. And the very important part is that it's very maintainable. No bridging code, like Go and Ruby words, or I'm very happy to write all invitation in Ruby, if you are Rubyist. And this is conclusion. To simplify development environment bootstrap, I made a configuration management too configured by Ruby DSL as a single binary using mruby, which is on that GitHub. And there are two ways to build that, too, configured by Ruby DSL as a single binary. If there is no reason to use Go language, if there is a reason, use Go mruby. And otherwise, mrb-CRI is recommended for that purpose. That's all. Thank you. Any questions? The question was, sorry, can you port, can I port the part I write in C-language to mrb-gem? I exported it as mrb-gem, mrb-open3. Since the spawn method is used only by mrb-open3 library, I implemented it in the mrb-open3 mrb-gem. So it is already ported to mrb-gem. Any other questions? OK, that's all. Thank you.