There are no reviews yet. Be the first to send feedback to the community and the maintainers!
~ ~ NOTE~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ I'm just pushing the latest changes out before I'll go on holiday, so at the moment it's not working 100% correctly. If you still want to play around you could: 1) git checkout 0797b2a253e40103c8132374996c1dc9ed5aa4aa (old version) 2) clone SexpTemplate and SexpBuilder (see github.com/judofyr) and put them in the load path at the top of lib/parkaby.rb I'll release it as a gem when I'll get back home. //Magnus Holm ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ = Parkaby, ParseTree meets Markaby In the beginning God created ERB. And ERB was waste and void; and darkness was upon the face of the deep: and the Spirit of God moved upon the face of the templates. And God said, Let there be Ruby: and there was Markaby. ~~ Genesis 1:1-3, The Template Engine Bible Ruby is nice. I think that's something we all can agree on. When you suddenly need to output some HTML in the middle of your app, it's very convenient to continue writing Ruby, instead of switching to String and interpolation. That's where Markaby comes in: mab { html { head { title "happy title" } body { h1 "happy heading" a "a link", "href" => "url" } } } Of course, we all know that Ruby is slow. Needless to say, that makes Markaby slow too. Not even Tagz, Ara T Howard's fast Markaby-clone, can't stand a chance against Erubis and Haml. It's time we fight back. Let's show them that pure, readable Ruby can get on par with percent signs and forced indentations: Parkaby { html { head { title "happy title" } body { h1 "happy heading" a "a link", "href" => "url" } } } See, no changes at all, but take a look at the benchmark: ~> ruby bench/run.rb simple 10000 user system total real Erubis 0.030000 0.000000 0.030000 ( 0.022264) Haml 0.110000 0.000000 0.110000 ( 0.117887) Parkaby (def_method) 0.130000 0.000000 0.130000 ( 0.135996) Parkaby (render) 0.150000 0.010000 0.160000 ( 0.150680) Parkaby (inline) 0.970000 0.000000 0.970000 ( 0.988010) Tagz 3.250000 0.040000 3.290000 ( 3.400699) Markaby 12.610000 0.140000 12.750000 ( 13.067794) Okay, with all respect: this is a really crappy benchmark. Luckily, the Haml guys have written a very nasty template: ~> ruby bench/run.rb nasty 500 user system total real Erubis 0.190000 0.010000 0.200000 ( 0.198487) Parkaby (def_method) 0.350000 0.000000 0.350000 ( 0.363106) Parkaby (render) 0.360000 0.010000 0.370000 ( 0.365007) Parkaby (inline) 0.570000 0.000000 0.570000 ( 0.614286) Haml 2.490000 0.030000 2.520000 ( 2.620025) Tagz 5.100000 0.060000 5.160000 ( 5.394778) Markaby 7.220000 0.090000 7.310000 ( 7.630588) That's more like it! It's still not a truly fair comparision though. Both Parkaby, Tagz and Markaby escapes all input, while in Erubis and Haml you'll need to explicitly mark it where needed. In the nasty template, nearly nothing needs to be escaped, so Parkaby, Tagz and Markaby are doing a lot of escaping for nothing! ~ ~ UPDATE ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ Nathan Weizenbaum reports in: "You say in the Parkaby docs that in Haml you need to manually mark escaping, but that's not true. Haml supports an option (:escape_html) that makes it default to escaping all input." ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ In a real-life scenario you want to escape nearly everything which comes from the user, so I'm still looking for a better template to run benchmarks on. = Synopsis require 'lib/parkaby' before = self ## inline Parkaby { self == before # => true self << "<!DOCTYPE html>" # or: text "<!DOCTYPE html>" html { head { something } # tag unless self.respond_to?(:head) tag.head { something } # force a tag self.head { something } # force a method call span "<em>Escape!</em>" # => "<span><em>Escape!</em></span>" span { "<em>No escape!</em>" } # => "<span><em>No escape!</em></span>" div { strong "Ruby!" # You can't mix tags "Silent." # and return values in blocks } # => "<div><strong>Ruby!</strong></div>" a "something", :href => 'http://google.com' a(:href => 'http://google.com') { "something else" } } } # => lots of html ## Using Parkaby::Template temp = Parkaby::Template.string('strong self') # = Parkaby::Template.block { strong self } ctemp = temp.compile(Helpers) ctemp.render("Ruby!") # => "<strong>Ruby!</strong>" temp.def_method(String, :strong) "Ruby!".strong # => "<strong>Ruby!</strong>" = More The easiest way to use Parkaby is simply to pass in a block: Parkaby { html { ... } } # => "<html>...</html>" Let's however take a look at how to use Parkaby::Template: temp = Parkaby::Template.string('html { ... }') temp = Parkaby::Template.block { html { ... } } After you got a Parkaby::Template, you'll have to compile it to a Parkaby::CompiledTemplate. There are two types of a compiled template: One that stores the template as a string, and one that stores it as a proc. The latter is quite a lot faster the former, but if you're going to evaluate the template under a binding, you'll have to use a string. You also have to give it a helper object when you compile it. This makes sure it won't turn methods into HTML-blocks if they exist on the helper object. (You can however always prepend the method with "self." to force a method call.) ctemp = temp.compile_as_string(helper || binding) ctemp = temp.compile_as_proc(helper || binding) temp.compile(binding) == temp.compile_as_string(binding) temp.compile(helper) == temp.compile_as_proc(helper) After you've compiled it, you just call #render with the object that you want to be `self`, or the binding it should be called with. Notice that both compile_as_proc and compile_as_string supports both bindings and regular objects as arguments (same goes for their #render). They're smart enough to convert it the way they want it. You can also use Template#def_method to define it as a method on an object: temp.def_method(String, :cool) "awesome".cool obj = Object.new temp.def_method(obj, :cool) obj.cool # Use :instance_eval to make it a class method on a class temp.def_method(String, :cool, :instance_eval) String.cool = How It's quite obvious that Parkaby is fast. How? The secret ingredient is: ParseTree! ParseTree turns this: html { head { title "happy title" } body { h1 "happy heading" a "a link", "href" => "url" } } into this: s(:iter, s(:call, nil, :html, s(:arglist)), nil, s(:block, s(:iter, s(:call, nil, :head, s(:arglist)), nil, s(:call, nil, :title, s(:arglist, s(:str, "happy title")))), s(:iter, s(:call, nil, :body, s(:arglist)), nil, s(:block, s(:call, nil, :h1, s(:arglist, s(:str, "happy heading"))), s(:call, nil, :a, s(:arglist, s(:str, "a link"), s(:hash, s(:str, "href"), s(:str, "url")))))))) then Parkaby::Processor turns it into this: s(:parkaby, :begin, s(:parkaby, :blocktag, :html, s(:block, s(:parkaby, :blocktag, :head, s(:parkaby, :tag, :title, s(:str, "happy title"), nil), nil), s(:parkaby, :blocktag, :body, s(:block, s(:parkaby, :tag, :h1, s(:str, "happy heading"), nil), s(:parkaby, :tag, :a, s(:str, "a link"), s(:hash, s(:str, "href"), s(:str, "url")))), nil)), nil)) and Parkaby::Generator turns that into this: _parkaby_buffer = [_parkaby_current = []] _parkaby_current << "<html>" _parkaby_buffer << (_parkaby_current = []) _parkaby_value = begin (_parkaby_current << "<head>" _parkaby_buffer << (_parkaby_current = []) _parkaby_value = begin _parkaby_current << "<title>happy title</title>" end _parkaby_current << _parkaby_value if _parkaby_current.empty? _parkaby_current << '</head>' _parkaby_current << "<body>" _parkaby_buffer << (_parkaby_current = []) _parkaby_value = begin (_parkaby_current << "<h1>happy heading</h1>" _parkaby_current << "<a href=\"url\">a link</a>" ) end _parkaby_current << _parkaby_value if _parkaby_current.empty? _parkaby_current << '</body>' ) end _parkaby_current << _parkaby_value if _parkaby_current.empty? _parkaby_current << '</html>' _parkaby_buffer.join In this specific example you can clearly see that we still have quite a few optimizations left until it's perfect, but it's still 23 times faster than Markaby's: mab { html { head { title "happy title" } body { h1 "happy heading" a "a link", "href" => "url" } } } = Notes * Parkaby doesn't change `self` at all. * It's smart (maybe too smart): * Parkaby { li "I", "got", "three arguments" } # => NoMethodError * Parkaby { li({:hash => :fist}, "content after")} # => NoMethodError * Parkaby { self.li } # => NoMethodError * Parkaby { li } # => "<li/>" * def li end; Parkaby { li } # => "" * Currently, you must give it a Hash-literal for the attributes. [BUG] * CssProxy and capture isn't implemented yet. * I'm thinking of adding a a follow-feature: when it sees `follow.some_method` it fetches the method definition and inlines it in the main template. * Yes, this is very experimental and I don't think anyone is ever going to use it.
temple
Template compilation framework in Rubyperloku
Perl on Herokutimeless
A mixture of a blog, wiki and CMS, inspired by Christoffer Sawicki's Termos and James Adam's Vanilla.rbglush
A parser toolkitduktape.rb
Ruby bindings to the Duktape JavaScript interpretergash
Git + Hashgithub-js
GitHub.jstubby
HTML templates as Rubyminz
Minimal string compressionminitest-line
rubyscript
A Ruby VM implemented in JavaScriptbankid-api
API documentation for the Norwegian BankID protocolzini
Minimal perfect hash function for Zigcamping
the 4k pocket full-of-gags web microframeworkparg
Lightweight argument parser for Zigcab
Lightweight code reloader for Ruby/Rackrcpu
DCPU assembler and emulator in Rubyish
Sketches for Ziggemify
The lightweight gemspec editor.recursive
The Recursive Benchmarknokogirl
Nokogirl โ because developers can't spellkramer
Generalized parser combinators in Rubyrgb-tree
RGB trees, a generalization of red-black trees. Implemented in Zig.grancher
Easily copy folders and files to other Git branchesquickgem
Speed up load timequickedit
Quickedit is a micro-CMS which you can easily embed in any Rails, Sinatra or Rack appsdevlicious
Chrome DevTools for Mojolicioushandlebars-jit
A JIT for Mustacheimba-rails
ippon
The gentle waytry-camping
RSoC: Interactive tutorials for learning web development in Rubywoop-2018
a programming language and environment that is almost entirely written in itselfImbaKit
mockie
The beginning of a proper mockup-driven template languagehox
Web toolkit in Nimfiltering_camping
Adding before/after-filters to Campingquickload.js
rumble
toml.nim
TOML parser for Nimimba-loader
imba-styles
dep.js
a tiny dependency managersodium.nim
solarbeam
Super effective Solr client in Perl that uses Mojolicious' event loopaoc2018
tvo-lang
arch-musl
PKGBUILDs for various statically built libraries using muslshasm.js
camping-test
Lightweight testing framework for Campinguno-lang
A programming languagesexp_template
A template engine which allows you to express S-expressions in pure Rubyimba-experiments
sexp_builder
Easily process and rewrite S-expressions using SexpPathimba-template
zig-dot-builder
Easily create .dot files from Zigrpython-example
Just me playing with RPythonethercode
forter
Forte implementation in Rubyrandom
flexmex
doo
Development tool for starting and running servicesjason.lua
imba-source
bob
Bob the Builderhix
HTTP server for Nimglick
Implementation of Glicko-2 in Rubydotfiles
tapper
Write simple TAP tests in Rubymockle
TODO: Implement thiswordle-analyze
mockle.rb
Just me playing with another template engine. Move along.nir
heffigy
Fast DOM manipulation template frameworksofaskull
Skull+Paws.rb
Paws implementation in Rubytipi
ufwx
Just 4-letter random project name that Mike Perham won't take. Still in need of some code thoughโฆlatcher
A more precise Matcher (for CtrlP)zrb
json-builder
minad
willdo
Task manager in Vim for programmersminitest-chain
tada
smockle
dominic
appy
mockle.spec
Specification for the Mockle template languagecoordinated-omission-example
perloku-debug
phat
No idea what this project is about. I just wanted the name so people in the future can call me "the fat guy" the same way the call Marc "the thin guy"Love Open Source and this site? Check out how you can help us