Dennis Hackethal’s Blog
My blog about philosophy, coding, and anything else that interests me.
Programming in Strings
I used to be a big fan of Angular and Angular 2. Then a colleague at Apple explained to me why what he called "programming in strings" is a bad idea. For example, this nonsense:
<h1 ng-repeat="i in array">{{i}}</h1>
Angular isn't the only framework that makes this mistake. Rails makes it, too, with embedded Ruby. If you're used to writing embedded Ruby (those pesky .erb
files), you may not realize how bad it is.
Consider this template:
<ul>
<% [1, 2, 3].each do |i| %>
<li><%= i %></li>
<% end %>
</ul>
This is gross. Embedded Ruby makes you mix your template and your logic. Rails is big on separation of concerns, and the above example is the opposite of that. It's "programming in strings" because you're writing logic inside your template string. This problem is well known in the Clojure world. Logic should be taken care of before rendering, not during.
Hiccdown makes this happen by taking a datastructure representing your template – which you're free to build up logically in any way you like, using the full power of the Ruby programming language (map
, filter
, reduce
etc) – and then turning that into HTML at the end. All of this still happens on the server, so you still get all the benefits of pre-processing.
This solution was first implemented in the Clojure world with the Hiccup package. Hiccdown introduces the same (?) functionality in Ruby. (There's another package doing that but, at the time of writing, it hasn't been maintained in seven years. Plus, it was more fun building this from scratch!)
Usage
# plain
Hiccdown::to_html [:h1, 'hello world']
# => '<h1>hello world</h1>'
# nested siblings
Hiccdown::to_html [:div, [:h1, 'hello world'], [:h2, 'hello again']]
# => '<div><h1>hello world</h1><h2>hello again</h2></div>'
# attributes
Hiccdown::to_html [:h1, {class: 'heading big'}, 'hello world']
# => '<h1 class="heading big">hello world</h1>'
# children as arrays
Hiccdown::to_html [:ul, [[:li, 'first'], [:li, 'second']]]
# => '<ul><li>first</li><li>second</li></ul>'
#
# This is equivalent to writing:
Hiccdown::to_html [:ul, [:li, 'first'], [:li, 'second']]
# So why use it? So you can use methods that return arrays inside your hiccdown structure without having to use the splat operator every time:
Hiccdown::to_html [:ul, ['first', 'second'].map { |i| [:li, i] }]
# => '<ul><li>first</li><li>second</li></ul>'
The last example in particular gives you an insight into how powerful Hiccdown is. In embedded Ruby or Angular or what have you, I cannot map over elements, only over data. And I need special syntax for it, and I need to extend that syntax manually. With Hiccdown, you use plain old Ruby, and you can iterate over whatever you like because everything is data.
Usage in Rails
Delete your view file. In your controller action, do:
class FooController < ApplicationController
def bar
render html: Hiccdown::to_html([:h1, 'hello world!']).html_safe
end
end
(Be careful with html_safe
.)
How to get it?
Hiccdown (currently version 0.1.2) is available as a gem and the source code is on GitHub.
What people are saying