Skip to content

Instantly share code, notes, and snippets.

@alloy
Last active August 29, 2015 14:09
Show Gist options
  • Save alloy/c81aecf541f3d842d7ea to your computer and use it in GitHub Desktop.
Save alloy/c81aecf541f3d842d7ea to your computer and use it in GitHub Desktop.

Derb

DOM-ERB is a prototype for an ERB based HTML template system that has the following benefits over regular ERB HTML templates:

  • The template file is a standalone renderable HTML file and can be viewed directly in a browser.
  • The template file can contain seed data so that directly viewing it in a browser gives a realistic result.
  • Because of these benefits, users can work on it without the need to install any sort of processor dependencies, lowering the barrier to contribution. (E.g. setting up a full web-app stack.)

Examples

# TODO
#
# * Find a way to add content without escaping. e.g. `<` escaped to `&lt;`.
# * How to deal with dynamic attributes, if necessary?
# ---------------------------------------------------------------------------- #
require 'nokogiri'
def process_derb(file)
doc = Nokogiri::HTML(File.read(file))
# A `data-derb` attribute replaces the content of the element with the ERB
# specified in the attribute.
doc.xpath('//*[@data-derb]').each do |element|
element.content = element['data-derb']
element.remove_attribute('data-derb')
end
# A `data-derb-block` attribute in addition inserts `<% end %>` before the
# closing tag of the element, which should be used for e.g. conditional blocks
# or closures.
doc.xpath('//*[@data-derb-block]').each do |element|
element.prepend_child(Nokogiri::XML::Text.new(element['data-derb-block'], doc))
element.add_child(Nokogiri::XML::Text.new('<% end %>', doc))
element.remove_attribute('data-derb-block')
end
# Replace attributes prefixed with `derb-` for the same name without the
# prefix. E.g. replace a static anchor `href` attribute with a dynamic one.
doc.xpath('//@*[starts-with(name(), "derb-")]').each do |attribute|
name = attribute.name.sub(/^derb-/, '')
element = attribute.parent
element[name] = attribute.content
element.remove_attribute(attribute.name)
end
template = doc.to_s
# Derp
template = template.gsub('&lt;', '<').gsub('&gt;', '>').gsub('%20', ' ')
#puts template
template
end
# ---------------------------------------------------------------------------- #
# Define `pod` variable that's available to template.
class Pod
def name
'ARAnalytics'
end
def homepage
'https://github.com/orta/ARAnalytics'
end
def summary
'Simplify your iOS/Mac analytics'
end
# Optional
def description
#'ARAnalytics is to iOS what Analytical is to ruby, or Analytics.js is to javascript.'
nil
end
# Multi-value
def platforms
[
'iOS',
#'OS X',
]
end
end
pod = Pod.new
require 'erb'
puts ERB.new(process_derb('index.derb.html')).result(binding)
<html>
<head>
<title derb="<%= pod.name %>">AFNetworking</title>
</head>
<body>
<h1>Overview of <a href="http://afnetworking.com" derb-href="<%= pod.homepage %>"><span derb="<%= pod.name %>">AFNetworking</span></a></h1>
<!-- The required summary. -->
<p derb="<%= pod.summary %>">A delightful iOS and OS X networking framework.</p>
<!-- An optional longer description. -->
<div derb-block="<% if pod.description %>">
<p derb="<%= pod.description %>">It's built on top of the Foundation URL Loading System, extending the powerful high-level networking abstractions built into Cocoa. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use.</p>
</div>
<!-- A list of all supported platforms. -->
<ul derb-block="<% pod.platforms.each do |platform| %>">
<li>Supported on <span derb="<%= platform %>">iOS</span></li>
</ul>
</body>
</html>
@Manfred
Copy link

Manfred commented Nov 14, 2014

You can use <%== to get unescaped stuff. Is that what you're looking for?

@tvandervossen
Copy link

  • Why not remove the <% … %> and <%= … %> markup from the attribute values?
  • You can safely remove the “data-” prefix and use “derb” and “block” as the attribute names directly; browsers don’t care and you’re removing these attributes anyway.

And as I said before, I think anything beyond replacing element content is a Bad Idea® and People Who Edit This Will Get Confused And Fuck That Up™.

For example, one thing those pesky web designers always seem to want to be able to do is this:

<% unless pod.dependencies %>
  <p>This pod has no dependencies. Ain’t that nice?</p>
<% elseif pod.dependencies.length ==  1 %>
  <p>This pod depends on <a href="<%= link_to pod.dependencies.first %>"><%= pod.dependencies.first.label %></a>.</p>
<% else %>
  <ul>
    <% pod.dependencies.each do |dependency| %>
      <li><a href="<%= link_to dependency %>"><%= dependency.label %></a></li>
    <% end %>
  </ul>
<% end %>

How are you planning to support that (without adding a ton of additional markup that’s not needed)?

@alloy
Copy link
Author

alloy commented Nov 14, 2014

@ManfredS No it’s Nokogiri that’s escaping < and >. I’m sure there’s a way to get that to work, but it’s not that important right now anyways.

@alloy
Copy link
Author

alloy commented Nov 14, 2014

@tvandervossen Very good points, thanks a bunch for that dose of reality! This is more of a thought experiment right now, I will have to mull those extra conditional branches over, so I like this :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment