Introducing Elemental

HTML Element Names as Helper Methods

Summary

repository: http://svn.dangosaur.us/svn/elemental/

Introduces builder-like syntax to rhtml:

1
2
<%= p @item.content %>
<%= p span @person.first_name, :id => dom_id(@person, "name_") %>
or even
1
2
3
4
<% table do @list.each do |item| tr do %>
  <%= td item.name %>
  <%= td item.content %>
<% end end end %>

Elemental allows you to use XHTML Transitional tags as helper methods in your rhtml. With traditional ERb, the code above would be written:

1
2
<p><%= @item.content %></p>
<p><span id="<%= dom_id(@person, "name_") %>"><%= @person.first_name %></span></p>
and
1
2
3
4
5
6
7
8
<table>
  <% @list.each do |item| -%>
  <tr>
    <td><%= item.name %></td>
    <td><%= item.content %></td>
  </tr>
  <% end %>
</table>

That’s more code, more noise as angle-brackets (especially embedded inside the html tag), and more lines. Elemental’s syntax is also cleaner and terser than when using content_tag:

1
2
<%= content_tag "p", @item.content %>
<%= content_tag "p", content_tag "span", @person.first_name, :id => dom_id(@person, "name_") %>

and you can’t send a block to content_tag

1
2
3
4
5
6
7
8
9
<table>
  <% @list.each do |item| -%>
  <tr>
    <%= content_tag "td", item.name %>
    <%= content_tag "td", item.content %>
  </tr>
  <% end %>
</table>
<table>

Usage

Elemental has three basic usages:

1. Self-closing tags: no argument, or hash only for argument:

1
2
<% br %>
<% br :class => "someClass" %>

2. Content tags: first argument is the value of the content:

1
2
<%= p "some content" %>
<%= p "some content", :id => dom_id(@object) %>

3. Content tags with a block argument:

1
2
3
<% div :class => "some-class" do %>
      ...
<% end %>

You can nest Elemental methods:

1
2
3
4
5
<%= p span @object.value %>
  or
<%= p(span(@object.value)) %>
  generates:
<p><span>the object's value</span></p>

The same thing with attributes (pay attention to your parentheses):

1
2
3
4
5
6
7
<%= p span @object.value, :id => "some_id", :class => "some_class" %>
  generates:
<p><span id="some_id" class="some_class">the object's value</span></p>
  while
<%= p span(@object.value, :id => "some_id"), :class => "some_class" %>
  generates:
<p class="some_class"><span id="some_id">the object's value</span></p>

You can nest the methods in blocks:

1
2
3
4
5
6
<% p do %>
  <% span :class => "someClass" do %>
    Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
    sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
  <% end %>
<% end %>
or
1
2
3
4
<% p do span :class => "someClass" do %>
  Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
  sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
<% end end %>
which both generate:
1
2
3
4
5
6
<p>
  <span class="someClass">
    Lorem ipsum dolor sit amet, consectetur adipisicing elit, 
    sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
  </span>
</p>

This is useful for loops:

1
2
3
<% ul do ['one', 'two', 'three'].each do |item| %>
  <%= li item %>
<% end end %>
which generates:
1
2
3
4
5
<ul>
  <li>one</li>
  <li>two</li>
  <li>three</li>
</ul>

Options/Attributes Hash

Options are converted to regular html attributes. None are filtered, so you can certainly insert invalid attributes.

1
2
3
<%= span :id => "some_id", :bogus_attribute => "some_value"%>
  generates:
<span id="some_id" bogus_attribute="some_value"></span>

Omitted Tags

Rails’ ActionView::Helpers already defines form, select, and input, so these are omitted from Elemental.

Motivation

Afer using Markaby a bit, I decided there were situations where I wanted a Markaby or Builder-type syntax within rhtml’s context. I had been using content_tag quite a bit for convenience, but wanted more legible and concise code, espcially for loops.

Acknowledgments

_why for Markaby and its list of XHTML and XHTML Transitional tag lists, which I used.