DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on

4 3

Open Source Adventures: Episode 61: How Opal Ruby represents classes

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:

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
Enter fullscreen mode Exit fullscreen mode

With wrappers, it gets compiled to this monstrosity:

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;
});
Enter fullscreen mode Exit fullscreen mode

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:

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
Enter fullscreen mode Exit fullscreen mode

This is not going to be very readable:

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;
});
Enter fullscreen mode Exit fullscreen mode

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.

Coming next

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

Image of AssemblyAI tool

Transforming Interviews into Publishable Stories with AssemblyAI

Insightview is a modern web application that streamlines the interview workflow for journalists. By leveraging AssemblyAI's LeMUR and Universal-2 technology, it transforms raw interview recordings into structured, actionable content, dramatically reducing the time from recording to publication.

Key Features:
πŸŽ₯ Audio/video file upload with real-time preview
πŸ—£οΈ Advanced transcription with speaker identification
⭐ Automatic highlight extraction of key moments
✍️ AI-powered article draft generation
πŸ“€ Export interview's subtitles in VTT format

Read full post

Top comments (0)

Image of Docusign

πŸ› οΈ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

πŸ‘‹ Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay