Electron Adventures: Episode 93: Opal Ruby
Tags: javascript, ruby, electron, opal
Published: 2021-11-10 23:08:31

I tried it a few times before, and it was a bit too complicated, so I dropped it and did something else, but the series is nearing completion, so I can't postpone any longer.

Opal Ruby is complicated primarily because it's not meant to be used as JavaScript-like language, it's meant to be used as part of a bigger system, generally with a Ruby backend, so all that wiring is behind the scenes. But we'll use it directly.

For this episode, I'll only do the frontend (renderer) in Opal Ruby, and the backend (main) in JavaScript.

### `index.js`

The backend process will just open `public/index.html`:

```javascript
let { app, BrowserWindow } = require("electron")

function createWindow() {
  let win = new BrowserWindow({height: 600, width: 800})
  win.loadFile(`${__dirname}/public/index.html`)
}

app.on("ready", createWindow)

app.on("window-all-closed", () => {
  app.quit()
})
```

### `public/index.html`

For the app we'll just show a button, and a count of how many times it was clicked:

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Ruby Opal Application</title>
    <link href="app.css" rel="stylesheet" type="text/css" />
  </head>
  <body>
    <button>Click the button</button>
    <div>Click count: <span id="count">0</span></div>
    <script src="./build/app.js"></script>
  </body>
</html>
```

The compiled file will go to `./build/app.js`.

### `public/app.css`

We're doing basically `mode: dark; display: center;` except with a few more words:

```css
body {
  margin: 0;
  background-color: #444;
  color: #fff;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  font-size: 300%;
  min-height: 100vh;
}

button {
  font-size: unset;
}
```

### `Gemfile`

That covers the Electron part, so let's get to the Ruby part. `Gemfile` is like `package.json` - and there's `Gemfile.lock` corresponding to `package-lock.json`

```ruby
gem "opal", "~> 1.3"
```

### `Rakefile`

We need to tell Opal to build the `app.js`. There are likely some packages for watching the source folder and doing it automatically, but I decided to do it the hard way for now.

All this could also go into `package.json` `scripts` section.

```ruby
require "pathname"

desc "Build JavaScript files"
task "build" do
  Pathname("public/build").mkpath
  sh "opal src/app.rb -c >public/build/app.js"
end
```

### `src/app.rb`

And finally the app! Opal Ruby generally requires wrappers around JavaScript objects. There are a few ways to do this - `native` module provides `$$` which corresponds to JavaScript `window`/`global` except it wraps every JavaScript object in an Opal Ruby wrapper:

```ruby
require "native"

counter = 0

button = $$.document.querySelector("button")
count = $$.document.querySelector("#count")

button.addEventListener("click") do
  counter += 1
  count.innerText = counter
end
```

### Results

Here's the results:

![Episode 93 Screenshot](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/srttqp1y9bxlm9u3byuz.png)

In the next episode we'll write the terminal app in Opal Ruby.

As usual, [all the code for the episode is here](https://github.com/taw/electron-adventures/tree/master/episode-93-opal-ruby).