We all know that Ruby is a great language to use for the backend of your web application, but did you know you can write Ruby code for the frontend as well?
Not only that, but there are two available options to choose from when looking to “transpile” from Ruby to Javascript. These are:
Let’s take a quick peek at each one and see what might be right for your project.
Ruby2JS
My personal favorite, Ruby2JS was created by Sam Ruby (yep, that’s his name), and it is intended to convert Ruby-like syntax to Javascript as cleanly and “natively” as possible. This means that (most of the time) you’ll get a line-by-line, 1:1 correlation between your source code and the JS output. For example:
class MyClass
def my_method(str)
ret = "Nice #{str} you got there!"
ret.upcase()
end
end
will get converted to:
class MyClass {
myMethod(str) {
let ret = `Nice ${str} you got there!`;
return ret.toUpperCase()
}
}
There’s actually a lot going on here so let me unpack it for you:
- Depending on how you configure Ruby2JS, you can convert classes to old-school JS functions/constructors, or you can use modern ES6+ classes like in the example here (which I recommend).
- Ruby2JS provides “filters” which you can apply selectively to your code to enable new functionality. In this example, the
camelCase
filter automatically converts typical Ruby snake_case to camelCase as is common in Javascript. Thefunctions
filter automatically converts many popular Ruby methods into JS counterparts (soupcase
becomestoUpperCase
). And thereturn
filter automatically add a return to the end of a method just like how Ruby works. - String interpolation in Ruby magically become valid ES6+ string interpolation, and it even works with squiggly heredocs!
How do you get started using Ruby2JS? It’s pretty simple: if you’re using a framework with Webpack support (Rails, Bridgetown), you can add the rb2js-loader plugin along with the ruby2js gem, write some frontend files with a .js.rb
extension, and import those right into your JS bundle. It even supports source maps right out of the box so if you have any errors, you can see the original Ruby source code right in your browser’s dev inspector!
Full disclosure: I recently joined the Ruby2JS team and built the Webpack loader, so let me know if you run into any issues and I’ll be glad to help!
Opal
The Opal project was founded by Adam Beynon in 2012 with the ambitious goal of implementing a nearly-full-featured Ruby runtime in Javascript, and since then it has grown to support an amazing number of projects, frameworks, and use cases.
There are plenty of scenarios where you can take pretty sophisticated Ruby code, port it over to Opal as-is, and it just compiles and runs either via Node or in the browser which is pretty impressive.
Because Opal implements a Ruby runtime in Javascript, it adds many additional methods to native JS objects (strings, integers, etc.) using a $
prefix for use within Opal code. Classes are also implemented via primitives defined within Opal’s runtime layer. All this means that the final JS output can sometimes look a little closer to bytecode than traditional JS scripts.
For instance, the above example compiled via Opal would result in:
/* Generated by Opal 1.0.3 */
(function(Opal) {
var self = Opal.top, $nesting = [], nil = Opal.nil, $$$ = Opal.const_get_qualified, $$ = Opal.const_get_relative, $breaker = Opal.breaker, $slice = Opal.slice, $klass = Opal.klass;
Opal.add_stubs(['$upcase']);
return (function($base, $super, $parent_nesting) {
var self = $klass($base, $super, 'MyClass');
var $nesting = [self].concat($parent_nesting), $MyClass_my_method$1;
return (Opal.def(self, '$my_method', $MyClass_my_method$1 = function $$my_method(str) {
var self = this, ret = nil;
ret = "" + "Nice " + (str) + " you got there!";
return ret.$upcase();
}, $MyClass_my_method$1.$$arity = 1), nil) && 'my_method'
})($nesting[0], null, $nesting)
})(Opal);
Thankfully, Opal too has support for source maps so you rarely need to look at anything like the above in day-to-day development—instead, your errors and debug output will reference clean Ruby source code in the dev inspector.
One of the more well-known frameworks using Opal is Hyperstack. Built on top of both Opal and React, Hyperstack lets you write “isomorphic” code that can run on both the server and the client, and you can reason about your web app using a well-defined component architecture and Ruby DSL.
Conclusion
As you look at the requirements for your project, you can decide whether Ruby2JS or Opal might suit your needs.
- If you use Webpack and already have a lot of JS code or libraries you need to interoperate with, Ruby2JS is a capable and lightweight solution which integrates easily into your build pipeline.
- If you’re starting from scratch and want all the power of a full Ruby runtime as well as opportunities to write isomorphic Ruby code, Opal might be just what the doctor ordered.
Regardless of which you choose, it’s exciting to know that we can apply our Ruby knowledge to the frontend as well as the backend for web applications large and small. It’s a great day to be a Rubyist.
“Ruby is simple in appearance, but is very complex inside, just like our human body.”
matz
Join 300 fullstack Ruby developers and subscribe to receive a timely tip you can apply directly to your Ruby site or application each week:
Banner image by Meritt Thomas on Unsplash
Other Recent Articles
Episode 9: Preact Signals and the Signalize Gem
What are signals? What is find-grained reactivity? Why is everyone talking about them on the frontend these days? And what, if anything, can we apply from our newfound knowledge of signals to backend programming?
Episode 8: Hotwiring Multi-Platform Rails Apps with Ayush Newatia
I’m very excited to have Ayush on the show today to talk about all things fullstack web dev, his new book The Rails & Hotwire Codex, and why “vanilla” is awesome!