diff --git a/lib/erb.rb b/lib/erb.rb index 4c29553a53..f367e32a97 100644 --- a/lib/erb.rb +++ b/lib/erb.rb @@ -889,6 +889,16 @@ class ERB end end + # Render a template on a new toplevel binding with local variables specified + # by a Hash object. + def result_with_hash(hash) + b = new_toplevel + hash.each_pair do |key, value| + b.local_variable_set(key, value) + end + result(b) + end + ## # Returns a new binding each time *near* TOPLEVEL_BINDING for runs that do # not specify a binding. diff --git a/test/erb/test_erb.rb b/test/erb/test_erb.rb index 10fb177f58..c41ba25d7c 100644 --- a/test/erb/test_erb.rb +++ b/test/erb/test_erb.rb @@ -564,6 +564,35 @@ EOS assert_equal(flag, erb.result) end end + + def test_result_with_hash + erb = @erb.new("<%= foo %>") + assert_equal("1", erb.result_with_hash(foo: "1")) + end + + def test_result_with_hash_does_not_use_caller_local_variables + erb = @erb.new("<%= foo %>") + foo = 1 + assert_raise(NameError) { erb.result_with_hash({}) } + end + + def test_result_with_hash_does_not_modify_caller_binding + erb = @erb.new("<%= foo %>") + erb.result_with_hash(foo: "1") + assert_equal(false, binding.local_variable_defined?(:foo)) + end + + def test_result_with_hash_does_not_modify_toplevel_binding + erb = @erb.new("<%= foo %>") + erb.result_with_hash(foo: "1") + assert_equal(false, TOPLEVEL_BINDING.local_variable_defined?(:foo)) + end + + # This depends on the behavior that #local_variable_set raises TypeError by invalid key. + def test_result_with_hash_with_invalid_keys_raises_type_error + erb = @erb.new("<%= 1 %>") + assert_raise(TypeError) { erb.result_with_hash({ 1 => "1" }) } + end end class TestERBCoreWOStrScan < TestERBCore