What is a Ruby implementation?

Ruby

Published on by

Mathieu EUSTACHY

Mathieu EUSTACHY

7 minutes reading

Did you know that, when talking about Ruby, you might not talk about something the same thing depending on the person you talk to?


Wait. There are several Ruby?! 💎


Let’s wrap it up!


This article is the first article of a broader series about “low-level” computing concepts applied to Ruby.

  1. What is a Ruby implementation?
  2. Process management in Ruby
  3. Concurrency and parallelism in Ruby
  4. Thread management in Ruby (TBA)
  5. Memory management in Ruby (TBA)


Always keep in mind that I am intentionally summarising things to give you a quick overview, there is more to each concept.



Before diving into “low-level” computing concepts applied to Ruby, we should first discuss about which implementation of Ruby we will talk about. In this article you will learn what is a Ruby implementation, what are the alternatives to the default one, and why it’s the fondation stone to this article series. 



What is a Ruby implementation?


A Ruby implementation is a specific version of the Ruby programming language that includes its own interpreter, runtime environment, and associated tools. It is also assumed to pass the Ruby specifications written in RubySpec (https://github.com/ruby/spec).


There is something that most Ruby / Ruby on Rails developers don’t properly get: the Ruby programming language, as the raw text that you write in your code editor, is something different from the Ruby implementation. The Ruby implementation is the way to make your Ruby code works with your computer.


Think of Ruby as a language like English (Ruby code), the implementation is how the language is actually coded in your brain (computer code). That’s because your computer can’t read Ruby, it needs to transforms Ruby into something that it can understand, and that’s the implementation role.

 



Ok, but I never heard of Ruby implementation before and I can write Ruby code, so why is it so important?


Well, I stumble upon some issues in my developper journey, for example, a job a client’s app was running had a memory leak. Back then I was asking the following question to myself: “How does Ruby manage its memory?”. That’s a question that you might struggle answering, but it’s a basic one. And guess what, it’s something that is done automatically by the Ruby implementation for you.


So, diving a bit into the internals of the Ruby implementation will make you for sure a better developer. That’s the purpose of this article series by the way.



Ruby default implementation (MRI)


“One Ruby to rule them all, one Ruby to find them, one Ruby to bring them all and in the darkness bind them.” The Lord of the Rings


Now that you know the importance of the Ruby implementation, you might wonder why you did not hear from it before? It’s because the Ruby default implementation is by far the most used one.


When people refer to “Ruby” they refer to the Ruby language + the Ruby default implementation 99% of the time. 


The first Ruby implementation of the Ruby programming language was conceived by the notorious Yukuhiro Matsumoto (aka Matz) in the 90s. It was called MRI, short for Matz’s Ruby Interpreter, since it had a big focus on the interpreter.


The default Ruby implementation had major changes with Ruby 1.9 (released in December 2007), it added YARV (Yet Another Ruby VM) virtual machine, which significantly improved the performance of Ruby programs compared to the previous version (but more on the Ruby VM in the next articles).


YARV was primarily conceived and developed by Koichi Sasada, therefore you might read the term “KRI” in someplace (Koichi’s Ruby Interpreter), in tribute to Koichi’s master work that drastically changed the Ruby internals.


The default Ruby implementation, MRI, is also known as CRuby because it is coded in C. So you could somehow say that when you run MRI Ruby code, you run C code behind (to be totally precise, you run machine code which is compiled C code, but more on that in the following articles).


Thats was a lot of acronyms, but you should now be better able to link the various terms together.



Other Ruby implementations (JRuby, TruffleRuby, Rubinius, …)


The default Ruby implementation, the one you probably use, is coded in C. But you could totally build a Ruby implementation in something other than C, or using another technology than YARV, it would be considered a Ruby implementation as long as it can pass RubySpec tests.


Alright, but why bother?


Well, the way the Ruby default implementation is coded has its pros and cons. As a developer you might want to use the Ruby language but for things that the main implementation might not be the best tool for.


As an example, the default Ruby implementation, MRI, might not be the most suited one for multithreaded or heavily concurrent applications. You can leverage other Ruby implementations for this kind of specific use cases.


Hum, multithreaded? Concurrent application? You are losing me…


Right, these will be concepts that I will develop in the next articles so hang tight. Let’s focus on other Ruby implementations for now. Below are the most used ones with a quick overview:

  • JRuby
  • This implementation of Ruby is coded in Java. It uses the Java Virtual Machine (JVM) instead of YARV. Therefore it is well suited if your program interacts a lot with the Java ecosystem due to the fact that … it can use any Java library
  • Wait, does this mean I can use Ruby to code an Android app, answer: yes, see Ruboto (https://github.com/ruboto/ruboto
  • Wait again, does this mean I can use Ruby to code Minecraft plugins, answer: yes, see Purugin (https://github.com/enebo/Purugin)
  • Github repository: https://github.com/jruby/jruby
  • TruffleRuby
  • This implementation is coded in C and is maintained by Oracle. It uses yet another VM called GraalVM. It is generally better than MRI for running Ruby code faster for CPU-intensive tasks, running code in parallel, and connecting with other languages like Java, JavaScript, Python and WebAssembly
  • Github repository: https://github.com/oracle/truffleruby
  • mruby
  • This one is coded in C also, and is designed to be a lightweight implementation of Ruby, making it a good fit for constrained devices such a small IoT devices
  • Fun fact 1: Matz is very active on this project
  • Fun fact 2: its development is sponsored by the Ministry of Economy, Trade and Industry of Japan
  • Github repository: https://github.com/mruby/mruby
  • Artichoke
  • This one is coded in Rust (❤️) and is not yet fully functional. It aims to provide a framework for building other Ruby implementations leveraging Rust.
  • Github repository: https://github.com/artichoke/artichoke



Using another Ruby implementation


To use another Ruby implementation, you should refer to their Github repository. But it’s quite easy most of the time. For example, you can use rbenv which supports:



Install the Ruby implementation, sets the ruby version of your project with rbenv local <ruby implementation version>, and you are set to go!


You can also use some of the major Ruby implementations with Rails, but you will have to do some work to make it work. Here are some documentation about it:


And if you are interested by a nice talk made by Charles Olivier Nutter, creator of JRuby, at the 2022 Rails Conf, go check this video: https://www.youtube.com/watch?v=3mH4OxFJa1c



Wrapping up


This article is the first stone of a larger article series about “low-level” computing concepts applied to Ruby. It was important to state that the Ruby language has several implementations, it already pictures some of the subjects this article series will talk about.


For the rest of the series, we will only discuss these “low-level” concepts for the default Ruby implementation, aka MRI, aka CRuby.


Can’t wait to share this series with you,


Best,

Mth0158





Resources used:



My last articles