From 048bdae548a76c9e33ea678a056adc8218b27202 Mon Sep 17 00:00:00 2001 From: Rainer Borene Date: Mon, 25 Nov 2024 12:05:06 -0300 Subject: [PATCH] feat(metal): implement base class for pure ruby components --- lib/view_component.rb | 1 + lib/view_component/metal.rb | 37 +++++++++++++++++++ .../sandbox/app/components/metal_component.rb | 12 ++++++ test/sandbox/test/metal_test.rb | 12 ++++++ 4 files changed, 62 insertions(+) create mode 100644 lib/view_component/metal.rb create mode 100644 test/sandbox/app/components/metal_component.rb create mode 100644 test/sandbox/test/metal_test.rb diff --git a/lib/view_component.rb b/lib/view_component.rb index 62c9cd2cb..88fbb0db0 100644 --- a/lib/view_component.rb +++ b/lib/view_component.rb @@ -7,6 +7,7 @@ module ViewComponent extend ActiveSupport::Autoload autoload :Base + autoload :Metal autoload :CaptureCompatibility autoload :Compiler autoload :CompileCache diff --git a/lib/view_component/metal.rb b/lib/view_component/metal.rb new file mode 100644 index 000000000..7d6a6f647 --- /dev/null +++ b/lib/view_component/metal.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module ViewComponent + class Metal + undef :p + + def self.tag_elements + @@tag_elements ||= ActionView::Helpers::TagHelper::TagBuilder.instance_methods(false) + end + + def render_in(view_context) + raise ArgumentError, "#{self.class} does not support passing a block" if block_given? + @view_context = view_context + @view_context.with_output_buffer { call }.to_s + end + + def plain(string) + @view_context.concat(string) + end + + def method_missing(method, ...) + if self.class.tag_elements.include?(method) + @view_context.concat @view_context.tag.__send__(method, ...) + elsif @view_context.respond_to?(method, false) + @view_context.__send__(method, ...) + else + super + end + end + + def respond_to_missing?(method, include_all = false) + return true if super + return true if self.class.tag_elements.include?(method) + @view_context.respond_to?(method, false) + end + end +end diff --git a/test/sandbox/app/components/metal_component.rb b/test/sandbox/app/components/metal_component.rb new file mode 100644 index 000000000..732e839d4 --- /dev/null +++ b/test/sandbox/app/components/metal_component.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class MetalComponent < ViewComponent::Metal + def call + button type: :button do + p "Hello" + span do + strong { " World" } + end + end + end +end diff --git a/test/sandbox/test/metal_test.rb b/test/sandbox/test/metal_test.rb new file mode 100644 index 000000000..1c8b46813 --- /dev/null +++ b/test/sandbox/test/metal_test.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require "test_helper" + +class MetalTest < ViewComponent::TestCase + test "it works" do + render_inline MetalComponent.new + + assert_selector "button > span > strong" + assert_text "Hello World" + end +end