From 9ea92d4cda11462a5d7b163e78977dc7062253d9 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Thu, 20 Jun 2024 08:51:47 +0200 Subject: [PATCH] Also pass `local_assigns` to strict locals templates If one of the locals conflict with a keyword, typically `class`. The potentially confusing part however is that if you define a default value, `local_assigns` won't respect it. --- actionview/lib/action_view/base.rb | 2 +- actionview/lib/action_view/template.rb | 9 +++++---- actionview/test/template/template_test.rb | 5 +++++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/actionview/lib/action_view/base.rb b/actionview/lib/action_view/base.rb index 2eec2d947a..38ea8ddfb9 100644 --- a/actionview/lib/action_view/base.rb +++ b/actionview/lib/action_view/base.rb @@ -265,7 +265,7 @@ def _run(method, template, locals, buffer, add_to_stack: true, has_strict_locals if has_strict_locals begin - public_send(method, buffer, **locals, &block) + public_send(method, locals, buffer, **locals, &block) rescue ArgumentError => argument_error raise( ArgumentError, diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index 9b7dfc2b56..cb2f854497 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -439,9 +439,9 @@ def compiled_source method_arguments = if set_strict_locals if set_strict_locals.include?("&") - "output_buffer, #{set_strict_locals}" + "local_assigns, output_buffer, #{set_strict_locals}" else - "output_buffer, #{set_strict_locals}, &_" + "local_assigns, output_buffer, #{set_strict_locals}, &_" end else "local_assigns, output_buffer, &_" @@ -500,11 +500,12 @@ def compile(mod) return unless strict_locals? - parameters = mod.instance_method(method_name).parameters - [[:req, :output_buffer]] + parameters = mod.instance_method(method_name).parameters + parameters -= [[:req, :local_assigns], [:req, :output_buffer]] + # Check compiled method parameters to ensure that only kwargs # were provided as strict locals, preventing `locals: (foo, *foo)` etc # and allowing `locals: (foo:)`. - non_kwarg_parameters = parameters.select do |parameter| ![:keyreq, :key, :keyrest, :nokey].include?(parameter[0]) end diff --git a/actionview/test/template/template_test.rb b/actionview/test/template/template_test.rb index 53b8b0c528..7876849044 100644 --- a/actionview/test/template/template_test.rb +++ b/actionview/test/template/template_test.rb @@ -238,6 +238,11 @@ def test_rails_injected_locals_can_be_specified assert_equal "Hello", render(message: "Hello", implicit_locals: %i[message]) end + def test_rails_local_assigns_and_strict_locals + @template = new_template("<%# locals: (class: ) -%>\n<%= local_assigns[:class] %>") + assert_equal "some-class", render(class: "some-class", implicit_locals: %i[message]) + end + def test_rails_injected_locals_can_be_specified_as_kwargs @template = new_template("<%# locals: (message: 'Hello', **kwargs) -%>\n<%= kwargs[:message_counter] %>-<%= kwargs[:message_iteration] %>") assert_equal "1-2", render(message: "Hello", message_counter: 1, message_iteration: 2, implicit_locals: %i[message_counter message_iteration])