Introduction

Just as there are many shades of the ruby gem, there are multiple implementations of the Ruby interpreter.

The most commonly used Ruby interpreter is the reference implementation, Ruby MRI, developed in C by the creator of Ruby (Yukihiro Matsumoto) and the Ruby core team.

Our Ruby on Rails Hiring Guide mentions that some of the drawbacks in Rails can potentially be solved or avoided by using an alternate Ruby interpreter. This article showcases the various existing Ruby interpreter implementations and runtimes available today, discussing the advantages and disadvantages of each.

Ruby Version History (and how it affects alternative implementations)

Sadly, there is no equivalent of the Python Language Reference for Ruby (ISO/IEC 30170:2012 describes Ruby 1.8 / Ruby 1.9, but no corresponding spec exists for Ruby 2.x). In the absence of any such language specification, Ruby implementors typically rely instead on the community-driven RubySpec that specifies expected behaviors of the Ruby language through tests that can be run in any Ruby interpreter. RubySpec is therefore used by Ruby implementors to verify the behavioral compliance of their Ruby implementations with the de facto standard.

Due to the lack of a formal specification, new releases of Ruby often simply correspond to new releases of the Ruby MRI. It’s worth noting that there is an open issue that discusses a design process for decoupling Ruby (the language) from the Ruby MRI.

Given the current tight coupling, though, between the Ruby language and the MRI reference implementation, developers of alternate Ruby implementations sometimes struggle to keep up with the language changes introduced in each new MRI release.

Never was this harder than in the transition between Ruby 1.8 and Ruby 1.9. In 2007, in an effort to clean up and consolidate Ruby’s syntax (as the language had evolved in the decade since Ruby 1.0’s release), the Ruby core team released Ruby 1.9.0, a version which introduced many backward incompatibilities into the language. As a result, not all Ruby implementations invested the effort necessary to make the syntactical jump from 1.8 to 1.9. As such, there are several 1.8-based Ruby implementations that aren’t used by the community anymore, but which you may still find online or talked about by old Ruby hands.

A new version of Ruby MRI is released every Christmas, following a semantic versioning principle. Ruby 2.0 (released 2013) and 2.1 (released 2014) each introduced additional language features that Ruby developers can take advantage of, without forfeiting backward compatibility with Ruby 1.9.

Why use an alternative Ruby implementation? What’s wrong with MRI?

There are a variety of alternative Ruby implementations, supporting a wide array of use cases and environments. Java Enterprise environments. Mobile applications. JavaScript implementations. Low CPU/RAM machines. In addition to supporting these use cases, alternative implementations can sometimes also offer an extra boost in speed or more efficient memory utilization, depending on the characteristics of your application.

For a long time many Ruby on Rails developers used Ruby Enterprise Edition (REE) instead of MRI, taking advantage of the better memory management techniques in REE as compared to the MRI version at that time. (REE was subsequently discontinued though in 2012.)

While MRI is the default Ruby implementation, it’s not the necessarily the correct choice for all environments and scenarios. For example, MRI’s concurrency support is inferior to that of JRuby or Rubinius. Also, although MRI’s memory and garbage collection schemes are constantly improving, they still do have some issues.

The survey of Ruby implementations that follows is intended to assist you in selecting the interpreter best suited to your project’s operational goals and constraints.

Matz’s Ruby Interpreter (MRI) / CRuby

Written in C by the Ruby core team lead by Yukihiro Matsumoto (“Matz”, the creator of Ruby), MRI is the reference implementation of Ruby which serves as the de facto standard. If an OS vendor includes a version of Ruby as part of the OS installed software, for example, it’s usually the MRI version. MRI benefits from more paid core team members than any other Ruby implementation, as well as contributed resources dedicated from people or companies that want to improve the Ruby ecosystem.

A new version of Ruby MRI – often implementing new language features, in addition to standard library changes – is released every Christmas. Features are implemented in Ruby MRI first, usually based on discussions on the Ruby core developer mailing list. Other Ruby implementations lag behind, in some cases even by years.

