Open Source Adventures: Episode 61: How Opal Ruby represents classes
Tags: javascript, ruby
Published: 2022-05-23 19:13:18

In the previous episode we checked how Opal Ruby represents basic data types, let's now define some classes and see what they compile to.

### Simple Person class

Let's start with a very simple class:

```ruby
class Person
  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end

  def to_s
    "#{@first_name} #{@last_name}"
  end
end

person = Person.new("Alice", "Ruby")
puts "Hello, #{person}!"
nil
```

With wrappers, it gets compiled to this monstrosity:

```javascript
Opal.queue(function(Opal) {/* Generated by Opal 1.5.0 */
  var self = Opal.top, $nesting = [], $$ = Opal.$r($nesting), nil = Opal.nil, $klass = Opal.klass, $def = Opal.def, person = nil;

  Opal.add_stubs('new,puts');

  (function($base, $super) {
    var self = $klass($base, $super, 'Person');

    var $proto = self.$$prototype;

    $proto.first_name = $proto.last_name = nil;


    $def(self, '$initialize', function $$initialize(first_name, last_name) {
      var self = this;


      self.first_name = first_name;
      return (self.last_name = last_name);
    }, 2);
    return $def(self, '$to_s', function $$to_s() {
      var self = this;

      return "" + (self.first_name) + " " + (self.last_name)
    }, 0);
  })($nesting[0], null);
  person = $$('Person').$new("Alice", "Ruby");
  self.$puts("Hello, " + (person) + "!");
  return nil;
});
```

Let's try to unpack some things:

* `$$('Person')` does constant lookup.
* all methods are defined with `$` prefix to reduce conflicts with JavaScript, so we have `.$initialize`, `.$to_s`, `.$new`, `.$puts` and so on
* instance variables are surprisingly mapped directly, so `first_name` instance variable is just `.first_name`
* `this` is assigned to local variable `self` when method is entered, as in pre-ES6 JavaScript `this` would get changed a lot. This technique was actually used a lot by hand-written pre-ES6 JavaScript code as well. It's rare nowadays.

### Inheritance

Let's try to inherit from `Person`:

```ruby
class Person
  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end

  def to_s
    "#{@first_name} #{@last_name}"
  end
end

class Cat < Person
  def to_s
    "Your Majesty, Princess #{super}"
  end
end

cat = Cat.new("Catherine", "Whiskers")
puts "Hello, #{cat}!"
nil
```

This is not going to be very readable:

```javascript
Opal.queue(function(Opal) {/* Generated by Opal 1.5.0 */
  var self = Opal.top, $nesting = [], $$ = Opal.$r($nesting), nil = Opal.nil, $klass = Opal.klass, $def = Opal.def, $send2 = Opal.send2, $find_super = Opal.find_super, cat = nil;

  Opal.add_stubs('new,puts');

  (function($base, $super) {
    var self = $klass($base, $super, 'Person');

    var $proto = self.$$prototype;

    $proto.first_name = $proto.last_name = nil;


    $def(self, '$initialize', function $$initialize(first_name, last_name) {
      var self = this;


      self.first_name = first_name;
      return (self.last_name = last_name);
    }, 2);
    return $def(self, '$to_s', function $$to_s() {
      var self = this;

      return "" + (self.first_name) + " " + (self.last_name)
    }, 0);
  })($nesting[0], null);
  (function($base, $super) {
    var self = $klass($base, $super, 'Cat');


    return $def(self, '$to_s', function $$to_s() {
      var $yield = $$to_s.$$p || nil, self = this;

      delete $$to_s.$$p;
      return "Your Majesty, Princess " + ($send2(self, $find_super(self, 'to_s', $$to_s, false, true), 'to_s', [], $yield))
    }, 0)
  })($nesting[0], $$('Person'));
  cat = $$('Cat').$new("Catherine", "Whiskers");
  self.$puts("Hello, " + (cat) + "!");
  return nil;
});
```

As you can see the generated code is completely unreadable, and barely uses any JavaScript OOP features. Which makes sense as pre-ES6 JavaScript OOP was hell, and that's what Opal targets. I'm not sure if targetting ES6+ would improve things much.

If you look closely, you can see some support for `yield` even though we're not using it for our case.

### Story so far

[All the code is on GitHub](https://github.com/taw/open-source-adventures).

### Coming next

In the next episode we'll see how Ruby2JS compares to Opal Ruby.