From f1fc68bc99202d5cfc23ba6b778b0f7634301f3c Mon Sep 17 00:00:00 2001 From: Takashi Kokubun Date: Thu, 18 May 2017 22:52:52 +0900 Subject: [PATCH] erb.rb: Add locals option to set local variables to `ERB#result` method. [ruby-core:55985] [Feature #8631] --- lib/erb.rb | 12 +++++++++++- test/erb/test_erb.rb | 31 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/lib/erb.rb b/lib/erb.rb index ae2bfd009143cf..fdc2bf8a84499b 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -885,8 +885,18 @@ def run(b=new_toplevel) # # _b_ accepts a Binding object which is used to set the context of # code evaluation. + # _locals_ accepts a Hash object which is used to set local variables + # on the Binding object. # - def result(b=new_toplevel) + def result(b=new_toplevel, locals: nil) + if locals + shadows = locals.keys.select { |l| b.local_variable_defined?(l) } + b = eval("proc { |#{shadows.join(',')}| self.send(:binding) }.call", b) + locals.each_pair do |key, value| + b.local_variable_set(key, value) + end + end + if @safe_level proc { $SAFE = @safe_level diff --git a/test/erb/test_erb.rb b/test/erb/test_erb.rb index 7a92de36309315..913d1391595624 100644 --- a/test/erb/test_erb.rb +++ b/test/erb/test_erb.rb @@ -540,6 +540,37 @@ def test_frozen_string_literal assert_equal(flag, erb.result) end end + + def test_result_with_locals + erb = @erb.new("<%= foo %>") + assert_equal("1", erb.result(locals: { foo: "1" })) + + erb = @erb.new("<%= foo %>") + b = Struct.new(:foo).new("1").instance_eval { binding } + assert_equal("2", erb.result(b, locals: { foo: "2" })) + + erb = @erb.new("<%= foo %><%= bar %>") + b = Struct.new(:foo).new("1").instance_eval { binding } + assert_equal("12", erb.result(b, locals: { bar: "2" })) + end + + def test_result_with_locals_does_not_modify_given_binding + EnvUtil.suppress_warning do + a = 1 + assert_equal("2", @erb.new("<%= a %>").result(binding, locals: { a: 2 })) + assert_equal(1, a) + end + end + + def test_result_with_locals_named_binding + b = binding + binding = 1 + assert_equal("2", @erb.new("<%= binding %>").result(b, locals: { binding: 2 })) + end + + def test_result_with_invalid_name_locals + assert_raise(NameError) { @erb.new("").result(locals: { :'foo-bar' => 1 }) } + end end class TestERBCoreWOStrScan < TestERBCore