Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ERB, ancient and future

ERB, ancient and future

ERB, ancient and future
RubyKaigi 2024 Day3

seki at druby.org

May 17, 2024
Tweet

More Decks by seki at druby.org

Other Decks in Programming

Transcript

  1. About me Masatoshi Seki Ruby Core Committer (dRuby, Rinda, ERB)

    toRuby / Tochigi Ruby Meetup Pokémon TCG, WCS 2010 Tochigi Pref. winner 2
  2. About me Masatoshi Seki Ruby Core Committer (dRuby, Rinda, ERB)

    toRuby / Tochigi Ruby Meetup Pokémon TCG, WCS 2010 Tochigi Pref. winner 40年くらいプログラマだよ 3
  3. About me Masatoshi Seki Ruby Core Committer (dRuby, Rinda, ERB)

    toRuby / Tochigi Ruby Meetup Pokémon TCG, WCS 2010 Tochigi Pref. winner Complex embedded systems with concurrency and GUI 4 Gallstones
  4. About me Masatoshi Seki Ruby Core Committer (dRuby, Rinda, ERB)

    toRuby / Tochigi Ruby Meetup Pokémon TCG, WCS 2010 Tochigi Pref. winner I wrote them in 1999-2000 5
  5. About me Masatoshi Seki Ruby Core Committer (dRuby, Rinda, ERB)

    toRuby / Tochigi Ruby Meetup Pokémon TCG, WCS 2010 Tochigi Pref. winner I wrote them in 1999-2000 6
  6. About me Masatoshi Seki Ruby Core Committer (dRuby, Rinda, ERB)

    toRuby / Tochigi Ruby Meetup Pokémon TCG, WCS 2010 Tochigi Pref. winner 7 Makoto Inoue Web Developer @ New Bamboo (UK) Translator of “The dRuby Book” Rails is A Follower what we can learn from dRuby’s metaprogramming magic Masatoshi SEKI Makoto Inoue
  7. About me Masatoshi Seki Ruby Core Committer (dRuby, Rinda, ERB)

    toRuby / Tochigi Ruby Meetup Pokémon TCG, WCS 2010 Tochigi Pref. winner 8 Big Bird. (scaling twitter) Rinda • Shared Queue (TupleSpace) • Built with DRb • RingyDingy makes it stupid easy • See Eric Hodel’s documentation • O(N) for take(). Sigh.
  8. About me Masatoshi Seki Ruby Core Committer (dRuby, Rinda, ERB)

    toRuby / Tochigi Ruby Meetup Pokémon TCG, WCS 2010 Tochigi Pref. winner 9
  9. About me Masatoshi Seki Ruby Core Committer (dRuby, Rinda, ERB)

    toRuby / Tochigi Ruby Meetup Pokémon TCG, WCS 2010 Tochigi Pref. winner 第205回toRubyは2024-06-05 10 情報デザインのロゴ
  10. About me Masatoshi Seki Ruby Core Committer (dRuby, Rinda, ERB)

    toRuby / Tochigi Ruby Meetup Pokémon TCG, WCS 2010 Tochigi Pref. winner RubyKaigi 2022, 2023 11
  11. Before ERB When I posted a template engine that replaces

    &entity;, Shugo told me about his plans for eRuby. ruby-dev:5286 (1999-02-19) Re: htmlelem.rb (Re: HTML generator) 16 1999
  12. eRuby specs ePerlの影響を強くうけているみたいだぞ 17 print ≒ <%= .. %> !?

    I'm not the one implementing it (Tsundere) ePerl! ? Shugo CEO was a part-time worker at the time. 1999
  13. Hello, World. 21 <p> <% print "Hello, World!" %> <%=

    "Hello, World! %> </p> print "<p>\n" print "Hello, World!" print "Hello, World!" print "</p>\n"
  14. control structure 22 <ul> <% ENV.each do |row| %> <li><%=

    row[0] %></li> <% end %> </ul> print "<ul>\n" ENV.each do |row| print "<li>"; print row[0]; print "</li>\n" end print "</ul>\n"
  15. Remember CGI? $stdout → HTTP response CGI・おぼえていますか 23 puts %Q{Content-Type:

    text/html} puts puts %Q{<!DOCTYPE html>} puts %Q{<html lang="ja">} puts %Q{<head>} puts %Q{</head>} puts %Q{<body>} puts %Q{<ul>} ENV.each do |row| puts %Q!<li>#{row[0]}</li>! end puts %Q{</ul>} puts %Q{</body>} puts %Q{</html>} Content-Type: text/html <!DOCTYPE html> <html lang="ja"> <head> </head> <body> <ul> <li>TERM_PROGRAM</li> ... </ul> </body> </html>
  16. Remember CGI? $stdout → HTTP response CGI・おぼえていますか 24 puts %Q{Content-Type:

    text/html} puts puts %Q{<!DOCTYPE html>} puts %Q{<html lang="ja">} puts %Q{<head>} puts %Q{</head>} puts %Q{<body>} puts %Q{<ul>} ENV.each do |row| puts %Q!<li>#{row[0]}</li>! end puts %Q{</ul>} puts %Q{</body>} puts %Q{</html>} Content-Type: text/html <!DOCTYPE html> <html lang="ja"> <head> </head> <body> <ul> <li>TERM_PROGRAM</li> ... </ul> </body> </html> puts puts puts
  17. Rewrite puts to eRuby Stringの式展開では制御構造が書きにくい(書ける?)のだがeRubyならかんたん 25 puts %Q{<!DOCTYPE html>} puts

    %Q{<html lang="ja">} puts %Q{<head>} puts %Q{</head>} puts %Q{<body>} puts %Q{<ul>} ENV.each do |row| puts %Q!<li>#{row[0]}</li>! end puts %Q{</ul>} puts %Q{</body>} puts %Q{</html>} <!DOCTYPE html> <html lang="ja"> <head> </head> <body> <ul> <% ENV.each do |row| %> <li><%= row[0] %></li> <% end %> </ul> </body> </html>
  18. This specification is not good CGI programming style will change

    in the near future Page generation becomes more complex Will change to something more server-like 26 1999
  19. becomes more complex Part of the page will be generated

    by a method Helper? 27 <h2>report hoge</h2> <ul> <% result.each do |row| %> <li><%= format_date(row.date) %></li> <% end %> </ul> def format_date(date) date.strftime("%Y-%m-%d") end ERB conjecture 1999
  20. becomes more complex nest common header, navigation, footer and content

    標準出力に出すよりも連結した方がよいのでは? 28 ERB conjecture <%= header %> <%= navi %> <%= content %> <%= footer %> 1999
  21. CGI programming model will change A long-lived server is required

    due to preprocessing costs and information exchange between requests. e.g. dRuby + ERB Concurrency and $stdout will conflict 29 ERB conjecture 1999
  22. ERb and ERbLight ERb - eRuby's spec, like a command

    ERbLight - eRuby w/o $stdout, String concatenation a library, build the page using String instead of puts() Support writing Ruby applications using eRuby notation 30 1999
  23. Ruby x 256 book Legendary hacker explains the power of

    this specification 2001, ASCII たださんとartonさんがeRuby/dRubyを絶賛するすごい本 31 2001
  24. renamed ERbLight to ERB And ERB was included in Ruby's

    standard library. ERb is obsolete ERb is obsolete 32 2003
  25. 25 years have passed almost correct ! ERB-inspired libraries make

    me happy Merb, Rails... 自分用のライブラリは自分で書いてるけど、そういうものがあるのはうれしい 33 2024 ERB conjecture
  26. Usage ERB.new, ERB#result 34 >> ERB.new('Hello World').result => "Hello World"

    >> ERB.new('1 + 1 = <%= 1 + 1 %>').result => "1 + 1 = 2"
  27. ERB#src Translate eRuby to Ruby _erboutという変数に結果を連結する 35 <ul> <% list.each

    do |row| %> <li><%= row %></li> <% end %> </ul> >> puts erb.src #coding:UTF-8 _erbout = +''; _erbout.<< "<ul>\n".freeze ; list.each do |row| ; _erbout.<< "\n<li>".freeze ; _erbout.<<(( row ).to_s); _erbout.<< "</li>\n".freeze ; end ; _erbout.<< "\n</ul>\n".freeze ; _erbout
  28. ERB#result with binding binding 36 <ul> <% list.each do |row|

    %> <li><%= row %></li> <% end %> </ul> >> erb = ERB.new(erb_src) >> list = [1, 'two', 3.0] >> erb.result(binding) => "<ul>\n\n<li>1</ li>\n\n<li>two</li>\n\n<li>3.0</ li>\n\n</ul>\n"
  29. ERB#def_method def_method(mod, methodname, fname='(ERB)') 37 class Foo def initialize(arg) @list

    = arg end ERB.new(<<EOS).def_method(self, 'bar') <ul> <% @list.each do |row| %> <li><%= row %></li> <% end %> </ul> EOS end Foo#bar instance var unpopular features
  30. ERB#def_method Avoid parsing the source multiple times Compose with small

    template objects Used only in Ikezawa's products 38 unpopular features
  31. Proc in template Case study: Create a survey page with

    similar questions repeatedly Each question has a large table with an answer column Element IDs and form names must be unique Write in one template ↑矢板市北部で一番Herokuを使ってる「情報デザイン」のロゴ 39 Q A Q A Q A Q A Q A
  32. Proc in template 43 <% t = Proc.new do |arg|

    %> <h3>Answer column for <%= arg %></h3> <table id="table-<%= arg %>"> ... </table> <% end %> <h3>Question 1</h3> this is the first question <% t.call("q1") %> <h3>Question 2</h3> That's the second question <% t.call("q2") %> <h3>Question 1</h3> this is the first question <h3>Answer column for q1</h3> <table id="table-q1"> ... </table> <h3>Question 2</h3> That's the second question <h3>Answer column for q2</h3> <table id="table-q2"> ... </table> call
  33. Proc in template いやなことを思い出した 44 <% t = Proc.new do

    |arg| %> <h3>Answer column for <%= arg %></h3> <table id="table-<%= arg %>"> ... </table> <% end %> <h3>Question 1</h3> this is the first question <% t.call("q1") %> <h3>Question 2</h3> That's the second question <% t.call("q2") %> <h3>Question 1</h3> this is the first question <h3>Answer column for q1</h3> <table id="table-q1"> ... </table> <h3>Question 2</h3> That's the second question <h3>Answer column for q2</h3> <table id="table-q2"> ... </table> call
  34. I tried やりましょう 47 I noticed this 2 years ago,

    ! And asked Seki-san if we could extend ERB to be "Rails compatible" before Ruby 2.0 release 2012
  35. and gave up できませんでした 48 But gave up. ! Because

    he didn't like this part in ActionView + Erubis ! BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/ ! This code scans the template with the Regexp and detects the Ruby block, but this kind of code could be imperfect ! So this is not acceptable as an ERB spec, said Seki- san.
  36. Maybe it's solved I noticed this while writing the slides

    for RWC2023. https://gist.github.com/seki/610a42932a85209aaa33547ae983bbdf 50 2023
  37. <%= ... %> to_s 51 <%= 1i ** 0.5 %>

    #coding:UTF-8 _erbout = +''; _erbout.<<(( 1i ** 0.5 ).to_s); _erbout.<< "\n".freeze ; _erbout
  38. <%= ... %> with block こうなっちゃう 53 <%= form_with do

    %> ... <% end %> #coding:UTF-8 _erbout = +''; _erbout.<<(( form_with do ).to_s); _erbout.<< "\n ... \n".freeze ; end ; _erbout.<< "\n".freeze ; _erbout
  39. (( ... ).to_s) ((...).to_s)の括弧がじゃまなのでバッファクラスへ移動すればよい? 54 <%= form_with do %> ...

    <% end %> #coding:UTF-8 _erbout = +''; _erbout.<<(( form_with do ).to_s); _erbout.<< "\n ... \n".freeze ; end ; _erbout.<< "\n".freeze ; _erbout
  40. Customizable Rubyへの変換方法はカスタマイズできる 55 <%= form_with do %> ... <% end

    %> #coding:UTF-8 _erbout = +''; _erbout.<<(( form_with do ).to_s); _erbout.<< "\n ... \n".freeze ; end ; _erbout.<< "\n".freeze ; _erbout
  41. Ruby is difficult Rubyむずかしい 56 _erbout << h("str") # OK

    _erbout << h "str" # SyntaxError <<と括弧なし のメソッド呼び 出し
  42. Ruby is difficult Rubyむずかしい 57 _erbout << h("str") # OK

    _erbout << h "str" # SyntaxError _erbout.concat h "str" # OK but .... _erbout.concat form_with "arg" do |arg| # NG ... # end # concatʹϒϩοΫ͕౉Δ <<と括弧なし のメソッド呼び 出し <<でなくふつうのメソッド呼び出しにしても このブロックはconcatのブロック引数になる
  43. Ruby is difficult Rubyむずかしい 58 _erbout << h("str") # OK

    _erbout << h "str" # SyntaxError _erbout = h "str" # _erbout = form_with "arg" do |arg| # ... end _erbout.concat h "str" # OK but .... _erbout.concat form_with "arg" do |arg| # NG ... # end # concatʹϒϩοΫ͕౉Δ 代入なら大丈夫なのに... <<と括弧なし のメソッド呼び 出し <<でなくふつうのメソッド呼び出しにしても このブロックはconcatのブロック引数になる
  44. ERBOut result buffer 60 class ERBOut Buffer = String #

    SafeBuffer if rails def initialize(s='') @str = Buffer.new(s) end def to_s @str end def <<(other) @str << other end def +(other) @str << other.to_s self end def capture(*arg, &block) save = @str @str = Buffer.new yield(*arg) return @str ensure @str = save end end
  45. ERBOut result buffer 61 class ERBOut Buffer = String #

    SafeBuffer if rails def initialize(s='') @str = Buffer.new(s) end def to_s @str end def <<(other) @str << other end def +(other) @str << other.to_s self end def capture(*arg, &block) save = @str @str = Buffer.new yield(*arg) return @str ensure @str = save end end
  46. ERBOut result buffer many tricks + 62 class ERBOut Buffer

    = String # SafeBuffer if rails def initialize(s='') @str = Buffer.new(s) end def to_s @str end def <<(other) @str << other end def +(other) @str << other.to_s self end def capture(*arg, &block) save = @str @str = Buffer.new yield(*arg) return @str ensure @str = save end end normalize and concatenate return itself
  47. ERBOut result buffer many tricks + capture 63 class ERBOut

    Buffer = String # SafeBuffer if rails def initialize(s='') @str = Buffer.new(s) end def to_s @str end def <<(other) @str << other end def +(other) @str << other.to_s self end def capture(*arg, &block) save = @str @str = Buffer.new yield(*arg) return @str ensure @str = save end end save restore call block and capture
  48. なんかできちゃった 64 <%= form_with do %> ... <% end %>

    #coding:UTF-8 _erbout = ERB::ERBOut.new; _erbout += form_with do ; _erbout << "\n ...\n".freeze ; end ; _erbout << "\n".freeze ; _erbout.to_s result buffer addition assignment
  49. I want someone to make it work in a Rails

    environment. I made no progress since last year 進捗ダメです 66