JRuby

JRuby is a version of Ruby implemented on top of the Java Virtual Machine (JVM). As it becomes popular for languages beyond just Java to run on top of the JVM (I’m looking in your direction, Clojure and Scala), a JVM-based Ruby implementation is likely to gain in popularity.

Ruby in the JVM also means that Ruby can run anywhere Java can run (like Android phones, using Ruboto for example). Additionally, thanks to the interoperability of the JVM, JRuby code can make use of the Java platform, including both standard and 3rd party libraries.

JRuby is also useful for bringing a Rails-based solution into a Java-only deployment environment, packaging up the Rails app as a .war file to deploy to a Tomcat container, or as a Java applet running as part of your web front-end, for example.

For those not accustomed to the JVM, though, JRuby brings standard JVM-related issues such as slow startup of the Ruby interpreter, debugging CLASSPATH issues if you’re using 3rd party Java libraries, larger memory usage, and the fact that now your code needs to be written with thread safety considerations in mind.

Also, some features of Ruby (C APIs, and one of Ruby’s powerful introspection tools, the ObjectSpace module) are not implemented in JRuby.

All that said, the advantages of using the JVM may outweigh the disadvantages for certain situations or projects. The JVM allows for many performance optimizations, such as turning on the JIT compiler, or using native Java objects and APIs.

As an example of a compelling JRuby use case, a former co-worker of mine once had a CPU-intensive problem that he initially solved with threads in Ruby 1.9.3. When he switched to JRuby and used Java’s java.util.concurrent.Executors , he saw a performance improvement of multiple orders of magnitude (tens of thousands of times faster) for this operation. See his experiment here.

Rubinius

Rubinius is an implementation of Ruby that implements a generic runtime for dynamic languages on top of a Low Level Virtual Machine (LLVM). Using this infrastructure and JIT compiler technology, Rubinius can often run Ruby code with less overhead than MRI.

Rubinius is also built using as much Ruby as possible to make development of the interpreter / runtime faster and easier.

Fun fact: RubySpec initially came into being in the process of implementing Rubinius.

Like JRuby, Rubinius includes a JIT compiler, better memory management, and a more mature virtual machine than Ruby MRI. However, unlike JRuby, Rubinius supports Ruby C libraries and the underpinnings of Rubinius are written in C++, not Java.

Rubinius may be a good middle ground when you need high performance on your Rails servers without the learning curve or other disadvantages of JRuby.

mruby

mruby is designed to be an embeddable version of Ruby (supporting Ruby 1.9.3). With mruby, you can offer Ruby as a scripting / automation language in native applications, use it for game scripting, and even for programming microcontroller boards like the Raspberry Pi.

If your platform has severe resource constraints, mruby may be just the Ruby interpreter for you. mruby is also being used to:

Build iOS apps (as a competitor to RubyMotion, discussed below)

Embed Ruby into iOS apps, for development speed

Offer end users an embedded scripting language for automation purposes

With the Internet of Things becoming more and more of a reality, home automation coming into its own, and extremely portable (and relatively powerful) computers being more commonplace, the landscape of target platforms to support is becoming increasingly diverse. mruby helps make it possible to do so with the same productive language one would use on the desktop.

Opal

Opal is a transpiler to turn Ruby into JavaScript.

With the rise of Coffeescript, developers are learning they don’t have to type JavaScript to get JavaScript. While Coffeescript admittedly has its advantages, use it long enough and you’re bound to run into things you don’t like about the language.

Enter Opal: Type Ruby, get Javascript. Pretty cool.

Opal strives to be as consistent as possible with other Ruby implementations and is therefore also tested against a subset of RubySpec. Some incompatibilities do exist, though, stemming from the nature of JavaScript and JavaScript runtimes. For example, strings and symbols in Opal are equal, and Opal does not provide any threading or shell execution mechanisms.

Opal runs standalone or can be used as part of the Rails asset pipeline (e.g., to automatically transpile your somefile.js.rb file into JavaScript).

