Last active
August 29, 2015 14:02
-
-
Save bryanp/933369a0e1aa0a952f42 to your computer and use it in GitHub Desktop.
Faster View Manip
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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