Class: Weft::Page

Inherits:
Arbre::Component
  • Object
show all
Extended by:
Registry::Eligibility
Includes:
Context::Interception, Context::Traversal, DSL::Containers, DSL::Params, DSL::Recoveries, Assets, Head
Defined in:
lib/weft/page.rb,
lib/weft/page/head.rb,
lib/weft/page/assets.rb

Overview

Document shell component. Renders the full HTML skeleton (doctype, head, body); the head surface — the title verb and the registered scripts and stylesheets — lives in Page::Head and Page::Assets. Subclass to add application-specific assets and CSS.

Pages auto-route via page_path declarations or class-name inference. The Router serves them as full documents at the resolved URL patterns.

class OrderDetailPage < Weft::Page
self.page_path = "/orders/:order_id"
param :order_id
end

Subclasses without an explicit page_path auto-infer one from the class name: the demodulized name, snake-cased, with any trailing "Page" suffix stripped (DashboardPage and Dashboard both route at "/dashboard"). Use abstract! to opt out — typical for an intermediate base class that hosts shared assets and helpers but isn't itself a destination.

Direct Known Subclasses

Defaults::ErrorPage, Defaults::NotFoundPage

Defined Under Namespace

Modules: Assets, Head

Constant Summary

Constants included from Assets

Assets::HTMX_ATTRS, Assets::HTMX_SRC, Assets::HTMX_SSE_ATTRS, Assets::HTMX_SSE_SRC

Class Attribute Summary collapse

Attributes included from DSL::Params

#params

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Registry::Eligibility

abstract!, routable!, routable?

Methods included from Head

included

Methods included from Assets

included

Methods included from Context::Traversal

#closest, #closest!, #enclosing, #enclosing!

Methods included from DSL::Containers

behavior_for, included

Methods included from DSL::Recoveries

included

Methods included from DSL::Params

included, #serializable_params, warned_collisions

Methods included from Context::Interception

#insert_tag

Constructor Details

#initializePage

Params resolve at construction (see Weft::Component#initialize) so user build bodies can read them before super — e.g. computing body chrome from a record looked up by param.



157
158
159
160
# File 'lib/weft/page.rb', line 157

def initialize(*)
  super
  @params = assembled_params if self.class.declared_keys.any?
end

Class Attribute Details

.page_pathObject

Class-level page path pattern. Sinatra-style string with :param segments. Bidirectional: forward (interpolate params → URL) and reverse (match request → params).

self.page_path = "/orders/:order_id"


59
60
61
62
63
64
65
# File 'lib/weft/page.rb', line 59

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

Class Method Details

.inferred_routable?Boolean

Inferred routability from declared state, ignoring any explicit override. Subclasses fall back to this when they have no override of their own, so an abstract parent does not disable concrete children.

A page is inferred-routable if it has an explicit page_path, or if its class name yields a usable default — i.e. the demodulized name has a non-empty stem after stripping any trailing "Page" suffix. The suffix is optional: FooBarPage and BazBar both route. Pages with params are not inferred-routable; they require an explicit page_path (a parameterized route can't be derived from the name; see default_page_path).

Returns:

  • (Boolean)


109
110
111
112
113
114
# File 'lib/weft/page.rb', line 109

def inferred_routable?
  return true if instance_variable_defined?(:@page_path)
  return false if params.any?

  !name.to_s.delete_suffix("Page").demodulize.empty?
end

.inherited(subclass) ⇒ Object



116
117
118
119
# File 'lib/weft/page.rb', line 116

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

.path_param_keysObject



94
95
96
97
# File 'lib/weft/page.rb', line 94

def path_param_keys
  pattern = page_path || default_page_path
  pattern.scan(/:(\w+)/).flatten.map(&:to_sym)
end

.redirect_url(params = {}) ⇒ Object

Build a redirect URL targeting this page with the given params. Path :param segments interpolate from params; declared-but-not-path params become query string entries. Anything not in the page's declared schema is discarded — never leaks into the URL.

class OrderDetailPage < Weft::Page
self.page_path = "/orders/:order_id"
param :order_id
param :highlight_section
end
OrderDetailPage.redirect_url(order_id: 42, highlight_section: "items", junk: "x")
# => "/orders/42?highlight_section=items"


88
89
90
91
92
# File 'lib/weft/page.rb', line 88

def redirect_url(params = {})
  path = resolve_page_path(params)
  query = params.slice(*(self.params.keys - path_param_keys)).compact
  query.empty? ? path : "#{path}?#{::URI.encode_www_form(query)}"
end

.render(**wire_params) ⇒ Object

Render this page as a full HTML document outside any Arbre DSL context. The kwargs are pseudo-wire: exactly what a request's query/path params would carry. Used by the Router for full-document responses, and available to users for testing or standalone rendering.



125
126
127
128
129
130
# File 'lib/weft/page.rb', line 125

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

.resolve_page_path(params = {}) ⇒ Object

Resolve the page path by interpolating params into the pattern. OrderDetailPage.resolve_page_path(order_id: "42") # => "/orders/42"



71
72
73
74
# File 'lib/weft/page.rb', line 71

def resolve_page_path(params = {})
  pattern = page_path || default_page_path
  pattern.gsub(/:(\w+)/) { params[::Regexp.last_match(1).to_sym] || ":#{::Regexp.last_match(1)}" }
end

Instance Method Details

#add_child(child) ⇒ Object



173
174
175
# File 'lib/weft/page.rb', line 173

def add_child(child)
  @body_el ? (@body_el << child) : super
end

#build(attributes = {}) ⇒ Object



162
163
164
165
166
167
# File 'lib/weft/page.rb', line 162

def build(attributes = {})
  warn_declared_chrome_collisions(attributes)
  super
  build_head
  @body_el = insert_tag(Arbre::HTML::Body)
end

#tag_nameObject



169
170
171
# File 'lib/weft/page.rb', line 169

def tag_name
  "html"
end

#to_sObject



177
178
179
# File 'lib/weft/page.rb', line 177

def to_s
  "<!DOCTYPE html>\n#{super}"
end

#weft_page(*args, &block) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Arbre builder for the Weft page element. Internal plumbing — authors subclass Weft::Page and render via the Router; they do not call this.



52
# File 'lib/weft/page.rb', line 52

builder_method :weft_page