This post is created for those front-end developers who knows ruby and might want to try using it for front-end development.
JavaScript is a great language, I love it, I always have been, but it has it's problems:
- Not well designed - As many of you probably know the first version designed within a day that might be the main reason why is it not so understandable.
- No module system - This is the main problem with almost every major Library / Framework currently out there. As it seems this problem will persist in the future as well, as long as the new generation IE X will appears on the market - this would be the future version, which support JS modules.
- Gothas - I think this is pretty much covers all other aspects. These are the parts of the language what surely cause you headaches for a while without the proper understanding of how it works (scope, binding, prototype, etc..)
What I want
I'm going to describe some features what I would like to use in front-end world:
- The most important thing I want is to create Custom Elements just with JavaScript by extending native classes. So what this means is that you could extend the select element and create a new class that works and behaves like a select element but with a different tagname:
class MySelect extends HTMLSelectElement {
constructor(){
super
this.addEventListener('change',this.onChange.bind(this))
}
onChange(){
alert(this.value)
}
}
mySelectInstance = new MySelect
document.body.appendChild(mySelectInstance)
// <my-select></my-select>
This would be awesome, but using in real life seems impossible. Implementation would take ages even if there be a draft created by the W3C to speed up the process, the older versions still need to be supported. So in one expression: total nightmare.
- Manage dependencies the way it makes sense. Sprockets / Mincer provides this in the form of require keywords at the top of the file. Basically it will produce one file with dependencies loaded in order, also could minify code.
- Getters and Setters as default. Sure you can do this with JavaScript but there are no conventions that specify it's use, also you can't declare getters and setters for classes.
As I'm constantly thinking of these features I'm also looking for a solution for a long time. Finally, I found something, so keep reading if you still interested.
Opal
About a year ago I found Opal. This is a Ruby to JavaScript compiler, and because I learned Ruby and I love the language I felt like "Yeeey! This is Awesome!" . Then I tried it and realised it wasn't working properly. There were problems with scoping and language features, there was no require and stuff, it wasn't ready at all. When I checked the site again a few months later, I found the product in more matured shape with the functions what make it more useable and effective:
- Ruby in the Browser
- It has Sprockets support (Yey!), and it uses require statements (2. featured solved).
- It has RSpec support, which makes unit testing a breeze.
- You can load any third party gems easily.
- Have most of Corelib and some of Stdlib.
- Fits in with Ruby development on the back end via Rack.
How it is done?
Easy DOM
Opal has no DOM bindings, because it's just a compiler, but it is easy to wrap any Native JavaScript object to a variable and thus a class instance with the backticks evaulation:
class Node
attr_reader :node
def initialize(node)
@node = node
end
def appendChild(otherNode)
`@node.appendChild(#{otherNode.node})`
end
end
body = Node.new `document.body`
This snippet defines a Node class which sets the instance variable node to the argument what is passed on initialise. The last line will set the body variable an instance of the Node class with the `document.body` as the node. We would need to do this as a library, and then it we should have easy DOM bindings.
Ruby features
So what we did is that we wrapped a Native Node into a Ruby Class, cool right? Yes but we can take this further: we can use Ruby to define the `<<` operator that will append the given node the the current node.
def appendChild(otherNode)
...
end
alias :<<, :appendChild
Well that was easy :). At the end what I love about Ruby, I can now use at the front end, some examples:
- Can use question marked methods. `body.empty?`
- attr_accessor, alias and such
- DSLs
- Module and Class inheritance
- Getters and Setters
- much more
Custom Components
Remember what the first feature was that I wanted? Custom Components.
Now with this wrapping system it's realtivly easy:
# We extend the Node class we created before
class Component < Node
def initialize
# we are not calling super but are getting the class name
tagName = self.class.name.split("::").pop
# then we create the Native Node
@node = `document.createElement(#{tagName})`
end
end
# now we can extend that class to create a custom element
class MyElement < Component
# and define custom logic
def select
`@node.addClass('selected')`
end
end
# and when you create a MyElement
myEl = MyElement.new
body << myEl
# <body><myelement></myelement></body>
myEl.select
If we work on this some more we can add a DSL to this and create a Library / Framework / System that in the end create components with inherintence, thus solving my first feature.
Does it worth it?
The main question is that is this worth it in the end? Well here are some pros and cons.
Pros:
- You can use Ruby in the browser instead of JavaScript
- You can manage your dependencies
- You can use ruby only gems
- There is a standard for building your code (Sprockets)
- There is a standard for testing your code (RSpec, Cucumber)
Cons:
- Bigger File size because of the Runtime dependency (44kb gzipped)
- Have to wrap external Libraries
- Harder to find Ruby developers than JavaScript developers
I think it is worth it if you know Ruby, and it's absolutely worth it if you are a Rails developer, but it's up to you in the end. I'll definatly try to use this whenever I can.
What's next?
At the beginning of this article I have three concerns mentioned related to front-end development: Custom Components, Module System, Getters and Setters. All of these can be solved based on what I wrote.
The next step for me is to write a Gem that contains wrapper for the most commonly used front end classes: DOM, LocalStorage, Requests and Websockets, custom components and a basic MVC architecture.