hayley 365

home

hello world with Opal Ruby and Middleman

20 Jan 2014

2014-01-20 08:48:56 -0600

I had a simple goal today: figure out how to integrate Opal with Middleman.

See, I've previously played with things like Dart, but I never moved past playing with it in the Dart Editor and actually integrating it into a project.

So I knew it was important that I get off on the right foot with Opal by figuring that out first.

building a sample Middleman project

Easy peasy.

gem install middleman
cd /wherever_you_want
middleman new test_app

I tend to build a lot of stuff in /tmp when I'm just futzing about because I know it will get cleared out on reboot (I can always move it somewhere else if I decide to keep it).

how to add Opal to your Middleman project

No lie. I totally stole this from the [opalrb.org github repo][opalrb-ruby]. But I wanted to understand what was actually required. And of course, I presumed that I would forget a step in the future, so I needed to have at least some clue as to where to look.

tl;dr

Add this to your Gemfile: gem 'opal' gem 'opal-sprockets'

Add 2 scripts to ./source/javascripts/.

hello_world.rb:

require 'opal'

puts "hello world"

hw.js:

//= require "hello_world"

Add this to your config.rb:

after_configuration do
  Opal.paths.each do |p|
    sprockets.append_path p
  end
end    

But if you want more of a walkthrough, including the errors you might see if you forget a step, well then read on.

create a test Opal script file

Spoiler alert: this thing is broken, but that's so you'll see what happens when you forget something.

Create a hello_world.rb in ./source/javascripts/ with these contents:

puts "hello world"

Now, create a file that will be used by Sprockets. Call it hw.js or whatever you care to. The only caveat appears to be that it can't share the same name as the rb script (e.g. hello_world.rb and hello_world.js).

In this file, put this:

//= require "hello_world"

Now with the middleman server running (bundle exec middleman), try to load http://localhost:4567/javascripts/hw.js.

And here's what you'll probably see:

throw Error("Sprockets::FileNotFound: couldn't find file 'hello_world'\n (in /private/tmp/test_app/source/javascripts/hw.js:1)")

If you were like me, you'll next go to your Gemfile and trying adding this:

gem 'opal'

Make sure you run bundle after any Gemfile changes.

It won't work though.

Add one more:

gem 'opal'
gem 'opal-sprockets'

Et voilĂ . Now when you load the hw.js file in your browser, you should see what looks like Opal generated code.

So now let's see what happens when we add it to a web page.

If you're using the basic Middleman project, you won't even need to add it to a webpage because it will already be included in the all.js generated by sprockets.

Otherwise, you can add something similar to whichever HTML file you want it to show up on:

<script src="/javascripts/hw.js"></script>

But we'll assume you just went with the default index.html.erb from the default Middleman project.

So now reload your browser on http://localhost:4567.

And?

Nothing happens.

Open the JavaScript console. Oh, an error:

Uncaught ReferenceError: Opal is not defined

So you add this to the top of your hello_world.rb script:

require 'opal'

And now there's a new error in the hw.js file. Lovely:

throw Error("Sprockets::FileNotFound: couldn't find file 'opal'\n (in /private/tmp/test_app/source/javascripts/hello_world.rb)")

And here's where I would've been absolutely lost if not for having the github repo to cheat off of.

Add this to your config.rb file (frankly, I have no idea if its location matters):

after_configuration do
  Opal.paths.each do |p|
    sprockets.append_path p
  end
end    

Now, finally, when you reload the index in your browser and open up your JS console, you'll see that lovely:

hello world

some thoughts

Now, at this point, you might be tempted to find out just how much actual JS code was generated to get to this point. I know I was.

For me, the simplest example still resulted in a file that was 327k. It wasn't minified though, so obviously that would help somewhat.

I'm still intrigued by the idea of tree shaking. Dart has it. I think ClojureScript has it as well. In those cases, it only gives you the code you actually use. So you can feel free to import some big ole library and use a single function in it. No worries. Tree shaking will take care of getting rid of all of the unused code.

For me personally though, the overload of having these extra library files may be worth because it may well be the difference betwen having an app written with Opal versus an app that I never write at all.