From fd236a9e238fd0e1ba48bd789408ef9edcb9d570 Mon Sep 17 00:00:00 2001 From: Joel Drapper Date: Thu, 8 May 2025 12:14:18 +0100 Subject: [PATCH] Raise when `options_for_select` is used as a value --- lib/phlex/rails.rb | 2 + lib/phlex/rails/helpers/options_for_select.rb | 14 ++- lib/phlex/rails/helpers/select_tag.rb | 12 ++- lib/phlex/rails/never.rb | 11 +++ test/helpers/options_for_select.test.rb | 91 +++++++++++++++++++ 5 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 lib/phlex/rails/never.rb create mode 100644 test/helpers/options_for_select.test.rb diff --git a/lib/phlex/rails.rb b/lib/phlex/rails.rb index c2326d5..2ced618 100644 --- a/lib/phlex/rails.rb +++ b/lib/phlex/rails.rb @@ -22,6 +22,8 @@ class HelpersCalledBeforeRenderError < StandardError; end autoload :Streaming, "phlex/rails/streaming" autoload :Builder, "phlex/rails/builder" + + autoload :Never, "phlex/rails/never" end CSV.prepend(Phlex::Rails::CSV) diff --git a/lib/phlex/rails/helpers/options_for_select.rb b/lib/phlex/rails/helpers/options_for_select.rb index 3a709c3..f5faa71 100644 --- a/lib/phlex/rails/helpers/options_for_select.rb +++ b/lib/phlex/rails/helpers/options_for_select.rb @@ -4,5 +4,17 @@ module Phlex::Rails::Helpers::OptionsForSelect extend Phlex::Rails::HelperMacros # [Rails Docs](https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-options_for_select) - register_output_helper def options_for_select(...) = nil + def options_for_select(*args, **kwargs, &block) + output = if block + view_context.options_for_select(*args, **kwargs) { |*args| capture(*args, &block) } + else + view_context.options_for_select(*args, **kwargs) + end + + raw(output) + + Phlex::Rails::Never.new do + raise Phlex::ArgumentError.new("You can’t use options_for_select as an argument for a select helper in Phlex. Instead, pass a block and call options_for_select inside that block.") + end + end end diff --git a/lib/phlex/rails/helpers/select_tag.rb b/lib/phlex/rails/helpers/select_tag.rb index 6816b6e..48e7e85 100644 --- a/lib/phlex/rails/helpers/select_tag.rb +++ b/lib/phlex/rails/helpers/select_tag.rb @@ -4,5 +4,15 @@ module Phlex::Rails::Helpers::SelectTag extend Phlex::Rails::HelperMacros # [Rails Docs](https://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html#method-i-select_tag) - register_output_helper def select_tag(...) = nil + # register_output_helper def select_tag(...) = nil + + def select_tag(name, *, **, &block) + output = if block + view_context.select_tag(name, capture(&block), *, **) + else + view_context.select_tag(name, *, **) + end + + raw(output) + end end diff --git a/lib/phlex/rails/never.rb b/lib/phlex/rails/never.rb new file mode 100644 index 0000000..8d57ff8 --- /dev/null +++ b/lib/phlex/rails/never.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class Phlex::Rails::Never < BasicObject + def initialize(&block) + @block = block + end + + def method_missing(method_name, *, **) + @block.call(method_name, *, **) + end +end diff --git a/test/helpers/options_for_select.test.rb b/test/helpers/options_for_select.test.rb new file mode 100644 index 0000000..1ac102f --- /dev/null +++ b/test/helpers/options_for_select.test.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +test "options_for_select as a value for a select_tag" do + component = Class.new(Phlex::HTML) do + include Phlex::Rails::Helpers::SelectTag + include Phlex::Rails::Helpers::OptionsForSelect + + define_method :view_template do + select_tag :test, options_for_select([["Option 1", 1], ["Option 2", 2]]) + end + end + + error = assert_raises Phlex::ArgumentError do + render(component) + end + + assert error.message.include?("options_for_select") +end + +test "options_for_select as a value for a form_with select" do + component = Class.new(Phlex::HTML) do + include Phlex::Rails::Helpers::FormWith + include Phlex::Rails::Helpers::OptionsForSelect + + define_method :view_template do + form_with(url: "/") do |form| + form.select(:test, options_for_select([["Option 1", 1], ["Option 2", 2]])) + end + end + end + + controller.define_singleton_method(:form_authenticity_token) { |_| "(example form authenticity token)" } + + error = assert_raises Phlex::ArgumentError do + render(component) + end + + assert error.message.include?("options_for_select") +end + +test "options_for_select in a form with select" do + component = Class.new(Phlex::HTML) do + include Phlex::Rails::Helpers::FormWith + include Phlex::Rails::Helpers::OptionsForSelect + + define_method :view_template do + form_with(url: "/") do |form| + form.select(:test) do + options_for_select([["Option 1", 1], ["Option 2", 2]]) + end + end + end + end + + controller.define_singleton_method(:form_authenticity_token) { |_| "(example form authenticity token)" } + + output = render(component) + + assert_equivalent_html output, <<~HTML +
+ + + +
+ HTML +end + +test "options_for_select in a select_tag" do + component = Class.new(Phlex::HTML) do + include Phlex::Rails::Helpers::SelectTag + include Phlex::Rails::Helpers::OptionsForSelect + + define_method :view_template do + select_tag :test do + options_for_select([["Option 1", 1], ["Option 2", 2]]) + end + end + end + + output = render(component) + + assert_equivalent_html output, <<~HTML + + HTML +end