Opal on Rails: Replacing CoffeeScript with client-side Ruby
CoffeeScript is one of the best things that happened to JavaScript. With its clear, easy to read, semicolon-less syntax, it feels almost like Ruby. Almost…
There has been a lot of discussion about isomorphic app development, where both server and client-side parts are written in the same language. One of the most mature and popular attempts to enable isomorphic development in Ruby is Opal.
In this article I’ll show you how easy it is to start writing your client-side scripts in Ruby. Right here, right now, with no excuses! :)
Merging two worlds with opal-rails
First, let’s install opal-rails
gem:
$ gem install opal-rails
This gem enables us to generate new Rails application with Opal support (note the --javascript=opal
option):
$ rails new opal-on-rails --javascript=opal
When bundle is complete we’re ready to start. Let’s create a controller for manipulating Colors:
$ cd opal-on-rails
$ rails g controller Colors
Then add it to config/routes.rb
as a root:
Rails.application.routes.draw do
root 'colors#index'
end
Now it’s time for some HTML. Create a view app/views/colors/index.erb
:
<h1>Hello Opal</h1>
<p>This is simple Opal application</p>
Launch the server and open http://localhost:3000 in your browser.
$ rails s
First encounter
Until now we saw nothing fancy, nothing more than some standard Rails bootstrap app. But take a look at what the controller generator created in app/assets/javascripts/colors_view.js.rb
!
class ColorsView
def initialize(selector = 'body.controller-colors', parent = Element)
@element = parent.find(selector)
setup
end
attr_reader :element
def setup
# Put here the setup for the view behavior
say_hello_when_a_link_is_clicked
end
def say_hello_when_a_link_is_clicked
all_links.on :click do |event|
# Use prevent_default to stop default behavior (as you would do in jQuery)
# event.prevent_default
puts "Hello! (You just clicked on a link: #{event.current_target.text})"
end
end
private
def all_links
@all_links ||= element.find('a')
end
end
It’s a nice piece of pure Ruby code that logs all clicked links to the browser console. Yup, a real Ruby script that works in a browser!
To see what Opal will generate from this Ruby code, checkout Try Opal online Opal-JS compiler.
To make the script work, we need to edit our template. Open app/views/layouts/application.html.erb
, find body tag and edit it:
<body class="controller-<%= controller_name %>">
We also want to initialize ColorsView
when the document is loaded. Remember jQuery? rails-opal
supports it. At the end of the file just add:
Document.ready? do
ColorsView.new
end
Looks familiar, right?
The last thing is to add some links to our template, and we’re ready to test the script. Edit app/views/colors/index.erb
:
<a href="#">Click me!</a>
Does it work? Of course it does. It’s Ruby :) After clicking on a link we’ll see our JavaScript console flooded with Hello! (You just clicked on a link: Click me!)
messages. Perfect.
Digging deeper
Want more? Let’s create some fancier client-side Ruby thing: add links that will change the header color on click.
Add links section to the template:
<section>
<h3>Change header color to:</h3>
<ul>
<li><a href="#" data-color="red">red</a></li>
<li><a href="#" data-color="green">green</a></li>
<li><a href="#" data-color="blue">blue</a></li>
</ul>
</section>
Create another method in ColorsView
:
def change_header_color_when_a_link_is_clicked
all_links.on :click do |event|
event.prevent_default
Element['h1'].css 'color', event.current_target.data('color')
end
end
Change setup method:
def setup
# Put here the setup for the view behavior
# say_hello_when_a_link_is_clicked
change_header_color_when_a_link_is_clicked
end
And we’re done. Refresh the browser and have fun playing with your isomorphic Ruby app :)
Any color you like, as long as it’s ruby
Opal is a fully featured Ruby implementation, including both corelib and stdlib. You have your much loved blocks, iterators, even method_missing
is here! The more I use it, the more I love it. It’s like discovering Ruby again!
The project itself is moving forward pretty fast. The codebase is tested against RubySpec to make Opal as compatible with MRI as possible. There are still some bugs and glitches, but still it’s worth the try. Version 0.8 is around the corner, with working source maps and lots of bug fixes.
What about support for native JavaScript libraries? Well, it’s not that simple like with CoffeeScript. You have to write a wrapper or include pure JavaScript code within backticks (just like executing shell commands
in Ruby), although most popular jQuery code works out of the box.
How do you like the idea of writing client-side Ruby? Do you think Opal can replace CoffeeScript? Should it?