Class: Weft::Component

Inherits:
Arbre::Component
  • Object
show all
Extended by:
Registry::Eligibility
Includes:
Weft::Context::Interception, DSL::Actions, DSL::Attributes, DSL::Containers, DSL::Inclusions, DSL::Recoveries, DSL::Triggers, DSL::Updates
Defined in:
lib/weft/component.rb

Overview

Base class for all Weft components. Extends Arbre::Component with:

  • Attribute DSL for declaring wire state
  • Convention-based DOM ID and partial URL
  • Auto-registration with the global Registry

Class Attribute Summary collapse

Attributes included from DSL::Attributes

#attrs

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Registry::Eligibility

abstract!, routable!, routable?, stale?

Methods included from Weft::Context::Interception

#insert_tag

Methods included from DSL::Containers

behavior_for, included

Methods included from DSL::Actions

included

Methods included from DSL::Updates

included

Methods included from DSL::Inclusions

included

Methods included from DSL::Triggers

included

Methods included from DSL::Recoveries

included

Methods included from DSL::Attributes

included

Class Attribute Details

.component_pathObject

Class-level path override (string or proc). Inherited by subclasses.



24
25
26
27
28
29
30
# File 'lib/weft/component.rb', line 24

def component_path
  if instance_variable_defined?(:@component_path)
    @component_path
  elsif superclass.respond_to?(:component_path)
    superclass.component_path
  end
end

Class Method Details

.inferred_routable?Boolean

Inferred routability from declared state, ignoring any explicit override (see Weft::Registry::Eligibility#routable?). A component is independently addressable when it declares interactive behavior — attributes, actions, refresh triggers, or push config. Pure presentational components (none of those) register but are never served. Subclasses fall back to this when they have no override of their own, so an abstract parent does not disable concrete children.

Returns:

  • (Boolean)


52
53
54
# File 'lib/weft/component.rb', line 52

def inferred_routable?
  attributes.any? || actions.any? || refresh_triggers.any? || !push_config.nil?
end

.inherited(subclass) ⇒ Object



56
57
58
59
# File 'lib/weft/component.rb', line 56

def inherited(subclass)
  super
  Weft.registry.register(subclass)
end

.render(**attributes) ⇒ Object

Render this component as an HTML string, outside any Arbre DSL context. Used by the Router for partial responses, and available to users for testing, REPL exploration, or any standalone rendering need.

StatCard.render(status: "shipped")  # => "<div id=\"...\">...</div>"


66
67
68
69
70
71
# File 'lib/weft/component.rb', line 66

def render(**attributes)
  klass = self
  Weft::Context.new({}, nil) do
    insert_tag(klass, **attributes)
  end.to_s
end

.resolved_component_pathObject

Resolves the actual path string for this component class. An explicit class-level override (string or proc) wins; otherwise the configured default proc derives one from the class name (see default_component_path).



37
38
39
40
41
42
43
# File 'lib/weft/component.rb', line 37

def resolved_component_path
  case (path = component_path)
  when Proc then path.call(self)
  when String then path
  else default_component_path
  end
end

.weft_id_for(attrs = {}) ⇒ Object

Compute the would-be DOM ID for an instance of this class given a plain attrs hash, without instantiating. The Router uses this to populate the :component_id auto-injected attribute when a recovery target opts in. Single source of truth; the instance method delegates.



77
78
79
80
81
# File 'lib/weft/component.rb', line 77

def weft_id_for(attrs = {})
  base = name.underscore.tr("/", "-").tr("_", "-")
  primary_value = attrs.respond_to?(:values) ? attrs.values.first : nil
  primary_value ? "#{base}-#{primary_value}" : base
end

Instance Method Details

#build(attributes = {}) ⇒ Object



113
114
115
116
117
118
119
120
# File 'lib/weft/component.rb', line 113

def build(attributes = {})
  schema = self.class.attributes
  @attrs = Weft::Attributes.extract_from(attributes, using: schema)
  super(attributes.except(*schema.keys))
  self.id = weft_id
  apply_refresh_attrs
  apply_push_attrs
end

#weft_idObject

Convention-based DOM ID: dasherized class name + primary attribute value.



135
136
137
# File 'lib/weft/component.rb', line 135

def weft_id
  self.class.weft_id_for(@attrs ? @attrs.to_h : {})
end

#weft_url(**overrides) ⇒ Object

URL to this component's Weft route with current attrs as query params. Pass overrides to change specific attr values in the URL.

weft_url                          # => "/_components/orders_panel?status=shipped&page=1"
weft_url(page: 2)                 # => "/_components/orders_panel?status=shipped&page=2"
weft_url(status: nil, page: 1)    # => "/_components/orders_panel?page=1"


128
129
130
131
132
# File 'lib/weft/component.rb', line 128

def weft_url(**overrides)
  path = self.class.resolved_component_path
  params = @attrs.to_h.merge(overrides).compact
  params.empty? ? path : "#{path}?#{URI.encode_www_form(params)}"
end