Tag: opal

In this post we’ll learn how Ruby objects are mapped in JavaScript-land by the Opal compiler, how to call methods on them and how object instantiation works for both Ruby and JavaScript.

The basics: constants, methods and instance variables

The rule by which Ruby objects are compiled into JavaScript by Opal is quite simple. Constants are registered with their regular name under the window.Opal (i.e. the Opal property on the JavaScript window object). Methods are mapped to properties prefixed by a $ (dollar sign). Instance variables are just regular properties.

As a Ruby developer you may be surprised that obj.baz returns undefined and not nil (actually window.Opal.nil) as that’s the value that you would expect to find while reading an instance variable for the first time.

What happens is that the Opal compiler will do just that, as long as you access those instance variables from Ruby code. In fact it statically analyzes the class’ code and pre-initializes to nil any instance variable for which it can find a reference.

Now that we have the basics let’s go further and put Foo.new under the microscope for both Opal and CRuby.

The birth of an object in 3 steps

By looking in detail how object instantiation is done in JavaScript by the Opal compiled code we’ll have a chance to learn how it actually works in CRuby.

The implementation of .new

At the cost of oversimplifying here’s the rough implementation of the #new method available to all classes in Ruby:

As you can see the the only notable difference is in how the block is passed. Opal will store any block call on the method itself under the $$p property. This way blocks can’t be confused with regular arguments. Apart from that it’s clear that it’s fundamentally the same code.

Uncovering Class#allocate

The curious reader at this point is wondering what the allocate method does in JavaScript because surely it can’t manage memory. Let’s see the implementation:

Step 2: Adding behavior

Now that we have a place let’s add some behavior. For example we want to clear the field if the ESC key is pressed and to block the submission if the field is empty. We need to concentrate on the #setup method.

As you may have noted we’re memoizing #input and we’re searching the element inside our current HTML subtree. The latter is quite important, especially if you’re coming from jQuery and used to $-search everything every time. Sticking to this convention will avoid down the road those nasty bugs caused by selector ambiguities.

feature 2: “prevent submit if the field’s empty”

class SearchBar
# …
def setup
element.on :keypress do |event|
clear if event.key_code == ESC_KEY
end
form.on :submit do |event|
event.prevent_default if input.value.empty?
end
end
private
def form
@form ||= element.find('form')
end
end

YAY! Sounds like we’re almost done!

Step 3: Extracting the View class

Now seems a good time to extract our view layer, thus leaving the SearchBar class with just the business logic:

Step 4: Topping with some “Usability”

Now that the View class has come to life we can add some sugar to make out lives easier.

Default Selector

We already know that the class will always stick to some specific HTML and its selector, it’s a good thing then to have some sensible defaults while still allowing to customize. We’ll define a default selector at the class definition, this way:

While there we also added a check on element existence and a list of active instances so that we can play nice with JS garbage collection and instantiate the class even if the HTML it needs is missing (e.g. we’re on a different page).

Conclusion

Hope you enjoyed and maybe learned a couple of things about Opal and opal-jquery, below are some links for you:

Forewords

In the last article we explored the internals of D3.js, the data visualization library.
In doing that we ended up with a number of files in a gist.

This morning I thought it’d be nice to put up the thing into one of those JavaScript fiddle sites, I looked up a bunch of them: CodePen, JSFiddle, JS Bin but none of them allowed for arbitrary extensions or loading a code from a Gist1.

A couple of thing we need to bear in mind are these: D3 is not object-oriented and it’s a pretty complex library. We’re expecting some problems.

To have something to compare it to we may note, for example, that JQuery is basically a class ($) that exposes methods that returns other instances of the same class. That makes things easy enough when we try to use it from an OO language like Ruby.

And last we have opal-parser.js and app.rb (that is declared as type="text/ruby"). The parser will look for all script tags marked as text/ruby and will load, parse and run them.

In additon we’re also providing a data.tsv file as described in the tutorial.

Part II — Code Ungarbling

About the process of translating

The approach I used to translate this code is quite simple, I copy/pasted all the JavaScript from the tutorial into app.rb and commented it out. Then I translated one line (or sentence) at a time tackling errors as they came.

Using Native

The first thing we need to do is to wrap the d3 global object with Native, so that is ready for Ruby consumption.

By trying to run this code we discover early on that somehow Native is failing to deliver calls via method missing.

The reason is that bridged classes are a kind of their own and turns out that D3 tends to return augmented anonymous functions and arrays. Now both Array and Proc are bridged classes. That means that they are the same as their JS counterparts: Array and Function.

Native will have no effect on bridged classes (no wrapping) and therefore all calls to properties added by D3 will end in calls on undefined.

To solve this we’ll manually expose the methods on those classes, the final code looks like this: