Skip to content

Instantly share code, notes, and snippets.

@bryanp
Last active August 29, 2015 14:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bryanp/933369a0e1aa0a952f42 to your computer and use it in GitHub Desktop.
Save bryanp/933369a0e1aa0a952f42 to your computer and use it in GitHub Desktop.
Faster View Manip
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Pakyow - Ruby Web Framework</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<link rel="stylesheet" type="text/css" media="screen, projection" href="/stylesheets/screen.css">
<link rel="stylesheet" type="text/css" media="print" href="/stylesheets/print.css">
<link rel="shortcut icon" type="image/png" href="/favicon.png">
<meta name="viewport" content="width=device-width">
<script type="text/javascript" src="http://use.typekit.com/eig1kgb.js"></script><script type="text/javascript">try{Typekit.load();}catch(e){}</script><link rel="alternate" type="application/rss+xml" href="http://feeds.feedburner.com/pakyow">
<meta name="description" content="An open-source framework for building web apps in Ruby. Get straight to work by letting data be data, views be views, and code be code.">
<meta name="tags" content="pakyow,ruby,web,framework,applications">
</head>
<body>
<div id="wrapper" data-scope="foo">
<div id="main">
<div class="homeIntro">
<div class="logoWrapper">
<img src="images/pakyow-small.png" class="logo"><h1 class="logoText">pakyow</h1>
</div>
<p class="clear"></p>
<ul id="navigation">
<li id="nav-overview">
<a href="/" class="active">Overview</a>
</li>
<li id="nav-manual">
<a href="/manual">Manual</a>
</li>
<li id="nav-blog">
<a href="/posts">Blog</a>
</li>
<li id="nav-community">
<a href="/community">Community</a>
</li>
<li>
<a href="http://github.com/metabahn/pakyow">Code</a>
</li>
</ul>
<h2>
Pakyow is an open-source framework for building web apps in Ruby. It enables you to get
straight to work by letting data be data, views be views, and code be code.
<em>Simple.</em>
</h2>
</div>
<div class="walkthrough">
<div class="description">
<h3>Designer-friendly Views</h3>
<p>
In Pakyow, views are purely structural and contain no logic, keeping the view focused on it's job of
presentation. No special markup is required in the view. This means the designer
can build the presentation layer for an app in their own environment.
</p>
<a href="/manual#section_3">Learn more about views →</a>
</div>
<img src="/images/walkthrough-views.png">
</div>
<div class="walkthrough">
<img src="/images/walkthrough-construction.png"><div class="description">
<h3>Sturdy Prototypes</h3>
<p>
View construction happens automatically, which means a working, navigable front-end can be created without
any back-end code. Business logic is added later without any changes to the front-end, eliminating resistance
and keeping development moving forward.
</p>
<a href="/manual#section_3.1">Learn more about view construction →</a>
</div>
</div>
<div class="walkthrough">
<div class="description">
<h3>Intelligent Connections</h3>
<p>
Data awareness is built into views, meaning a view knows what it presents. Data is bound in from the back-end
without requiring a single change to the view. Roles and responsibilities remain clear throughout the
development process.
</p>
<a href="/manual#section_8.3">Learn more about data binding →</a>
</div>
<img src="/images/walkthrough-data.png">
</div>
<p>
Pakyow does all of this (and more) while being simple to use and performant in tough
situations. It's the best way to take down a hard-hitting project.
</p>
<div class="action"><a href="/manual">Get ready to rumble.</a></div>
</div>
<div id="footer">
<hr>
© 2011. Created by <a href="http://twitter.com/bryanp">Bryan Powell</a> and <a href="http://twitter.com/hby">Bret Young</a>.
Pakyow is a <a href="http://metabahn.com">Metabahn</a> project.
</div>
</div>
<script src="//static.getclicky.com/js" type="text/javascript"></script><script type="text/javascript">try{ clicky.init(66467206); }catch(e){}</script>
</body>
</html>
#TODO figure out why parsing <head> isn't working right
require 'pp'
# PARSING
require 'nokogiri'
def parse(string, writer = '')
structure = []
if string.match(/<html.*>/)
doc = Nokogiri::HTML::Document.parse(string)
structure << '<!DOCTYPE html>'
else
doc = Nokogiri::HTML.fragment(string)
end
breadth_first(doc) do |node|
next if node == doc
children = node.children.reject {|n| n.is_a?(Nokogiri::XML::Text)}
attributes = node.attributes
if children.empty? && !significant?(node)
structure << node.to_html
throw :reject
else
if significant?(node)
attr_structure = attributes.inject({}) do |attrs, attr|
attrs[attr[1].name] = attr[1].value
attrs
end
closing = ['>', parse(node.inner_html)]
closing << "</#{node.name}>" unless self_closing?(node.name)
structure << ["<#{node.name} ", attr_structure, closing.flatten(1)]
throw :reject
else
attr_s = attributes.inject('') { |s, a|
s << " #{a[1].name}=\"#{a[1].value}\""
s
}
structure << "<#{node.name}#{attr_s}>"
structure.concat parse(node.inner_html)
structure << "</#{node.name}>" unless self_closing?(node.name)
throw :reject
end
end
end
return merge(structure)
end
def merge(structure)
structure.inject([]) {|merged, content|
if merged.last.is_a?(String) && content.is_a?(String)
merged.last << content
elsif content.is_a?(Array)
merged << merge(content)
else
merged << content
end
merged
}
end
def significant?(node)
return false if node.attributes.empty?
return true if node['data-scope']
end
def breadth_first(doc)
queue = [doc]
until queue.empty?
catch(:reject) do
node = queue.shift
yield node
queue.concat(node.children)
end
end
end
# html = '<div>...</div><a attr_k="attr_v">foo</a>'
# html = '<div>...</div>'
# html = '<div><a attr_k="attr_v">foo</a></div>'
# pp parse(html)
# FLATTENING
# just a string
# e1 = ['<div>...</div>']
# a string plus tuple representing attributes
# e2 = ['<div>...</div>', ['a', [['attr_k', 'attr_v']], ['foo']]]
# nested case
# e3 = ['<div>', ['<a ', {href: '#'}, ['>', 'foo', '</a>']], '</div>']
def render(structure)
flatten(structure).flatten.join
end
def flatten(structure)
structure.map { |content|
content.is_a?(Array) ? contentify(content) : content
}
end
def contentify(content)
content.map { |p|
if p.is_a?(Hash)
attrify(p)
elsif p.is_a?(Array)
flatten(p)
else
p
end
}
end
def attrify(attrs)
attrs.map { |attr|
#TODO do this without interpolation?
# "#{attr[0]}=\"#{attr[1]}\""
attrs.join('=')
}.join(' ')
end
SELF_CLOSING = %w[area base basefont br hr input img link meta]
def self_closing?(tag)
SELF_CLOSING.include? tag
end
# BENCHMARKS
require 'benchmark'
html = File.read('pakyow-full.html')
# parsing
iterations = 100
Benchmark.bm do |bm|
# joining an array of strings
bm.report do
iterations.times do
parse(html)
end
end
# using string interpolation
doc = Nokogiri::HTML::Document.parse(html)
bm.report do
iterations.times do
Nokogiri::HTML::Document.parse(html)
end
end
end
# rendering
# iterations = 10_000
# Benchmark.bm do |bm|
# # joining an array of strings
# bm.report do
# iterations.times do
# render(structure)
# end
# end
# # using string interpolation
# doc = Nokogiri::HTML::Document.parse(html)
# bm.report do
# iterations.times do
# doc.to_html
# end
# end
# end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment