summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormakoto kuwata <kwa@kuwata-lab.com>2007-09-18 08:02:49 +0000
committermakoto kuwata <kwa@kuwata-lab.com>2007-09-18 08:02:49 +0000
commit7cb00b66a50550da046928b5bf0059e4f00da88a (patch)
tree0b004bf50ee52e8469d6bb7219207ac20c3f6ee9
parent5cdc7a11e351e6322fb1a4eb01532e49cdb7c804 (diff)
downloaderubis-7cb00b66a50550da046928b5bf0059e4f00da88a.tar.gz
- [change] add Erubis::EMPTY_BINDING
- [bugfix] use Erubis::EMPTY_BINDING instead of TOPLEVEL_BINDING in Evaluator#evaluate() - [enhance] user's guide: new section 'evaluate(context) vs. result(binding)' added - [update] CHANGES
-rw-r--r--CHANGES44
-rw-r--r--ChangeLog.txt6
-rw-r--r--doc/Rookbook.yaml12
-rw-r--r--doc/users-guide.html93
-rw-r--r--doc/users-guide.txt90
-rw-r--r--lib/erubis/evaluator.rb4
-rw-r--r--test/test-erubis.rb15
7 files changed, 256 insertions, 8 deletions
diff --git a/CHANGES b/CHANGES
index a5319fd..12e4661 100644
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,50 @@
# $Release:$
# $Copyright$
+#
+- release: 2.4.1
+ date:
+ enhancements:
+
+ - |
+ Add new section 'evaluate(context) v.s. result(binding)' to user's guide.
+ This section describes why Erubis::Eruby#evaluate(context) is recommended
+ rather than Erubis::Eruby#result(binding).
+ User's Guide : Other Topics : evaluate(context) v.s. result(binding)
+ http://www.kuwata-lab.com/erubis/users-guide.06.html#topics-context-vs-binding
+
+ bugfix:
+
+ - |
+ When using Erubis::Eruby#evaluate(), changing local variables in
+ templates have affected to variables accessible with TOPLEVEL_BINDING.
+ It means that if you change variables in templates, it is possible to
+ change variables in main program.
+ This was a bug and is now fixed not to affect to variables in main
+ program.
+
+ ex. template.rhtml
+ --------------------
+ <% for x in @items %>
+ item = <%= x %>
+ <% end %>
+ --------------------
+
+ ex. main-program.rb
+ --------------------
+ require 'erubis'
+ x = 10
+ items = ['foo', 'bar', 'baz']
+ eruby = Erubis::Eruby.new(File.read('template.rhtml'))
+ s = eruby.evaluate(:items=>items)
+ print s
+ $stderr.puts "*** debug: x=#{x.inspect}" #=> x="baz" (2.4.0)
+ #=> x=10 (2.4.1)
+ --------------------
+
+ - |
+ PercentLineEnhancer was very slow. Now performance problem is solved.
+
#
- release: 2.4.0
diff --git a/ChangeLog.txt b/ChangeLog.txt
index 4dc8609..3b87da8 100644
--- a/ChangeLog.txt
+++ b/ChangeLog.txt
@@ -2,6 +2,12 @@
.?lastupdate: $Date$
.?version: $Rev$
+: Rev.94 (2007-09-18)
+ .- [change] add Erubis::EMPTY_BINDING
+ .- [bugfix] use Erubis::EMPTY_BINDING instead of TOPLEVEL_BINDING in Evaluator#evaluate()
+ .- [enhance] user's guide: new section 'evaluate(context) vs. result(binding)' added
+ .- [update] CHANGES
+
: Rev.93 (2007-09-10)
.- [bugfix] PercentLineEnhancer#add_text() was very slow (now very fast)
diff --git a/doc/Rookbook.yaml b/doc/Rookbook.yaml
index 5ae672c..c031a35 100644
--- a/doc/Rookbook.yaml
+++ b/doc/Rookbook.yaml
@@ -45,10 +45,11 @@ recipes:
ingreds: [ $(u).txt ]
byprods: [ $(u).toc.html ]
method*: |
- rm_rf "guide.d"
- mkdir_p "guide.d"
- sys "retrieve -d guide.d #{@ingred}"
- for filename in Dir.glob("guide.d/*.*_filter")
+ dir = "../test/data/$(u)"
+ mkdir_p dir
+ rm_rf "#{dir}/*"
+ sys "retrieve -d #{dir} #{@ingred}"
+ for filename in Dir.glob("#{dir}/*.*_filter")
content = File.read(filename)
name = (filename =~ /\.(\w+)_filter$/) && $1
case name
@@ -67,9 +68,6 @@ recipes:
end
File.unlink(filename)
end
- mkdir_p "../test/data/$(u)/"
- rm_f "../test/data/$(u)/*"
- cp_p "guide.d/*", "../test/data/$(u)/"
sys "kwaser -t $(tagfile) -T #{@ingred} > #{@byprod}"
sys "kwaser -t $(tagfile) #{@ingred} > #{@product}"
rm "#{@byprod}"
diff --git a/doc/users-guide.html b/doc/users-guide.html
index a8706ce..62bfc50 100644
--- a/doc/users-guide.html
+++ b/doc/users-guide.html
@@ -151,6 +151,8 @@ It has the following features.
</li>
<li><a href="#topics">Other Topics</a>
<ul>
+ <li><a href="#topics-context-vs-binding">evaluate(context) v.s. result(binding)</a>
+ </li>
<li><a href="#topics-fasteruby">Class Erubis::FastEruby</a>
</li>
<li><a href="#topics-syntax">Syntax Checking</a>
@@ -661,6 +663,9 @@ output</div>
&lt;li&gt;ccc&lt;/li&gt;
&lt;/ul&gt;
</pre>
+<p>It is recommended to use 'Erubis::Eruby#evaluate(context)' rather than 'Erubis::Eruby#result(binding())' because the latter has some problems.
+See <a href="#topics-context-vs-binding">evaluate(context) v.s. result(binding)</a> section for details.
+</p>
<br>
@@ -2487,6 +2492,94 @@ because tag helpers generate different html code when form parameter has errors
<a name="topics"></a>
<h2 class="section1">Other Topics</h2>
+<a name="topics-context-vs-binding"></a>
+<h3 class="section2">evaluate(context) v.s. result(binding)</h3>
+<p>It is recommended to use 'Erubis::Eruby#evaluate(context)' instead of 'Erubis::Eruby#result(binding)' because Ruby's Binding object has some problems.
+</p>
+<ul type="disc">
+<li>It is not able to specify variables to use.
+ Using binding() method, all of local variables are passed to templates.
+</li>
+<li>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.
+</li>
+</ul>
+<p>The following example shows that assignment of some values into variable 'x' in templates affect to local variable 'x' in main program unintendedly.
+</p>
+<a name="template1.rhtml"></a>
+<div class="program_caption">
+template1.rhtml (intended to be passed 'items' from main program)</div>
+<pre class="program">&lt;% for <strong>x</strong> in <strong>items</strong> %&gt;
+item = &lt;%= x %&gt;
+&lt;% end %&gt;
+** debug: local variables=&lt;%= local_variables().inspect() %&gt;
+</pre>
+<a name="main_program1.rb"></a>
+<div class="program_caption">
+main_program1.rb (intended to pass 'items' to template)</div>
+<pre class="program">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 <strong>eruby.result(binding())</strong>
+## local variable 'x' is changed unintendedly because it is changed in template!
+puts "** debug: x=#{x.inspect}" #=&gt; "baz"
+</pre>
+<a name="main_program1.result"></a>
+<div class="terminal_caption">
+Result:</div>
+<pre class="terminal">$ ruby main_program1.rb
+item = foo
+item = bar
+item = baz
+** debug: local variables=["eruby", "items", "x", "_buf"]
+** debug: x="baz"
+</pre>
+<p>This problem is caused because Ruby's Binding class is poor to use in template engine.
+Binding class should support the following features.
+</p>
+<pre class="program">b = Binding.new # create empty Binding object
+b['x'] = 1 # set local variables using binding object
+</pre>
+<p>But the above features are not implemented in Ruby.
+</p>
+<p>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.
+</p>
+<a name="template2.rhtml"></a>
+<div class="program_caption">
+template2.rhtml (intended to be passed '@items' from main program)</div>
+<pre class="program">&lt;% for <strong>x</strong> in <strong>@items</strong> %&gt;
+item = &lt;%= x %&gt;
+&lt;% end %&gt;
+** debug: local variables=&lt;%= local_variables().inspect() %&gt;
+</pre>
+<a name="main_program2.rb"></a>
+<div class="program_caption">
+main_program2.rb (intended to pass '@items' to template)</div>
+<pre class="program">require 'erubis'
+eruby = Erubis::Eruby.new(File.read('template2.rhtml'))
+items = ['foo', 'bar', 'baz']
+x = 1
+## only 'items' are passed to template
+print <strong>eruby.evaluate(:items=&gt;items)</strong>
+## local variable 'x' is not changed!
+puts "** debug: x=#{x.inspect}" #=&gt; 1
+</pre>
+<a name="main_program2.result"></a>
+<div class="terminal_caption">
+Result:</div>
+<pre class="terminal">$ ruby main_program2.rb
+item = foo
+item = bar
+item = baz
+** debug: local variables=["x", "_buf"]
+** debug: x=1
+</pre>
+<br>
+
+
<a name="topics-fasteruby"></a>
<h3 class="section2">Class Erubis::FastEruby</h3>
<p>[experimental]
diff --git a/doc/users-guide.txt b/doc/users-guide.txt
index 5dcd267..6415bfb 100644
--- a/doc/users-guide.txt
+++ b/doc/users-guide.txt
@@ -539,6 +539,10 @@ $ ruby example6.rb
.====================
+It is recommended to use 'Erubis::Eruby#evaluate(context)' rather than 'Erubis::Eruby#result(binding())' because the latter has some problems.
+See {{<evaluate(context) v.s. result(binding)|#topics-context-vs-binding>}} section for details.
+
+
.$$ Context Data File | tut-datafile
@@ -2589,6 +2593,92 @@ If it is nil (default), Erubis prints converted Ruby code into log file only whe
.$ Other Topics | topics
+.$$ evaluate(context) v.s. result(binding) | topics-context-vs-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.
+
+.? template1.rhtml (intended to be passed 'items' from main program)
+.-------------------- template1.rhtml
+<% for {{*x*}} in {{*items*}} %>
+item = <%= x %>
+<% end %>
+** debug: local variables=<%= local_variables().inspect() %>
+.--------------------
+
+.? main_program1.rb (intended to pass 'items' to template)
+.-------------------- main_program1.rb
+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"
+.--------------------
+
+.? Result:
+.==================== main_program1.result
+$ 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.
+
+.? template2.rhtml (intended to be passed '@items' from main program)
+.-------------------- template2.rhtml
+<% for {{*x*}} in {{*@items*}} %>
+item = <%= x %>
+<% end %>
+** debug: local variables=<%= local_variables().inspect() %>
+.--------------------
+
+.? main_program2.rb (intended to pass '@items' to template)
+.-------------------- main_program2.rb
+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
+.--------------------
+
+.? Result:
+.==================== main_program2.result
+$ ruby main_program2.rb
+item = foo
+item = bar
+item = baz
+** debug: local variables=["x", "_buf"]
+** debug: x=1
+.====================
+
+
+
.$$ Class Erubis::FastEruby | topics-fasteruby
[experimental]
diff --git a/lib/erubis/evaluator.rb b/lib/erubis/evaluator.rb
index f946af1..8a76bc4 100644
--- a/lib/erubis/evaluator.rb
+++ b/lib/erubis/evaluator.rb
@@ -10,6 +10,8 @@ require 'erubis/context'
module Erubis
+ EMPTY_BINDING = binding()
+
##
## evaluate code
@@ -64,7 +66,7 @@ module Erubis
def evaluate(context=Context.new)
context = Context.new(context) if context.is_a?(Hash)
#return context.instance_eval(@src, @filename || '(erubis)')
- @_proc ||= eval("proc { #{@src} }", TOPLEVEL_BINDING, @filename || '(erubis)')
+ @_proc ||= eval("proc { #{@src} }", Erubis::EMPTY_BINDING, @filename || '(erubis)')
return context.instance_eval(&@_proc)
end
diff --git a/test/test-erubis.rb b/test/test-erubis.rb
index 1642a71..4505b48 100644
--- a/test/test-erubis.rb
+++ b/test/test-erubis.rb
@@ -192,8 +192,23 @@ END
assert_nil(eruby.instance_variable_get('@_proc'))
end
+ #def test_toplevel_binding
+ # s = "locals = <%= local_variables().inspect %>\n<% x = 50 %>\n"
+ # eruby = Erubis::Eruby.new(s)
+ # _x = eval 'x', TOPLEVEL_BINDING
+ # _y = eval 'y', TOPLEVEL_BINDING
+ # actual = eruby.evaluate(:x=>_x, :y=>_y)
+ # _x = eval 'x', TOPLEVEL_BINDING
+ # _y = eval 'y', TOPLEVEL_BINDING
+ # puts "*** actual=#{actual.inspect}, x=#{_x.inspect}, y=#{_y.inspect}"
+ #end
+
end
+x = 10
+y = 20
+
+
__END__
- name: basic1
input: &basic1_input|