Maybe you have a problem domain well-suited for JavaScript’s asynchronous concurrency pattern (such as a small Node.js service) but want the language or certain gems from the Ruby space. Opal may be a good solution for you in that case.

Or perhaps you want to write a full stack Ruby web app. With Opal, you can. Have one Ruby interpreter running your server-side Ruby code and then have Opal generating JavaScript to run on the client side.

Opal recognizes that you’ll likely be interacting with other JavaScript APIs (the DOM, or Node.js for example). It therefore makes it easy to transition into JavaScript and provides some Ruby syntactical sugar over common JavaScript libraries like jQuery.

Opal’s JavaScript-centric nature, though, is both its strength and its weakness. On the downside, Opal’s runtime is the JavaScript runtime, and Opal is informed by JavaScript design decisions. So if you’re looking for a good implementation of Ruby to write a small shell script with, or looking for a better Ruby runtime for your Rails app, Opal is probably not your best choice.

RubyMotion

RubyMotion is both (a) a Ruby implementation (written using Objective-C and Cocoa) and (b) a set of language bindings so developers can access Cocoa APIs through Ruby.

RubyMotion is a commercial product that enables you to write Cocoa native apps in Ruby. RubyMotion 2.0 allows you to write iOS and Mac OS X apps in Ruby, and RubyMotion 3 promises to bring this same support to Android.

RubyMotion implements version 1.9 of the Ruby language.

Defunct Implementations

Over the years since Ruby was first introduced, some of the Ruby implementations that have come into being have been abandoned or discontinued, such as:

Ruby Enterprise Edition (REE). REE was a fork of MRI 1.8 from the folks at Phusion Passenger that implemented many memory and garbage collection improvements for web developers. For several years, it was the default Ruby implementation deployed for production Rails sites. It was never updated for Ruby 1.9 or Ruby 2.0, though, and was ultimately discontinued in 2012.

REE was a fork of MRI 1.8 from the folks at Phusion Passenger that implemented many memory and garbage collection improvements for web developers. For several years, it was the default Ruby implementation deployed for production Rails sites. It was never updated for Ruby 1.9 or Ruby 2.0, though, and was ultimately discontinued in 2012. IronRuby. IronRuby is Ruby implemented on top of Microsoft .NET, written in C#, and for a while the project was funded by Microsoft. Abandoned in 2011, IronRuby last supported Ruby 1.8.6.

Wrap-up

There are a wide variety of runtimes and interpreters to pick from on the Ruby landscape. For most Ruby projects, the Ruby reference implementation (Ruby MRI) remains the interpreter of choice. However, alternate Ruby implementations may very well be the right choice for your project, depending on your functional and technical goals and constraints.

In its role as the reference implementation of Ruby, MRI gets new language features faster, has good enough concurrency and memory stories (that are only getting better), and has the widest compatibility with gems (some partially written in C). All in all, MRI is a solid, dependable choice for general purpose Ruby code.

For larger, enterprise deployments, or for situations where you either need to interact with Java code (or other JVM languages) or need highly evolved concurrency patterns, JRuby is a compelling option.

And of course, if you have unique needs (e.g., writing JavaScript, running on the current generation of embedded devices, and so on), the other Ruby alternatives may be just what you’re looking for.

With a wide variety of Ruby runtimes and interpreters to pick from, Ruby shows itself to be a flexible language, useful for a wide array of computing environments, ranging from a corporate big iron Java deployment shop, to software that controls that stoplight in your office you hooked your Raspberry Pi into last weekend. Picking the right tool for the right purpose is essential, yes, but hopefully this article has shown you that Ruby is so much more than the default Ruby interpreter that came with your OS.

The world of Ruby is greatly enhanced by alternative Ruby implementation teams working with the core Ruby MRI team as changes to the language are proposed. They add diversity to the Ruby implementation community, adding their hard won Ruby implementation experiences and their own perspectives on features going into the language. Ruby enthusiasts collectively owe these teams a great debt of gratitude. Kudos to them for their efforts!