Erubis Users' Guide
6 Other Topics
6-1 '<%= =%>' and '<%= -%>'
Since 2.6.0, '<%= -%>' remove tail spaces and newline. This is for compatibiliy with ERB when trim mode is '-'. '<%= =%>' also removes tail spaces and newlines, and this is Erubis-original enhancement (cooler than '<%= -%>', isn't it?).
<div> <%= @var -%> # or <%= @var =%> </div>
$ erubis -c '{var: "AAA\n"}' tailnewline.rhtml
<div>
AAA
</div>
$ erubis -c '{var: "AAA\n"}' tailnewline.rhtml
<div>
AAA
</div>
6-2 '<%% %>' and '<%%= %>'
Since 2.6.0, '<%% %>' and '<%%= %>' are converted into '<% %>' and '<%= %>' respectively. This is for compatibility with ERB.
<ul> <%% for item in @list %> <li><%%= item %></li> <%% end %> </ul>
$ erubis doublepercent.rhtml <ul> <% for item in @list %> <li><%= item %></li> <% end %> </ul>
6-3 evaluate(context) v.s. result(binding)
It is recommended to use 'Erubis::Eruby#evaluate(context)' instead of 'Erubis::Eruby#result(binding)' because Ruby's Binding object has some problems.
- It is not able to specify variables to use. Using binding() method, all of local variables are passed to templates.
- Changing local variables in templates may affect to varialbes in main program. If you assign '10' to local variable 'x' in templates, it may change variable 'x' in main program unintendedly.
The following example shows that assignment of some values into variable 'x' in templates affect to local variable 'x' in main program unintendedly.
<% for x in items %> item = <%= x %> <% end %> ** debug: local variables=<%= local_variables().inspect() %>
require 'erubis'
eruby = Erubis::Eruby.new(File.read('template1.rhtml'))
items = ['foo', 'bar', 'baz']
x = 1
## local variable 'x' and 'eruby' are passed to template as well as 'items'!
print eruby.result(binding())
## local variable 'x' is changed unintendedly because it is changed in template!
puts "** debug: x=#{x.inspect}" #=> "baz"
$ ruby main_program1.rb item = foo item = bar item = baz ** debug: local variables=["eruby", "items", "x", "_buf"] ** debug: x="baz"
This problem is caused because Ruby's Binding class is poor to use in template engine. Binding class should support the following features.
b = Binding.new # create empty Binding object b['x'] = 1 # set local variables using binding object
But the above features are not implemented in Ruby.
A pragmatic solution is to use 'Erubis::Eruby#evaluate(context)' instead of 'Erubis::Eruby#result(binding)'. 'evaluate(context)' uses Erubis::Context object and instance variables instead of Binding object and local variables.
<% for x in @items %> item = <%= x %> <% end %> ** debug: local variables=<%= local_variables().inspect() %>
require 'erubis'
eruby = Erubis::Eruby.new(File.read('template2.rhtml'))
items = ['foo', 'bar', 'baz']
x = 1
## only 'items' are passed to template
print eruby.evaluate(:items=>items)
## local variable 'x' is not changed!
puts "** debug: x=#{x.inspect}" #=> 1
$ ruby main_program2.rb item = foo item = bar item = baz ** debug: local variables=["_context", "x", "_buf"] ** debug: x=1
6-4 Class Erubis::FastEruby
[experimental]
Erubis provides Erubis::FastEruby class which includes InterpolationEnhancer and works faster than Erubis::Eruby class. If you desire more speed, try Erubis::FastEruby class.
<html>
<body>
<h1><%== @title %></h1>
<table>
<% i = 0 %>
<% for item in @list %>
<% i += 1 %>
<tr>
<td><%= i %></td>
<td><%== item %></td>
</tr>
<% end %>
</table>
</body>
</html>
require 'erubis'
input = File.read('fasteruby.rhtml')
eruby = Erubis::FastEruby.new(input) # create Eruby object
puts "---------- script source ---"
puts eruby.src
puts "---------- result ----------"
context = { :title=>'Example', :list=>['aaa', 'bbb', 'ccc'] }
output = eruby.evaluate(context)
print output
$ ruby fasteruby.rb
---------- script source ---
_buf = ''; _buf << %Q`<html>
<body>
<h1>#{Erubis::XmlHelper.escape_xml( @title )}</h1>
<table>\n`
i = 0
for item in @list
i += 1
_buf << %Q` <tr>
<td>#{ i }</td>
<td>#{Erubis::XmlHelper.escape_xml( item )}</td>
</tr>\n`
end
_buf << %Q` </table>
</body>
</html>\n`
_buf.to_s
---------- result ----------
<html>
<body>
<h1>Example</h1>
<table>
<tr>
<td>1</td>
<td>aaa</td>
</tr>
<tr>
<td>2</td>
<td>bbb</td>
</tr>
<tr>
<td>3</td>
<td>ccc</td>
</tr>
</table>
</body>
</html>
6-5 Syntax Checking
Command-line option '-z' checks syntax. It is similar to 'erubis -x file.rhtml | ruby -wc', but it can take several file names.
$ erubis -z app/views/*/*.rhtml Syntax OK
6-6 File Caching
Erubis::Eruby.load_file(filename) convert file into Ruby script and return Eruby object. In addition, it caches converted Ruby script into cache file (filename + '.cache') if cache file is old or not exist. If cache file exists and is newer than eruby file, Erubis::Eruby.load_file() loads cache file.
require 'erubis'
filename = 'example.rhtml'
eruby = Erubis::Eruby.load_file(filename)
cachename = filename + '.cache'
if test(?f, cachename)
puts "*** cache file '#{cachename}' created."
end
Since 2.6.0, it is able to specify cache filename.
filename = 'example.rhtml' eruby = Erubis::Eruby.load_file(filename, :cachename=>filename+'.cache')
Caching makes Erubis about 40-50 percent faster than no-caching. See benchmark for details.
6-7 Erubis::TinyEruby class
Erubis::TinyEruby class in 'erubis/tiny.rb' is the smallest implementation of eRuby. If you don't need any enhancements of Erubis and only require simple eRuby implementation, try Erubis::TinyEruby class.
6-8 NoTextEnhancer and NoCodeEnhancer in PHP
NoTextEnhancer and NoCodEnahncer are quite useful not only for eRuby but also for PHP. The former "drops" HTML text and show up embedded Ruby/PHP code and the latter drops embedded Ruby/PHP code and leave HTML text.
For example, see the following PHP script.
<html>
<body>
<h3>List</h3>
<?php if (!$list || count($list) == 0) { ?>
<p>not found.</p>
<?php } else { ?>
<table>
<tbody>
<?php $i = 0; ?>
<?php foreach ($list as $item) { ?>
<tr bgcolor="<?php echo ++$i % 2 == 1 ? '#FFCCCC' : '#CCCCFF'; ?>">
<td><?php echo $item; ?></td>
</tr>
<?php } ?>
</tbody>
</table>
<?php } ?>
</body>
</html>
This is complex because PHP code and HTML document are mixed. NoTextEnhancer can separate PHP code from HTML document.
$ erubis -l php --pi=php -N -E NoText --trim=false notext-example.php
1:
2:
3:
4: <?php if (!$list || count($list) == 0) { ?>
5:
6: <?php } else { ?>
7:
8:
9: <?php $i = 0; ?>
10: <?php foreach ($list as $item) { ?>
11: <?php echo ++$i % 2 == 1 ? '#FFCCCC' : '#CCCCFF'; ?>
12: <?php echo $item; ?>
13:
14: <?php } ?>
15:
16:
17: <?php } ?>
18:
19:
In the same way, NoCodeEnhancer can extract HTML tags.
$ erubis -l php --pi=php -N -E NoCode --trim=false notext-example.php
1: <html>
2: <body>
3: <h3>List</h3>
4:
5: <p>not found.</p>
6:
7: <table>
8: <tbody>
9:
10:
11: <tr bgcolor="">
12: <td></td>
13: </tr>
14:
15: </tbody>
16: </table>
17:
18: </body>
19: </html>
6-9 Helper Class for mod_ruby
Thanks Andrew R Jackson, he developed 'erubis-run.rb' which enables you to use Erubis with mod_ruby.
- Copy 'erubis-2.6.2/contrib/erubis-run.rb' to the 'RUBYLIBDIR/apache' directory (for example '/usr/local/lib/ruby/1.8/apache') which contains 'ruby-run.rb', 'eruby-run.rb', and so on.
$ cd erubis-2.6.2/ $ sudo copy contrib/erubis-run.rb /usr/local/lib/ruby/1.8/apache/
- Add the following example to your 'httpd.conf' (for example '/usr/local/apache2/conf/httpd.conf')
LoadModule ruby_module modules/mod_ruby.so <IfModule mod_ruby.c> RubyRequire apache/ruby-run RubyRequire apache/eruby-run RubyRequire apache/erubis-run <Location /erubis> SetHandler ruby-object RubyHandler Apache::ErubisRun.instance </Location> <Files *.rhtml> SetHandler ruby-object RubyHandler Apache::ErubisRun.instance </Files> </IfModule> - Restart Apache web server.
$ sudo /usr/local/apache2/bin/apachectl stop $ sudo /usr/local/apache2/bin/apachectl start
- Create *.rhtml file, for example:
<html> <body> Now is <%= Time.now %> Erubis version is <%= Erubis::VERSION %> </body> </html>
- Change mode of your directory to be writable by web server process.
$ cd /usr/local/apache2/htdocs/erubis $ sudo chgrp daemon . $ sudo chmod 775 .
- Access the *.rhtml file and you'll get the web page.
You must set your directories to be writable by web server process, because Apache::ErubisRun calls Erubis::Eruby.load_file() internally which creates cache files in the same directory in which '*.rhtml' file exists.
6-10 Define method
Erubis::Eruby#def_method() defines instance method or singleton method.
require 'erubis'
s = "hello <%= name %>"
eruby = Erubis::Eruby.new(s)
filename = 'hello.rhtml'
## define instance method to Dummy class (or module)
class Dummy; end
eruby.def_method(Dummy, 'render(name)', filename) # filename is optional
p Dummy.new.render('world') #=> "hello world"
## define singleton method to dummy object
obj = Object.new
eruby.def_method(obj, 'render(name)', filename) # filename is optional
p obj.render('world') #=> "hello world"
6-11 Benchmark
A benchmark script is included in Erubis package at 'erubis-2.6.2/benchark/' directory. Here is an example result of benchmark.
$ cd erubis-2.6.2/benchmark/
$ ruby bench.rb -n 10000 -m execute
*** ntimes=10000, testmode=execute
user system total real
eruby 12.720000 0.240000 12.960000 ( 12.971888)
ERB 36.760000 0.350000 37.110000 ( 37.112019)
ERB(cached) 11.990000 0.440000 12.430000 ( 12.430375)
Erubis::Eruby 10.840000 0.300000 11.140000 ( 11.144426)
Erubis::Eruby(cached) 7.540000 0.410000 7.950000 ( 7.969305)
Erubis::FastEruby 10.440000 0.300000 10.740000 ( 10.737808)
Erubis::FastEruby(cached) 6.940000 0.410000 7.350000 ( 7.353666)
Erubis::TinyEruby 9.550000 0.290000 9.840000 ( 9.851729)
Erubis::ArrayBufferEruby 11.010000 0.300000 11.310000 ( 11.314339)
Erubis::PrintOutEruby 11.640000 0.290000 11.930000 ( 11.942141)
Erubis::StdoutEruby 11.590000 0.300000 11.890000 ( 11.886512)
This shows that...
- Erubis::Eruby runs more than 10 percent faster than eruby.
- Erubis::Eruby runs about 3 times faster than ERB.
- Caching (by Erubis::Eruby.load_file()) makes Erubis about 40-50 percent faster.
- Erubis::FastEruby is a litte faster than Erubis::Eruby.
- Array buffer (ArrayBufferEnhancer) is a little slower than string buffer (StringBufferEnhancer which Erubis::Eruby includes)
- $stdout and print() make Erubis a little slower.
- Erubis::TinyEruby (at 'erubis/tiny.rb') is the fastest in all eRuby implementations when no caching.
Escaping HTML characters (such as '< > & "') makes Erubis more faster than eruby and ERB, because Erubis::XmlHelper#escape_xml() works faster than CGI.escapeHTML() and ERB::Util#h(). The following shows that Erubis runs more than 40 percent (when no-cached) or 90 percent (when cached) faster than eruby if HTML characters are escaped.
$ ruby bench.rb -n 10000 -m execute -ep
*** ntimes=10000, testmode=execute
user system total real
eruby 21.700000 0.290000 21.990000 ( 22.050687)
ERB 45.140000 0.390000 45.530000 ( 45.536976)
ERB(cached) 20.340000 0.470000 20.810000 ( 20.822653)
Erubis::Eruby 14.830000 0.310000 15.140000 ( 15.147930)
Erubis::Eruby(cached) 11.090000 0.420000 11.510000 ( 11.514954)
Erubis::FastEruby 14.850000 0.310000 15.160000 ( 15.172499)
Erubis::FastEruby(cached) 10.970000 0.430000 11.400000 ( 11.399605)
Erubis::ArrayBufferEruby 14.970000 0.300000 15.270000 ( 15.281061)
Erubis::PrintOutEruby 15.780000 0.300000 16.080000 ( 16.088289)
Erubis::StdoutEruby 15.840000 0.310000 16.150000 ( 16.235338)