No more HTML ?
Simplifying webdevelopment with Ferro framework
Since the dawn of the world wide web, web developers have been writing HTML. Later to be followed by writing CSS and Javascript. Next we needed Javascript libraries and frameworks to interact with our HTML.
But no more! It is easily possible to create websites and -applications without using HTML but using Javascript. Next, we can replace Javascript with Ruby. Combine Ruby with a small library called Ferro and we can transform web development. Web developers can focus all their attention to the structure of the code. No more distractions like HTML, naming elements and searching for elements. Just beautiful and simple Ruby code.
The interactive version of this article can be found here. It is uses Ferro and has working versions of example code.
An example
Imagine a webpage that displays a form with a checkbox and an input field. The input should only be enabled when the checkbox is checked. This kind of logic is best handled by the webbrowser, not the webserver. So we would write or generate an HTML page with the two inputs and some javascript. The javascript runs when the page is loaded, it runs a (jQuery) selector to find the checkbox and adds an eventlistener to the click event. When the checkbox is clicked the javascript event-handler function is executed. This runs a selector to find the input field and modifies its disabled attribute. Ironically we need some code to look for the elements that we know are there. We just created them in html!
If we look at this example from a functional perspective we only need two things: (1) a form with a checkbox and an input and (2) an action that should run when the checkbox value changes. Wouldn’t it be nice if we could translate these functional requirements into some Ruby classes and be done. Well, and this should come as no surprise, we can!
Opal-Ferro gem
Let me introduce the Opal-Ferro gem. Opal is that wonderful piece of kit that allows us to run Ruby in the webbrowser. Ferro is a small Ruby library that manages the webbrowsers DOM, erradicates the need for searching for elements and introduces some handy naming conventions to simplify CSS design.
But most importantly, using Ferro the webdeveloper only needs to think about the structure of the code. Not about all the things that are needed to make the code work in a webbrowser.
How does it work?
Ferro uses an object oriented programming style. You instantiate an object, that object in turn instantiates more child objects and add these as instance variables to itself. And so on, producing a hierarchy of object instances. This is called the Master Object Model (MOM).
When an object is instanciated in the MOM, Ferro will add an element to the webbrowsers Document Object Model (DOM). The MOM keeps a reference to every DOM element. This erradicates the need for element lookups (jquery $ searches). If you need an element you know where to find it in the MOM. Getter methods are automatically added by Ferro for easy access to instance variables.
Each object in the MOM inherits from a Ferro class. Which Ferro class you use determines what type of DOM element will be created. All Ferro classes inherit from one base class: FerroElement. For most DOM elements in the html specs there is a corresponding Ferro class. For instance if you need a html5 <header> element you would create a class that inherits from Ferro::Element::Header.
Other html elements have a more abstract counterpart. All text elements (<p>, <h1> .. <h6>) have one Ferro class: FerroElementText. The size of the text element is an option when you instantiate the object.
Maybe it sounds difficult but it is quity easy to get the hang of, as I hope the examples will prove. Besides, the source code of the Ferro library is so compact that it will take no more than 30 minutes to read.
What are some typical use cases?
Ferro is primarily meant for applications that heavily rely on front-end code. Single-page apps and (progressive) web apps are prime use cases.
What are the advantages?
- Only Ruby and CSS needed
- Easy naming conventions: CSS classnames match Ruby classnames
- Easy naming conventions: every DOM element has same ID as the corresponding Ruby object. Useful when attaching javascript libraries to elements
- Never lookup an element in the DOM. Ferro keeps a handle to each and every element which you can access from Ruby, object oriented style
- It is fast, browser javascript engines are highly optimized. The developer can control when to render components, only render what is needed when the application loads
- More secure: it is easy to embed scripts into html. As long as you stay away from using the innerHtml method, XSS attacks should be impossible
- Easy integration into serverside frameworks like Rails
What are some disadvantages
Every solution to a problem has its pro’s and con’s. Ferro is no exception. Here are some of Ferro’s disadvantages
- SEO results may suffer if the webcrawler only looks at html. Not really an issue when creating a webapp
- Separate content for screenreaders and javascript disabled browsers is needed
- Coding errors may disable (parts of) the webapp. A good set of (integration)tests is useful
- Somewhat higher browser memory usage to store the MOM
- No support for old browsers
What we don’t need when using Ferro
- HTML
- Javascript DOM finders (like jQuery, Zepto, …)
- Javascript libraries/frameworks that extend html (like Ember, Angular, JSX, Vue, Stimulus)
- Virtual DOM Javascript frameworks (like React)
Is Ferro finished?
No, Ferro is not yet feature complete. Most of the basic DOM components are done. Navigation and routing are handled. AJAX calls are wrapped.
Areas that need work are:
- Adding a service worker to catch all networktraffic when the browser is offline
- Adding support for storing and retrieving data
- A nice ActionCable / websockets client
- A markdown parser (that does not produce html but directly adds elements to the DOM)
- Localization (I18n)
- Many more ready made components, for instance a table component with sorting, filtering, etcetera
- Capture touch events and gestures
- A utility to convert html to ferro code might be useful
Enough with the introduction to Ferro. Let’s look at some examples.
A simple demo
Let’s start with a very simple example. We will define a small component with a title and a button. The button should change the title text when clicked.
class Demo < Ferro::Component::Base
def cascade
# Add the title
add_child(
:title,
Ferro::Element::Text,
size: 4,
content: 'Title'
) # Add the button
add_child(
:btn,
DemoButton,
content: 'Click me'
)
end def rotate_title
# We have access to the 'title' instance variable
txt = title.get_text
title.set_text (txt[1..-1] + txt[0]).capitalize
end
endclass DemoButton < Ferro::Form::Button
def clicked
# Every element knows its parent
parent.rotate_title
end
end
The code in Demo cascade method is equivalent to something like this, which should look more familiar to a Ruby programmer:
class Demo
def initialize
@title = Ferro::Element::Text.new(size: 4, content: 'Title')
@btn = DemoButton.new(content: 'Click me')
end
endEvery Ferro class has 3 hooks into the object creation lifecycle:
- before_create
- after_create
- cascade
The first two are called just before and after the object itself is created. The cascade hook is called when the object is ready to create child objects.
In this example most of the action happens in the cascade method.
Get Ivo Herweijer’s stories in your inbox
Join Medium for free to get updates from this writer.
By inheriting from Ferro::Form::Button, DemoButton has access to the click event handler. After a click occurred, it signals its parent (Demo) to rotate the title text.
Html ?
At first glance this code might look like an elaborate way to write html. But note that there is no ‘render’ method, there is no extra work needed to interact with the two DOM elements and we don’t have to divide our attention over separate html and javascript files.
In real life applications, where more logic is needed, the Master-Object-Model construction will blend into the background.
A simple Todo list
Every new technology seems to need a todo application to show of its usefulness. So lets not disappoint anyone and make our own version. Even a simple todo app has more moving parts than you might at first think. Functionally we need the following elements:
- a container to keep the entire todolist
- a title to show that it is indeed a todo list
- a text input and button to add new todo items
- a status bar showing how many (open) items there are, plus a link to remove completed items
- a list of todo items
- the actual todo items, showing a description and a checkbox to mark the item as completed
Now we translate these functional elements into a Master Object Model.
The todo component
This defines all the needed parts (except todo items) and defines some helper methods.
class Todo < Ferro::Component::Base
def cascade
add_child(
:title,
Ferro::Element::Text,
size: 4,
content: 'Todo list'
) add_child(
:entry,
TodoEntry,
button_text: 'Add',
placeholder: 'New item ...'
) add_child :status, TodoStatus add_child :list, TodoList update_status
end def update_status
status.update_status
end def add_list_item(value)
list.add_item value
update_status
end def clear_list
list.clear
end
end
Text input and button
This is easy since we can use a ready made Ferro component.
class TodoEntry < Ferro::Combo::Search
def submitted(value)
parent.add_list_item value
end
endStatus bar
Just a text to show a count of items and a ‘clear’ link.
class TodoStatus < Ferro::Element::Block
def cascade
add_child :info, Ferro::Element::Text add_child(
:clear,
TodoStatusClear,
content: '[clear]',
href: ''
)
end def update_status
info.set_text(
"#{parent.list.open_items} of {parent.list.total_items} remaining"
)
end
endclass TodoStatusClear < Ferro::Element::Anchor def clicked
# We could use: parent.parent.clear_list
# Here we use the top-down way to access the same method.
# 'component' points to the nearest element in the
# hierarchy that is a Ferro component.
# In this case the Todo instance.
component.clear_list
end
end
List of todo items
The list to hold and manage the todo items. It adds 3 default items.
class TodoList < Ferro::Form::Base
def cascade
@list = []
@id = Ferro::Sequence.new 'item_' [
'Learn Ruby',
'Learn Ferro',
'Create first NoHTML website',
].each do |name|
add_item(name)
end
end def add_item(name)
label = name.strip if !label.empty?
@list << add_child(@id.next, TodoItem, content: label)
end
end def clear
@list.delete_if do |item|
item.destroy if item.checked?
end parent.update_status
end def open_items
@list.select { |item| !item.checked? }.length
end def total_items
@list.length
end
end
Todo item
A checkbox with a label.
class TodoItem < Ferro::Element::Block def before_create
@content = option_replace :content
end def cascade
add_child :cb, TodoCheckBox add_child(
:content,
TodoLabel,
content: @content,
for: b.object_id
)
end def toggle_content(completed)
content.update_state(:completed, completed)
component.update_status
end def checked?
cb.checked?
end
endclass TodoCheckBox < Ferro::Form::CheckBox def clicked
parent.toggle_content checked?
end
endclass TodoLabel < Ferro::Form::Label def after_create
add_state :completed
end
end
Here we see the add_state and update_state methods being used for the first time. A state is a boolean flag that you can add to any Ferro class. If it has a true state Ferro will add a CSS class with the same name to the DOM element. In this case there is a CSS rule for the strike-through effect:
.completed {
text-decoration: line-through;
}Conclusion
Hopefully you now have a good first impression of what Ferro looks like and maybe even a little bit convinced of Ferro’s promise of simplifying web development. If you want to play around with Ferro you might want to check out the todolist example repo.
Ferro is already quite usable. More work is needed to have all the nice things in codinglife for creating a (progressive) webapp.
Also Ferro is just a first stab at making a library to support a NoHtml style of web development. Other coding styles are possible, for instance using a domain specific language (as long as it doesn’t look like html).
You can read more about Ferro and decoupling of webcontent and -styling.








