enhancer.rb 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  1. ##
  2. ## $Release: 2.7.0 $
  3. ## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
  4. ##
  5. module Erubis
  6. ##
  7. ## switch '<%= ... %>' to escaped and '<%== ... %>' to unescaped
  8. ##
  9. ## ex.
  10. ## class XmlEruby < Eruby
  11. ## include EscapeEnhancer
  12. ## end
  13. ##
  14. ## this is language-indenedent.
  15. ##
  16. module EscapeEnhancer
  17. def self.desc # :nodoc:
  18. "switch '<%= %>' to escaped and '<%== %>' to unescaped"
  19. end
  20. #--
  21. #def self.included(klass)
  22. # klass.class_eval <<-END
  23. # alias _add_expr_literal add_expr_literal
  24. # alias _add_expr_escaped add_expr_escaped
  25. # alias add_expr_literal _add_expr_escaped
  26. # alias add_expr_escaped _add_expr_literal
  27. # END
  28. #end
  29. #++
  30. def add_expr(src, code, indicator)
  31. case indicator
  32. when '='
  33. @escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
  34. when '=='
  35. @escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
  36. when '==='
  37. add_expr_debug(src, code)
  38. end
  39. end
  40. end
  41. #--
  42. ## (obsolete)
  43. #module FastEnhancer
  44. #end
  45. #++
  46. ##
  47. ## use $stdout instead of string
  48. ##
  49. ## this is only for Eruby.
  50. ##
  51. module StdoutEnhancer
  52. def self.desc # :nodoc:
  53. "use $stdout instead of array buffer or string buffer"
  54. end
  55. def add_preamble(src)
  56. src << "#{@bufvar} = $stdout;"
  57. end
  58. def add_postamble(src)
  59. src << "\n''\n"
  60. end
  61. end
  62. ##
  63. ## use print statement instead of '_buf << ...'
  64. ##
  65. ## this is only for Eruby.
  66. ##
  67. module PrintOutEnhancer
  68. def self.desc # :nodoc:
  69. "use print statement instead of '_buf << ...'"
  70. end
  71. def add_preamble(src)
  72. end
  73. def add_text(src, text)
  74. src << " print '#{escape_text(text)}';" unless text.empty?
  75. end
  76. def add_expr_literal(src, code)
  77. src << " print((#{code}).to_s);"
  78. end
  79. def add_expr_escaped(src, code)
  80. src << " print #{escaped_expr(code)};"
  81. end
  82. def add_postamble(src)
  83. src << "\n" unless src[-1] == ?\n
  84. end
  85. end
  86. ##
  87. ## enable print function
  88. ##
  89. ## Notice: use Eruby#evaluate() and don't use Eruby#result()
  90. ## to be enable print function.
  91. ##
  92. ## this is only for Eruby.
  93. ##
  94. module PrintEnabledEnhancer
  95. def self.desc # :nodoc:
  96. "enable to use print function in '<% %>'"
  97. end
  98. def add_preamble(src)
  99. src << "@_buf = "
  100. super
  101. end
  102. def print(*args)
  103. args.each do |arg|
  104. @_buf << arg.to_s
  105. end
  106. end
  107. def evaluate(context=nil)
  108. _src = @src
  109. if context.is_a?(Hash)
  110. context.each do |key, val| instance_variable_set("@#{key}", val) end
  111. elsif context
  112. context.instance_variables.each do |name|
  113. instance_variable_set(name, context.instance_variable_get(name))
  114. end
  115. end
  116. return instance_eval(_src, (@filename || '(erubis)'))
  117. end
  118. end
  119. ##
  120. ## return array instead of string
  121. ##
  122. ## this is only for Eruby.
  123. ##
  124. module ArrayEnhancer
  125. def self.desc # :nodoc:
  126. "return array instead of string"
  127. end
  128. def add_preamble(src)
  129. src << "#{@bufvar} = [];"
  130. end
  131. def add_postamble(src)
  132. src << "\n" unless src[-1] == ?\n
  133. src << "#{@bufvar}\n"
  134. end
  135. end
  136. ##
  137. ## use an Array object as buffer (included in Eruby by default)
  138. ##
  139. ## this is only for Eruby.
  140. ##
  141. module ArrayBufferEnhancer
  142. def self.desc # :nodoc:
  143. "use an Array object for buffering (included in Eruby class)"
  144. end
  145. def add_preamble(src)
  146. src << "_buf = [];"
  147. end
  148. def add_postamble(src)
  149. src << "\n" unless src[-1] == ?\n
  150. src << "_buf.join\n"
  151. end
  152. end
  153. ##
  154. ## use String class for buffering
  155. ##
  156. ## this is only for Eruby.
  157. ##
  158. module StringBufferEnhancer
  159. def self.desc # :nodoc:
  160. "use a String object for buffering"
  161. end
  162. def add_preamble(src)
  163. src << "#{@bufvar} = '';"
  164. end
  165. def add_postamble(src)
  166. src << "\n" unless src[-1] == ?\n
  167. src << "#{@bufvar}.to_s\n"
  168. end
  169. end
  170. ##
  171. ## use StringIO class for buffering
  172. ##
  173. ## this is only for Eruby.
  174. ##
  175. module StringIOEnhancer # :nodoc:
  176. def self.desc # :nodoc:
  177. "use a StringIO object for buffering"
  178. end
  179. def add_preamble(src)
  180. src << "#{@bufvar} = StringIO.new;"
  181. end
  182. def add_postamble(src)
  183. src << "\n" unless src[-1] == ?\n
  184. src << "#{@bufvar}.string\n"
  185. end
  186. end
  187. ##
  188. ## set buffer variable name to '_erbout' as well as '_buf'
  189. ##
  190. ## this is only for Eruby.
  191. ##
  192. module ErboutEnhancer
  193. def self.desc # :nodoc:
  194. "set '_erbout = _buf = \"\";' to be compatible with ERB."
  195. end
  196. def add_preamble(src)
  197. src << "_erbout = #{@bufvar} = '';"
  198. end
  199. def add_postamble(src)
  200. src << "\n" unless src[-1] == ?\n
  201. src << "#{@bufvar}.to_s\n"
  202. end
  203. end
  204. ##
  205. ## remove text and leave code, especially useful when debugging.
  206. ##
  207. ## ex.
  208. ## $ erubis -s -E NoText file.eruby | more
  209. ##
  210. ## this is language independent.
  211. ##
  212. module NoTextEnhancer
  213. def self.desc # :nodoc:
  214. "remove text and leave code (useful when debugging)"
  215. end
  216. def add_text(src, text)
  217. src << ("\n" * text.count("\n"))
  218. if text[-1] != ?\n
  219. text =~ /^(.*?)\z/
  220. src << (' ' * $1.length)
  221. end
  222. end
  223. end
  224. ##
  225. ## remove code and leave text, especially useful when validating HTML tags.
  226. ##
  227. ## ex.
  228. ## $ erubis -s -E NoCode file.eruby | tidy -errors
  229. ##
  230. ## this is language independent.
  231. ##
  232. module NoCodeEnhancer
  233. def self.desc # :nodoc:
  234. "remove code and leave text (useful when validating HTML)"
  235. end
  236. def add_preamble(src)
  237. end
  238. def add_postamble(src)
  239. end
  240. def add_text(src, text)
  241. src << text
  242. end
  243. def add_expr(src, code, indicator)
  244. src << "\n" * code.count("\n")
  245. end
  246. def add_stmt(src, code)
  247. src << "\n" * code.count("\n")
  248. end
  249. end
  250. ##
  251. ## get convert faster, but spaces around '<%...%>' are not trimmed.
  252. ##
  253. ## this is language-independent.
  254. ##
  255. module SimplifyEnhancer
  256. def self.desc # :nodoc:
  257. "get convert faster but leave spaces around '<% %>'"
  258. end
  259. #DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
  260. SIMPLE_REGEXP = /<%(=+|\#)?(.*?)-?%>/m
  261. def convert(input)
  262. src = ""
  263. add_preamble(src)
  264. #regexp = pattern_regexp(@pattern)
  265. pos = 0
  266. input.scan(SIMPLE_REGEXP) do |indicator, code|
  267. match = Regexp.last_match
  268. index = match.begin(0)
  269. text = input[pos, index - pos]
  270. pos = match.end(0)
  271. add_text(src, text)
  272. if !indicator # <% %>
  273. add_stmt(src, code)
  274. elsif indicator[0] == ?\# # <%# %>
  275. n = code.count("\n")
  276. add_stmt(src, "\n" * n)
  277. else # <%= %>
  278. add_expr(src, code, indicator)
  279. end
  280. end
  281. #rest = $' || input # ruby1.8
  282. rest = pos == 0 ? input : input[pos..-1] # ruby1.9
  283. add_text(src, rest)
  284. add_postamble(src)
  285. return src
  286. end
  287. end
  288. ##
  289. ## enable to use other embedded expression pattern (default is '\[= =\]').
  290. ##
  291. ## notice! this is an experimental. spec may change in the future.
  292. ##
  293. ## ex.
  294. ## input = <<END
  295. ## <% for item in list %>
  296. ## <%= item %> : <%== item %>
  297. ## [= item =] : [== item =]
  298. ## <% end %>
  299. ## END
  300. ##
  301. ## class BiPatternEruby
  302. ## include BiPatternEnhancer
  303. ## end
  304. ## eruby = BiPatternEruby.new(input, :bipattern=>'\[= =\]')
  305. ## list = ['<a>', 'b&b', '"c"']
  306. ## print eruby.result(binding())
  307. ##
  308. ## ## output
  309. ## <a> : &lt;a&gt;
  310. ## <a> : &lt;a&gt;
  311. ## b&b : b&amp;b
  312. ## b&b : b&amp;b
  313. ## "c" : &quot;c&quot;
  314. ## "c" : &quot;c&quot;
  315. ##
  316. ## this is language independent.
  317. ##
  318. module BiPatternEnhancer
  319. def self.desc # :nodoc:
  320. "another embedded expression pattern (default '\[= =\]')."
  321. end
  322. def initialize(input, properties={})
  323. self.bipattern = properties[:bipattern] # or '\$\{ \}'
  324. super
  325. end
  326. ## when pat is nil then '\[= =\]' is used
  327. def bipattern=(pat) # :nodoc:
  328. @bipattern = pat || '\[= =\]'
  329. pre, post = @bipattern.split()
  330. @bipattern_regexp = /(.*?)#{pre}(=*)(.*?)#{post}/m
  331. end
  332. def add_text(src, text)
  333. return unless text
  334. m = nil
  335. text.scan(@bipattern_regexp) do |txt, indicator, code|
  336. m = Regexp.last_match
  337. super(src, txt)
  338. add_expr(src, code, '=' + indicator)
  339. end
  340. #rest = $' || text # ruby1.8
  341. rest = m ? text[m.end(0)..-1] : text # ruby1.9
  342. super(src, rest)
  343. end
  344. end
  345. ##
  346. ## regards lines starting with '^[ \t]*%' as program code
  347. ##
  348. ## in addition you can specify prefix character (default '%')
  349. ##
  350. ## this is language-independent.
  351. ##
  352. module PrefixedLineEnhancer
  353. def self.desc # :nodoc:
  354. "regard lines matched to '^[ \t]*%' as program code"
  355. end
  356. def init_generator(properties={})
  357. super
  358. @prefixchar = properties[:prefixchar]
  359. end
  360. def add_text(src, text)
  361. unless @prefixrexp
  362. @prefixchar ||= '%'
  363. @prefixrexp = Regexp.compile("^([ \\t]*)\\#{@prefixchar}(.*?\\r?\\n)")
  364. end
  365. pos = 0
  366. text2 = ''
  367. text.scan(@prefixrexp) do
  368. space = $1
  369. line = $2
  370. space, line = '', $1 unless $2
  371. match = Regexp.last_match
  372. len = match.begin(0) - pos
  373. str = text[pos, len]
  374. pos = match.end(0)
  375. if text2.empty?
  376. text2 = str
  377. else
  378. text2 << str
  379. end
  380. if line[0, 1] == @prefixchar
  381. text2 << space << line
  382. else
  383. super(src, text2)
  384. text2 = ''
  385. add_stmt(src, space + line)
  386. end
  387. end
  388. #rest = pos == 0 ? text : $' # ruby1.8
  389. rest = pos == 0 ? text : text[pos..-1] # ruby1.9
  390. unless text2.empty?
  391. text2 << rest if rest
  392. rest = text2
  393. end
  394. super(src, rest)
  395. end
  396. end
  397. ##
  398. ## regards lines starting with '%' as program code
  399. ##
  400. ## this is for compatibility to eruby and ERB.
  401. ##
  402. ## this is language-independent.
  403. ##
  404. module PercentLineEnhancer
  405. include PrefixedLineEnhancer
  406. def self.desc # :nodoc:
  407. "regard lines starting with '%' as program code"
  408. end
  409. #--
  410. #def init_generator(properties={})
  411. # super
  412. # @prefixchar = '%'
  413. # @prefixrexp = /^\%(.*?\r?\n)/
  414. #end
  415. #++
  416. def add_text(src, text)
  417. unless @prefixrexp
  418. @prefixchar = '%'
  419. @prefixrexp = /^\%(.*?\r?\n)/
  420. end
  421. super(src, text)
  422. end
  423. end
  424. ##
  425. ## [experimental] allow header and footer in eRuby script
  426. ##
  427. ## ex.
  428. ## ====================
  429. ## ## without header and footer
  430. ## $ cat ex1.eruby
  431. ## <% def list_items(list) %>
  432. ## <% for item in list %>
  433. ## <li><%= item %></li>
  434. ## <% end %>
  435. ## <% end %>
  436. ##
  437. ## $ erubis -s ex1.eruby
  438. ## _buf = []; def list_items(list)
  439. ## ; for item in list
  440. ## ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
  441. ## '; end
  442. ## ; end
  443. ## ;
  444. ## _buf.join
  445. ##
  446. ## ## with header and footer
  447. ## $ cat ex2.eruby
  448. ## <!--#header:
  449. ## def list_items(list)
  450. ## #-->
  451. ## <% for item in list %>
  452. ## <li><%= item %></li>
  453. ## <% end %>
  454. ## <!--#footer:
  455. ## end
  456. ## #-->
  457. ##
  458. ## $ erubis -s -c HeaderFooterEruby ex4.eruby
  459. ##
  460. ## def list_items(list)
  461. ## _buf = []; _buf << '
  462. ## '; for item in list
  463. ## ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
  464. ## '; end
  465. ## ; _buf << '
  466. ## ';
  467. ## _buf.join
  468. ## end
  469. ##
  470. ## ====================
  471. ##
  472. ## this is language-independent.
  473. ##
  474. module HeaderFooterEnhancer
  475. def self.desc # :nodoc:
  476. "allow header/footer in document (ex. '<!--#header: #-->')"
  477. end
  478. HEADER_FOOTER_PATTERN = /(.*?)(^[ \t]*)?<!--\#(\w+):(.*?)\#-->([ \t]*\r?\n)?/m
  479. def add_text(src, text)
  480. m = nil
  481. text.scan(HEADER_FOOTER_PATTERN) do |txt, lspace, word, content, rspace|
  482. m = Regexp.last_match
  483. flag_trim = @trim && lspace && rspace
  484. super(src, txt)
  485. content = "#{lspace}#{content}#{rspace}" if flag_trim
  486. super(src, lspace) if !flag_trim && lspace
  487. instance_variable_set("@#{word}", content)
  488. super(src, rspace) if !flag_trim && rspace
  489. end
  490. #rest = $' || text # ruby1.8
  491. rest = m ? text[m.end(0)..-1] : text # ruby1.9
  492. super(src, rest)
  493. end
  494. attr_accessor :header, :footer
  495. def convert(input)
  496. source = super
  497. return @src = "#{@header}#{source}#{@footer}"
  498. end
  499. end
  500. ##
  501. ## delete indentation of HTML.
  502. ##
  503. ## this is language-independent.
  504. ##
  505. module DeleteIndentEnhancer
  506. def self.desc # :nodoc:
  507. "delete indentation of HTML."
  508. end
  509. def convert_input(src, input)
  510. input = input.gsub(/^[ \t]+</, '<')
  511. super(src, input)
  512. end
  513. end
  514. ##
  515. ## convert "<h1><%=title%></h1>" into "_buf << %Q`<h1>#{title}</h1>`"
  516. ##
  517. ## this is only for Eruby.
  518. ##
  519. module InterpolationEnhancer
  520. def self.desc # :nodoc:
  521. "convert '<p><%=text%></p>' into '_buf << %Q`<p>\#{text}</p>`'"
  522. end
  523. def convert_input(src, input)
  524. pat = @pattern
  525. regexp = pat.nil? || pat == '<% %>' ? Basic::Converter::DEFAULT_REGEXP : pattern_regexp(pat)
  526. pos = 0
  527. is_bol = true # is beginning of line
  528. str = ''
  529. input.scan(regexp) do |indicator, code, tailch, rspace|
  530. match = Regexp.last_match()
  531. len = match.begin(0) - pos
  532. text = input[pos, len]
  533. pos = match.end(0)
  534. ch = indicator ? indicator[0] : nil
  535. lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
  536. is_bol = rspace ? true : false
  537. _add_text_to_str(str, text)
  538. ## * when '<%= %>', do nothing
  539. ## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
  540. if ch == ?= # <%= %>
  541. rspace = nil if tailch && !tailch.empty?
  542. str << lspace if lspace
  543. add_expr(str, code, indicator)
  544. str << rspace if rspace
  545. elsif ch == ?\# # <%# %>
  546. n = code.count("\n") + (rspace ? 1 : 0)
  547. if @trim && lspace && rspace
  548. add_text(src, str)
  549. str = ''
  550. add_stmt(src, "\n" * n)
  551. else
  552. str << lspace if lspace
  553. add_text(src, str)
  554. str = ''
  555. add_stmt(src, "\n" * n)
  556. str << rspace if rspace
  557. end
  558. else # <% %>
  559. if @trim && lspace && rspace
  560. add_text(src, str)
  561. str = ''
  562. add_stmt(src, "#{lspace}#{code}#{rspace}")
  563. else
  564. str << lspace if lspace
  565. add_text(src, str)
  566. str = ''
  567. add_stmt(src, code)
  568. str << rspace if rspace
  569. end
  570. end
  571. end
  572. #rest = $' || input # ruby1.8
  573. rest = pos == 0 ? input : input[pos..-1] # ruby1.9
  574. _add_text_to_str(str, rest)
  575. add_text(src, str)
  576. end
  577. def add_text(src, text)
  578. return if !text || text.empty?
  579. #src << " _buf << %Q`" << text << "`;"
  580. if text[-1] == ?\n
  581. text[-1] = "\\n"
  582. src << " #{@bufvar} << %Q`#{text}`\n"
  583. else
  584. src << " #{@bufvar} << %Q`#{text}`;"
  585. end
  586. end
  587. def _add_text_to_str(str, text)
  588. return if !text || text.empty?
  589. str << text.gsub(/[`\#\\]/, '\\\\\&')
  590. end
  591. def add_expr_escaped(str, code)
  592. str << "\#{#{escaped_expr(code)}}"
  593. end
  594. def add_expr_literal(str, code)
  595. str << "\#{#{code}}"
  596. end
  597. end
  598. end