Module: ViewComponent::Slotable

Extended by:
ActiveSupport::Concern
Included in:
Base
Defined in:
lib/view_component/slotable.rb

Instance Method Summary collapse

Instance Method Details

#__vc_get_slot(slot_name) ⇒ Object



355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
# File 'lib/view_component/slotable.rb', line 355

def __vc_get_slot(slot_name)
  @__vc_set_slots ||= {}
  content unless defined?(@__vc_content_evaluated) && @__vc_content_evaluated # ensure content is loaded so slots will be defined

  # If the slot is set, return it
  return @__vc_set_slots[slot_name] if @__vc_set_slots[slot_name]

  # If there is a default method for the slot, call it
  if (default_method = registered_slots[slot_name][:default_method])
    renderable_value = send(default_method)
    slot = Slot.new(self)

    if renderable_value.respond_to?(:render_in)
      slot.__vc_component_instance = renderable_value
    else
      slot.__vc_content = renderable_value
    end

    slot
  elsif self.class.registered_slots[slot_name][:collection]
    # If empty slot is a collection, return an empty array
    []
  end
end

#__vc_set_polymorphic_slot(slot_name, poly_type = nil, *args, **kwargs, &block) ⇒ Object



444
445
446
447
448
449
450
451
452
453
454
# File 'lib/view_component/slotable.rb', line 444

def __vc_set_polymorphic_slot(slot_name, poly_type = nil, *args, **kwargs, &block)
  slot_definition = self.class.registered_slots[slot_name]

  if !slot_definition[:collection] && defined?(@__vc_set_slots) && @__vc_set_slots[slot_name]
    raise ContentAlreadySetForPolymorphicSlotError.new(slot_name)
  end

  poly_def = slot_definition[:renderable_hash][poly_type]

  __vc_set_slot(slot_name, poly_def, *args, **kwargs, &block)
end

#__vc_set_slot(slot_name, slot_definition = nil, *args, **kwargs, &block) ⇒ Object



380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/view_component/slotable.rb', line 380

def __vc_set_slot(slot_name, slot_definition = nil, *args, **kwargs, &block)
  slot_definition ||= self.class.registered_slots[slot_name]
  slot = Slot.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
  if block
    slot.__vc_content_block = block
    # Capture the virtual path at the time the block is defined, so that
    # translations resolve relative to where the block was created, not where it's rendered
    slot.__vc_content_block_virtual_path = view_context.instance_variable_get(:@virtual_path)
  end

  # If class
  if slot_definition[:renderable]
    slot.__vc_component_instance = slot_definition[:renderable].new(*args, **kwargs)
  # If class name as a string
  elsif slot_definition[:renderable_class_name]
    slot.__vc_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_function = slot_definition[:renderable_function].bind(self)
    renderable_value =
      if block
        renderable_function.call(*args, **kwargs) do |*rargs|
          with_captured_virtual_path(@old_virtual_path) do
            view_context.capture(*rargs, &block)
          end
        end
      else
        renderable_function.call(*args, **kwargs)
      end

    # Function calls can return components, so if it's a component handle it specially
    if renderable_value.respond_to?(:render_in)
      slot.__vc_component_instance = renderable_value
    else
      slot.__vc_content = renderable_value
    end
  end

  @__vc_set_slots ||= {}

  if slot_definition[:collection]
    @__vc_set_slots[slot_name] ||= []
    @__vc_set_slots[slot_name].push(slot)
  else
    @__vc_set_slots[slot_name] = slot
  end

  slot
end