• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
All about Erubis (English)
 

All about Erubis (English)

on

  • 5,863 views

(This is presentation slide for RubyKaigi 2009) ...

(This is presentation slide for RubyKaigi 2009)
Erubis is very fast and extensible implementation of eRuby. In this slides, I show you features of Erubis, and issues related to eRuby and solution by Erubis. Also I show you some ideas about the future of template system.

Statistics

Views

Total Views
5,863
Views on SlideShare
5,846
Embed Views
17

Actions

Likes
2
Downloads
23
Comments
0

1 Embed 17

http://www.slideshare.net 17

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    All about Erubis (English) All about Erubis (English) Presentation Transcript

    • RubyKaigi2009 All about Erubis And the future of template system makoto kuwata <kwa@kuwata-lab.com> http://www.kuwata-lab.com/ copyright(c) 2009 kuwata-lab.com all rights reserved. 1
    • I have something to say at first... ‣ Thank you for all staff of RubyKaigi! ‣ Thank you for all audience who join this session! copyright(c) 2009 kuwata-lab.com all rights reserved. 2
    • Agenda ‣ Part 1. Features of Erubis ‣ Part 2.Issues about eRuby and solutions by Erubis ‣ Part 3. Future of template system copyright(c) 2009 kuwata-lab.com all rights reserved. 3
    • Part 1. Features of Erubis copyright(c) 2009 kuwata-lab.com all rights reserved. 4
    • Introduction to Erubis ‣ Pure Ruby implementation of eRuby ‣ Very fast • http://jp.rubyist.net/magazine/?0022-FasterThanC ‣ Highly functional • HTML escape in default • Changing embedded pattern • Support PHP, Java, JS, C, Perl, Scheme • and so on... copyright(c) 2009 kuwata-lab.com all rights reserved. 5
    • Basically Usage Ruby program: require 'rubygems' # if need require 'erubis' str = File.read('template.eruby') eruby = Erubis::Eruby.new(str) print eruby.result(binding()) command-line: $ erubis template.eruby # execute $ erubis -x template.eruby # convert into Ruby $ erubis -z template.eruby # syntax check copyright(c) 2009 kuwata-lab.com all rights reserved. 6
    • HTML Escape in Default str =<<END <%= %> ... WITH escaping, <%= var %> <%== %> ... WITHOUT escaping <%== var %> END eruby = Erubis::Eruby.new(str, :escape=>true) puts eruby.result(:var=>"<B&B>") output: &lt;B&am;&gt; User can choose escape or not <B&B> escape in default (choosability) copyright(c) 2009 kuwata-lab.com all rights reserved. 7
    • Changing Embedded Pattern ‣ ex : use '[% %]' instead of '<% %>' [% for x in @list %] <li>[%= x %]</li> You must escape regexp meta [% end %] characters by backslash! ## Ruby Erubis::Eruby.new(str, :pattern=>'[% %]') ## command-line $ erubis -p '[% %]' file.eruby copyright(c) 2009 kuwata-lab.com all rights reserved. 8
    • Use Hash or Object instead of Binding example of using Hash example of using Object hash = { @title = "Example" :title => "Example", @items = [1, 2, 3] :items => [1, 2, 3], } erubis = erubis = Erubis::Eruby.new(str) Erubis::Eruby.new(str) puts erubis.evaluate(self) puts erubis.result(hash) <h1><%= title%></h1> <h1><%= @title%></h1> <% for x in items %> <% for x in @items %> <% end %> <% end %> copyright(c) 2009 kuwata-lab.com all rights reserved. 9
    • Enhancer ‣ Ruby modules which enhances Erubis features ## do HTML escape <%= %> in default module EscapeEnhancer def add_expr(src, code, indicator) if indicator == '=' src << " _buf<<escapeXml(#{code})" elsif indicator == '==' src << " _buf<<(#{code}).to_s;" end end It is easy to override Erubis features because internal of Erubis is splitted into end many small methods. copyright(c) 2009 kuwata-lab.com all rights reserved. 10
    • Enhancer (cont') ### Enhance which prints into stdout ### (you can use print() in statements) module StdoutEnhancer use _buf=$stdout def add_preamble(src) instead of _buf="" src << "_buf = $stdout;" end def add_postamble(src) src << "n""n" end use "" (empty string) end instead of _buf.to_s copyright(c) 2009 kuwata-lab.com all rights reserved. 11
    • Usage of Enhancer All you have to do is to include ### Ruby or extend ehnacer modules class MyEruby < Erubis::Eruby include Erubis::EscapeEnhancer include Erubis::PercentLineEnhancer end puts MyEruby.new(str).result(:items=>[1,2,3]) Specify names with ',' ### command-line $ erubis -E Escape,Percent file.eruby copyright(c) 2009 kuwata-lab.com all rights reserved. 12
    • Standard Enhancers ‣ EscapeEnhancer : escape html in default ‣ PercentLineEnhancer : recognize lines starting with '%' as embedded statements ‣ InterporationEnhancer : use _buf<<"#{expr}" for speed ‣ DeleteIndentEnhancer : delete HTML indentation ‣ StdoutEnhancer : use _buf=$stdout instead of _buf="" ‣ ... and so on (you can show list of all by erubis -h) copyright(c) 2009 kuwata-lab.com all rights reserved. 13
    • Context Data ‣ You can specify data to pass into template file (context data) in command-line ### command-line $ erubis -c '{arr: [A, B, C]}' template.eruby # YAML $ erubis -c '@arr=%w[A B C]' template.eruby # Ruby <% for x in @arr %> <li>A</li> <li><%= x %></li> <li>B</li> <% end %> <li>C</li> copyright(c) 2009 kuwata-lab.com all rights reserved. 14
    • Context Data File ‣ Load '*.yaml' or '*.rb' as context data file $ erubis -f data.yaml template.eruby # YAML $ erubis -f data.rb template.eruby # Ruby data.yaml data.rb title: Example @title = "Example" items: @items = - name: Foo [ {"name"=>"Foo"}, - name: Bar {"name"=>"Bar"}, ] copyright(c) 2009 kuwata-lab.com all rights reserved. 15
    • Debug Print ‣ <%=== expr %> represents debug print <%=== @var %> No need to write the same expression twice ### Ruby code $stderr.puts("*** debug: @var=#{@var.inspect}") ### Result *** debug: @var=["A", "B", "C"] copyright(c) 2009 kuwata-lab.com all rights reserved. 16
    • Support Other Programming Langs ‣ PHP, Java, JS, C, Perl,Scheme (only for convertion) <% for (i=0; i<n; i++) { %> (example of C) <li><%= "%d", i %> <% } %> same format as printf() #line 1 "file.ec" (output of erubis -xl c file.ec) for (i=0; i<n; i++) { fputs("<li>", stdout); fprintf(stdout, "%d", i); fputs("n", stdout); } copyright(c) 2009 kuwata-lab.com all rights reserved. 17
    • Conslution ‣ Erubis is very functional and extensible • HTML escape in default • Changing embedded pattern • Enhancer • Context data and file • Debug print • Support PHP, Java, JS, C, Perl, and Scheme copyright(c) 2009 kuwata-lab.com all rights reserved. 18
    • Part 2. Issues about eRuby and solutions by Erubis copyright(c) 2009 kuwata-lab.com all rights reserved. 19
    • Issue : local variables can be changed ‣ When using binding(), local variables can be non-local • Difficult to find if exists i=0 ### file.erb str = File.read('file.erb') <% for i in 1..3 %> ERB.new(str).result(binding) <li><%= i %></li> p i #=> 3 <% end %> Changed insidiously! copyright(c) 2009 kuwata-lab.com all rights reserved. 20
    • Cause of the issue ‣ binding() passes all local variables into template file • It is impossible to pass only variables which you truly want to pass • It is hard to recognize what variables are passed b = Bingind.new This is ideal b[:title] = "Example" but impossible... b[:items] = [1, 2, 3] copyright(c) 2009 kuwata-lab.com all rights reserved. 21
    • Solution by ERB ‣ Nothing, but the author of ERB introduced a solution to define custom Struct • http://d.hatena.ne.jp/m_seki/20080528/1211909590 Foo = Struct.new(:title, :items) class Foo def env; binding(); end end ctx = Foo.new("Example", [1,2,3]) ERB.new(str).result(ctx.env) copyright(c) 2009 kuwata-lab.com all rights reserved. 22
    • Solution by Erubis ‣ Use Hash instead of Binding It is very clear what erubis.result(:items=>[1, 2, 3]) data are passed! def result(b=TOPLEVEL_BINDING) if b.is_a?(Hash) s = b.collect{|k,v| "#{k}=b[#{k.inspect}];"}.join b = binding() eval s, b Set hash values as local vars end with creating new Binding return eval(@src, b) end copyright(c) 2009 kuwata-lab.com all rights reserved. 23
    • Solution by Erubis (cont') ‣ Use Object instead of Binding @items = [1, 2, 3]; <% for x in @items %> erubis.evaluate(self) <% end %> def evaluate(ctx) Convert Hash values into if ctx.is_a?(Hash) instance variables hash = ctx; ctx = Object.new hash.each {|k,v| ctx.instance_variable_set("@#{k}", v) } end return ctx.instance_eval(@src) end copyright(c) 2009 kuwata-lab.com all rights reserved. 24
    • Issue : cost of convertion and parsing ERB 1. 8.6 Erubis::Eruby ERB 1. 8.7 Erubis::Eruby ERB 1.9.1 Erubis::Eruby 0 10 20 30 (sec) Costs of parsing and Execution convertion are higher Parsing(by eval) than of execution Convertion(eRuby to Ruby) copyright(c) 2009 kuwata-lab.com all rights reserved. 25
    • Solution by ERB ‣ Convertion cost : nothing ‣ Parsing cost : helper to define method • Usage is much different from normal usage class Foo extend ERB::DefMethod def_erb_method('render', 'template.erb') end print Foo.new.render copyright(c) 2009 kuwata-lab.com all rights reserved. 26
    • Solution by Erubis ‣ Convertion cost : cache Ruby code into file • 1st time : save converted Ruby code into *.cache file • 2nd time : read ruby code from *.cache file eruby = Erubis::Eruby.load_file("file.eruby") print eruby.result() Available even in CGI copyright(c) 2009 kuwata-lab.com all rights reserved. 27
    • Solution by Erubis (cont') ‣ Parsing cost : keep ruby code as Proc object • The same way to use • Almost the same speed as defining method instance_eval can take a def evaluate(ctx) Proc object as argument @proc ||= eval(@src) instead of string (ruby code) ctx.instance_eval(@proc) end copyright(c) 2009 kuwata-lab.com all rights reserved. 28
    • Issue: extra line breaks ‣ eRuby outpus extra line breaks • Big problem for non-HTML text Extra line break <ul> <ul> <% for x in @list %> <li>AAA</li> <li><%= x %></li> <% end %> <li>BBB</li> </ul> Extra line break </ul> copyright(c) 2009 kuwata-lab.com all rights reserved. 29
    • Solution by ERB ‣ Provides various trim mode • ">" : removes LF at the end of line • "<>" : removes LF if "<%" is at the beginning of line and "%>" is at the end of line • "-" : removes extra spaces and LF around "<%-" and "-%>" • "%" : regard lines starting with "%" as embedded statements • "%>", "%<>", "-" : combination of "%" and ">"/"<>"/"-" ERB.new(str, nil, "%<>") copyright(c) 2009 kuwata-lab.com all rights reserved. 30
    • Solution by Erubis ‣ Change operation between embedded statement and expression • <% stmt %> : remove spaces around it • <%= expr %> : do nothing (leave as it is) <ul> Remove! <ul> <% for x in @list %> AAA <%= x %> BBB <% end %> CCC </ul> Leave as it is </ul> copyright(c) 2009 kuwata-lab.com all rights reserved. 31
    • Comparison of solutions ERB Erubis eRuby spec compatible × spec) (compatible) (extends Spec simplicity × opts) (only one rule) (too much Easy to implement × (very easy) (complicated) copyright(c) 2009 kuwata-lab.com all rights reserved. 32
    • Hint to think ‣ "Extra line breaks" problem has been recognized since early times • [ruby-list:18894] extra LF in output of eRuby ‣ Nobody hit on the idea of changing operations between stmts and exprs • Everybody looks <% %> and <%= %> as same • It is important to recoginize two things which looks to be the same things as different things copyright(c) 2009 kuwata-lab.com all rights reserved. 33
    • Issue : Escape HTML in default ‣ <%= expr %> should be HTML escaped in default! • But eRuby is not only for HTML but also for all of text file • However security is the most important thing ‣ How to do when not to escape? copyright(c) 2009 kuwata-lab.com all rights reserved. 34
    • Solution by ERB ‣ Nothing for officially ‣ Unofficial solution • Define a certain class which represents HTML string (not to escape) separately from String class • http://www2a.biglobe.ne.jp/~seki/ruby/erbquote.html copyright(c) 2009 kuwata-lab.com all rights reserved. 35
    • Solution by Erubis ‣ Enhance embedded pattern and Erubis class • Fast, and easy to implement eruby = Erubis::Eruby.new(str, :escape=>true) # or eruby = Erubis::EscapedEruby.new(str) puts eruby.evaluate(ctx) Hi <%= @name %>! # with escape Hi <%== @name %>! # without escape copyright(c) 2009 kuwata-lab.com all rights reserved. 36
    • Issue : hard to find syntax error <% unless @items.blank? %> <table> <tbody> <% @items.each do |item| %> <tr class="item" id="item-<%=item.id%>"> <td class="item-id"><%= item.id %></td> <td class="item-name"> <% if item.url && !item.url.empty? %> <a href="<%= item.url %>"><%=item.name%></a> <% else %> <span><%=item.name%></span> <% end %> </td> </tr> • HTML and Ruby code are mixed <% end %> • It is hard to recognize corresponding </tbody> </table> 'end' (because 'do' and 'end' can be <% end %> separated 100 lines for example) copyright(c) 2009 kuwata-lab.com all rights reserved. 37
    • Solution by ERB ‣ Nothing but '-x' option $ erb -x foo.eruby _erbout = ''; unless @items.blank? ; _erbout.concat "n" _erbout.concat "<table>n" _erbout.concat " <tr class="record">n" $ erb -x foo.eruby | ruby -wc Syntax OK copyright(c) 2009 kuwata-lab.com all rights reserved. 38
    • Solution by Erubis ‣ Provides a lot of command-line options • -x : show Ruby script • -X : suppress to print HTML • -N : print line numbers • -U : unify consecutive empty lines into a line • -C : remove consecutive empty lines (compact) • -z : check template syntax copyright(c) 2009 kuwata-lab.com all rights reserved. 39
    • $ cat foo.eruby <% unless @items.blank? %> <table> <% @items.each_with_index do|x, i| %> <tr class="record"> <td><%= i +1 %></td> <td><%=h x %></td> </tr> <% end %> </table> <% end %> copyright(c) 2009 kuwata-lab.com all rights reserved. 40
    • -x : show Ruby script $ erubis -x foo.eruby _buf = ''; unless @items.blank? _buf << '<table> '; @items.each_with_index do|x, i| _buf << ' <tr class="record"> <td>'; _buf << ( i +1 ).to_s; _buf << '</td> <td>'; _buf << (h x ).to_s; _buf << '</td> </tr> '; end _buf << '</table> '; end _buf.to_s copyright(c) 2009 kuwata-lab.com all rights reserved. 41
    • -X : suppress to print HTML $ erubis -X foo.eruby _buf = ''; unless @items.blank? @items.each_with_index do|x, i| _buf << ( i +1 ).to_s; _buf << (h x ).to_s; end end _buf.to_s copyright(c) 2009 kuwata-lab.com all rights reserved. 42
    • -N : print line numbers $ erubis -XN foo.eruby 1: _buf = ''; unless @items.blank? 2: 3: @items.each_with_index do|x, i| 4: 5: _buf << ( i +1 ).to_s; 6: _buf << (h x ).to_s; 7: 8: end 9: 10: end 11: _buf.to_s copyright(c) 2009 kuwata-lab.com all rights reserved. 43
    • -U : unifiy consecutive empty lines into a line $ erubis -XNU foo.eruby 1: _buf = ''; unless @items.blank? 3: @items.each_with_index do|x, i| 5: _buf << ( i +1 ).to_s; 6: _buf << (h x ).to_s; 8: end 10: end 11: _buf.to_s copyright(c) 2009 kuwata-lab.com all rights reserved. 44
    • -C : remove empty lines (compact) $ erubis -XNC foo.eruby 1: _buf = ''; unless @items.blank? 3: @items.each_with_index do|x, i| 5: _buf << ( i +1 ).to_s; 6: _buf << (h x ).to_s; 8: end 10: end 11: _buf.to_s copyright(c) 2009 kuwata-lab.com all rights reserved. 45
    • Issue : expr can contain statements embed return value of helper method by <%= %> block contains statements <%= form_for :user do %> <div> <%= text_field :name %> </div> <% end %> beyond of eRuby spec! copyright(c) 2009 kuwata-lab.com all rights reserved. 46
    • Cause of Issue <%= 10.times do %> Hello <%= expr %> is expected <% end %> to be completed by itself Convert Syntax error! _buf = ""; _buf << ( 10.times do ).to_s; _buf << " Hellon"; end copyright(c) 2009 kuwata-lab.com all rights reserved. 47
    • Solution by ERB+Rails ‣ Change local variable (_erbout) on caller- size from callee-side agic!! b lack m Append to '_erbout' from Not use <%= %> internal of form_for() <% form_for do %> _erbout = "" Hello form_for do <% end %> _erbout.concat("Hello") end copyright(c) 2009 kuwata-lab.com all rights reserved. 48
    • Solution by Erubis+Merb ‣ Extend parser of Erubis Recognize blok in embedded expr <%= form_for do %> @_buf << (form_for do; Hello @_buf << "Hellon" <% end =%> end); Introduce end-of- Change _buf into block notation instance variable copyright(c) 2009 kuwata-lab.com all rights reserved. 49
    • Discussion ‣ Extend spec of eRuby ‣ Not use black magic (kool!) ‣ Available only for helper method, in fact • It is required to manipulate @_buf from internal of helper method ‣ Difficult to provide general solution copyright(c) 2009 kuwata-lab.com all rights reserved. 50
    • Conslusion ‣ A lot of issues around eRuby! • Extra line breaks • Local variables are not local • Difficult to specify context variable • Large cost for convertion and parsing • HTML escape in default • Difficult to find syntax error • Can't embed return value of method with block copyright(c) 2009 kuwata-lab.com all rights reserved. 51
    • Part 3. Future of template system copyright(c) 2009 kuwata-lab.com all rights reserved. 52
    • Template and Programming ‣ Template is also program code <ul> print "<ul>n" <% for x in @a %> for x in @a <li><%=x%></li> Equiv. print "<li>#{x}</li>n" <% end %> end </ul> print "</u>n" Possible to apply programming techniques or concepts to template system copyright(c) 2009 kuwata-lab.com all rights reserved. 53
    • Template and Method Template is a kind of method definition <ul> s = File.read('foo.eruby') <li><%=x%></li> e = Erubis::Eruby.new(s) </ul> puts e.evaluate(:x=>1) Context data is actual Rendering template is a argument for method kind of method invokation copyright(c) 2009 kuwata-lab.com all rights reserved. 54
    • Template and formal argument ‣ Formal arguments may be necessary for template <%#ARGS: items, name='guest' %> Hello <%= name %>! <% for x in items %> <li><%=x%></li> • Clear context variables <% end %> • Available default value copyright(c) 2009 kuwata-lab.com all rights reserved. 55
    • Template and modularity ‣ Also HTML should be Small & Many, not Single & Large • Same as principle of method definition <html> <html> Be benefit for <body> designer! <body> </body> <h1><%=@title%></h1> </html> <ul id="menulist"> split <% for x in @items %> <h1><%=@title%></h1> <li><%=x%></li> <ul id="menulist"> </ul> <% end %> </ul> <% for x in @items %> </body> <li><%= x %></li> </html> <% end %> copyright(c) 2009 kuwata-lab.com all rights reserved. 56
    • Template and Object-Oriented ‣ Template inheritance in Django Parent template .... Available to overwrite {% block pagetitle %} or add contents <h1>{{title}}</h1> before/after {% endblock %} (method override) .... copyright(c) 2009 kuwata-lab.com all rights reserved. 57
    • Template and Aspect-Oriented ‣ Weave some code into other program • Similar to layer of Photoshop <table> "for x in @a" •Enable to split HTML <tr> and presentation <td> "print x" logics •Available to insert a </tr> logic into several "end" points (DRY) </table> copyright(c) 2009 kuwata-lab.com all rights reserved. 58
    • Template and Data Type ‣ End is coming to escape HTML in view layer • forget to escape, helper method argument, ... ‣ HTML should be different data type from String (http://www.oiwa.jp/~yutaka/tdiary/20051229.html) • No need to take care to escape or not • Prior art : str and unicode in Python • "HTML + String" should be String? or HTML? • Other escaping also should be considered (ex. SQL) copyright(c) 2009 kuwata-lab.com all rights reserved. 59
    • Conslusion ‣ Template is also programming code ‣ Available to apply programming techniques and concepts into template system • Formal argument, Inheritance, AOP, and so on ‣ Template system is still on developing stage copyright(c) 2009 kuwata-lab.com all rights reserved. 60
    • Bibliography ‣ Introduction to Template System (in Japanese) • http://jp.rubyist.net/magazine/?0024-TemplateSystem • http://jp.rubyist.net/magazine/?0024-TemplateSystem2 ‣ Erubis • http://www.kuwata-lab.com/erubis/ ‣ Benchmarks of many template systems • http://www.kuwata-lab.com/tenjin/ copyright(c) 2009 kuwata-lab.com all rights reserved. 61
    • one more thing copyright(c) 2009 kuwata-lab.com all rights reserved. 62
    • Tenjin - template engine replacing eRuby ‣ Both ERB and Erubis are out of date • They are merely text-processor • Less features as template engine ‣ Tenjin : replacer of ERB/Erubis • Designed and implemented as template engine from the beginning • Provides a lot of features required for template engines - layout template, partial template, and so on • http://www.kuwata-lab.com/tenjin/ copyright(c) 2009 kuwata-lab.com all rights reserved. 63
    • thank you copyright(c) 2009 kuwata-lab.com all rights reserved. 64