module ViewComponent::SlotableV2
def set_slot(slot_name, *args, **kwargs, &block)
def set_slot(slot_name, *args, **kwargs, &block) slot_definition = self.class.registered_slots[slot_name] slot = SlotV2.new(self) # Passing the block to the sub-component wrapper like this has two # benefits: # # 1. If this is a `content_area` style sub-component, we will render the # block via the `slot` # # 2. Since we have to pass block content to components when calling # `render`, evaluating the block here would require us to call # `view_context.capture` twice, which is slower slot._content_block = block if block_given? # If class if slot_definition[:renderable] slot._component_instance = slot_definition[:renderable].new(*args, **kwargs) # If class name as a string elsif slot_definition[:renderable_class_name] slot._component_instance = self.class.const_get(slot_definition[:renderable_class_name]).new(*args, **kwargs) # If passed a lambda elsif slot_definition[:renderable_function] # Use `bind(self)` to ensure lambda is executed in the context of the # current component. This is necessary to allow the lambda to access helper # methods like `content_tag` as well as parent component state. renderable_value = slot_definition[:renderable_function].bind(self).call(*args, **kwargs, &block) # Function calls can return components, so if it's a component handle it specially if renderable_value.respond_to?(:render_in) slot._component_instance = renderable_value else slot._content = renderable_value end end @_set_slots ||= {} if slot_definition[:collection] @_set_slots[slot_name] ||= [] @_set_slots[slot_name].push(slot) else @_set_slots[slot_name] = slot end nil end