diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ffd121035..d110e73b7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,6 +10,10 @@ nav_order: 5 ## main +* Raise the error `RedefinedExistingMethodError` when declaring a slot which conflicts with an existing method. + + *Hugo Chantelauze* + * Resolve an issue where slots starting with `call` would cause a `NameError` *Blake Williams* diff --git a/docs/index.md b/docs/index.md index b20009efd..0ba8e83e2 100644 --- a/docs/index.md +++ b/docs/index.md @@ -141,6 +141,7 @@ ViewComponent is built by over a hundred members of the community, including: fsateler fugufish g13ydson +hchtlz horacio horiaradu yykamei diff --git a/lib/view_component/errors.rb b/lib/view_component/errors.rb index b896f53d1..c2279ef8f 100644 --- a/lib/view_component/errors.rb +++ b/lib/view_component/errors.rb @@ -129,6 +129,16 @@ def initialize(klass_name, slot_name) end end + class RedefinedExistingMethodError < InvalidSlotNameError + MESSAGE = + "COMPONENT declares a slot named SLOT_NAME, which conflicts with an existing method.\n\n" \ + "To fix this issue, choose a different slot name." + + def initialize(klass_name, slot_name) + super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("SLOT_NAME", slot_name.to_s)) + end + end + class ReservedSingularSlotNameError < InvalidSlotNameError MESSAGE = "COMPONENT declares a slot named SLOT_NAME, which is a reserved word in the ViewComponent framework.\n\n" \ diff --git a/lib/view_component/slotable.rb b/lib/view_component/slotable.rb index d46094b3c..e3b1cd2ea 100644 --- a/lib/view_component/slotable.rb +++ b/lib/view_component/slotable.rb @@ -312,6 +312,11 @@ def validate_singular_slot_name(slot_name) raise_if_slot_conflicts_with_call(slot_name) raise_if_slot_ends_with_question_mark(slot_name) raise_if_slot_registered(slot_name) + raise_if_method_exists(slot_name) + end + + def raise_if_slot_ends_with_question_mark(slot_name) + raise SlotPredicateNameError.new(name, slot_name) if slot_name.to_s.ends_with?("?") end def raise_if_slot_registered(slot_name) @@ -321,8 +326,10 @@ def raise_if_slot_registered(slot_name) end end - def raise_if_slot_ends_with_question_mark(slot_name) - raise SlotPredicateNameError.new(name, slot_name) if slot_name.to_s.ends_with?("?") + def raise_if_method_exists(slot_name) + if instance_methods.include?(slot_name) + raise RedefinedExistingMethodError.new(name, slot_name) + end end def raise_if_slot_conflicts_with_call(slot_name) diff --git a/test/sandbox/test/slotable_test.rb b/test/sandbox/test/slotable_test.rb index 086f418e5..8938bf8d0 100644 --- a/test/sandbox/test/slotable_test.rb +++ b/test/sandbox/test/slotable_test.rb @@ -741,4 +741,24 @@ def test_slot_names_can_start_with_call end end end + + def test_raises_error_on_slot_name_conflicting_with_existing_method_for_renders_one + exception = assert_raises ViewComponent::RedefinedExistingMethodError do + Class.new(ViewComponent::Base) do + renders_one :tag + end + end + + assert_includes exception.message, "declares a slot named tag" + end + + def test_raises_error_on_slot_name_conflicting_with_existing_method_for_renders_many + exception = assert_raises ViewComponent::RedefinedExistingMethodError do + Class.new(ViewComponent::Base) do + renders_many :tags + end + end + + assert_includes exception.message, "declares a slot named tag" + end end