class Document
A single request through content middleware. We use a struct to hide instance varibles since we instance_exec within this context.
Definitions
def self.render(node, request, attributes)
Render a content node into a new document.
Signature
-
parameter
nodeUtopia::Content::Node The content node.
-
parameter
requestRack::Request The request.
-
parameter
attributesHash The attributes.
-
returns
Document The rendered document.
Implementation
def self.render(node, request, attributes)
self.new(request, attributes).render!(node, attributes)
end
def initialize(request, attributes = {})
Initialize a document for a protocol request.
Signature
-
parameter
requestRack::Request The request.
-
parameter
attributesHash The attributes.
Implementation
def initialize(request, attributes = {})
@request = request
@attributes = attributes
@first = nil
@current = nil
@end_tags = []
super()
end
def request_path
Signature
-
returns
Path The original request path, if known.
Implementation
def request_path
Path[request.env["REQUEST_PATH"]]
end
def base_uri(relative_to = self.current_base_uri_path)
Compute the relative path from the curent base uri (e.g. the node being rendered) to the request uri. This path can be used to ensure resources are loaded relative to a given path.
| Relative To | Request Path | Base URI |
|---|---|---|
| "/page" | "/index" | "" |
| "/blog/entry" | "/blog/2025/05/my-cat" | "../.." |
Signature
-
returns
String the base uri for the current page.
Implementation
def base_uri(relative_to = self.current_base_uri_path)
Path[relative_to].dirname.shortest_path(request_path)
end
def [](key)
Fetch a document-global attribute.
Signature
-
parameter
keyString | Symbol The lookup key.
-
returns
Object | Nil The document attribute.
Implementation
def [] key
@attributes[key]
end
def []=(key, value)
Assign a document-global attribute.
Signature
-
parameter
keyString | Symbol The lookup key.
-
parameter
valueObject The value to assign.
-
returns
Object The assigned value.
Implementation
def []= key, value
@attributes[key] = value
end
def render!(node, attributes)
Render.
Signature
-
parameter
nodeUtopia::Content::Node The content node.
-
parameter
attributesHash The attributes.
-
returns
self This object.
Implementation
def render!(node, attributes)
@body << render_node(node, attributes)
return self
end
def controller
A helper method for accessing controller variables from view:
Implementation
def controller
@controller ||= Utopia::Controller[request]
end
def localization
Return a localization wrapper for the current request.
Signature
-
returns
Localization::Wrapper The localization wrapper.
Implementation
def localization
@localization ||= Utopia::Localization[request]
end
def parse_markup(markup)
Parse markup into this document.
Signature
-
parameter
markupString The markup.
-
returns
Nil Parsing completes through document callbacks.
Implementation
def parse_markup(markup)
MarkupParser.parse(markup, self)
end
attr :request
The Rack::Request for this document.
attr :attributes
Per-document global attributes.
attr :current
The current state, represents a list from outer to inner most tag by traversing State#parent.
At any point in parsing markup, this is a list of the inner most tag,
then the next outer tag, etc.
attr :first
The first State generated by rendering this document. It contains useful information
regarding the node and uri used to access the resource.
def tag(name, attributes = {})
Render a complete or block-delimited tag.
Signature
-
parameter
nameString The name.
-
parameter
attributesHash The attributes.
-
yields
{|node| ...} The node selected to render a block-delimited tag.
-
returns
Object | Nil The completed tag result.
Implementation
def tag(name, attributes = {})
# If we provide a block which can give inner data, we are not self-closing.
tag = Tag.new(name, !block_given?, attributes)
if block_given?
node = tag_begin(tag)
yield node
tag_end(tag)
else
tag_complete(tag, node)
end
end
def tag_complete(tag, node = nil)
Render a complete tag through a matching content node or the current builder.
Signature
-
parameter
tagXRB::Tag The tag.
-
parameter
nodeUtopia::Content::Node The content node.
-
returns
Object The builder's completion result.
Implementation
def tag_complete(tag, node = nil)
node ||= lookup_tag(tag)
if node
tag_begin(tag, node)
tag_end(tag)
else
@current.tag_complete(tag)
end
end
def tag_begin(tag, node = nil)
Begin a tag through a matching content node or the current builder.
Signature
-
parameter
tagXRB::Tag The tag.
-
parameter
nodeUtopia::Content::Node The content node.
-
returns
Node | Nil The content node selected for the tag.
Implementation
def tag_begin(tag, node = nil)
node ||= lookup_tag(tag)
if node
@current = Builder.new(@current, tag, node, tag.to_hash, indent: false)
node.tag_begin(self, @current) if node.respond_to?(:tag_begin)
return node
end
# raise ArgumentError.new("tag_begin: #{tag} is tag.self_closed?") if tag.self_closed?
@current.tag_begin(tag)
return nil
end
def write(string)
Append raw content to the current builder.
Signature
-
parameter
stringString The string.
-
returns
String The current output buffer.
Implementation
def write(string)
@current.write(string)
end
def text(string)
Process text content.
Signature
-
parameter
stringString The string.
-
returns
Object | Nil The builder's text result.
Implementation
def text(string)
@current.text(string)
end
def tag_end(tag = nil)
Complete the current content node or close a nested markup tag.
Signature
-
parameter
tagXRB::Tag | Nil The nested tag to close.
-
returns
String | Nil The completed node output, or
nilafter closing a nested tag.
Implementation
def tag_end(tag = nil)
# Determine if the current state contains tags that need to be completed, or if the state itself is finished.
if @current.empty?
if node = @current.node
node.tag_end(self, @current) if node.respond_to?(:tag_end)
end
@end_tags << @current
buffer = @current.call(self)
@current = @current.parent
@end_tags.pop
@current.write(buffer) if @current
return buffer
else
# raise ArgumentError.new("tag_begin: #{tag} is tag.self_closed?") if tag.self_closed?
@current.tag_end(tag)
end
return nil
end
def render_node(node, attributes = {})
Render a content node within the current builder state.
Signature
-
parameter
nodeUtopia::Content::Node The content node.
-
parameter
attributesHash The attributes.
-
returns
String The rendered node output.
Implementation
def render_node(node, attributes = {})
@current = Builder.new(@current, nil, node, attributes, indent: false)
# We keep track of the first thing rendered by this document.
@first ||= @current
# This returns the content of rendering the tag:
return tag_end
end
def lookup_tag(tag)
Maps a tag to a node instance by asking the current node to lookup the tag name. This function is called for each tag and thus heavily affects performance.
Implementation
def lookup_tag(tag)
# result = tag
#
# # This loop works from inner to outer tags, and updates the tag we are currently searching for based on any overrides:
# @begin_tags.reverse_each do |state|
# result = state.lookup(result)
#
# return result if result.is_a?(Node)
# end
# This loop looks up a tag by asking the most embedded node to look it up based on tag name. This almost always only evaluates the top state:
@end_tags.reverse_each do |state|
return state.node.lookup_tag(tag) if state.node.respond_to?(:lookup_tag)
end
return nil
end
def lookup_node(path)
Lookup a node with the given path relative to the current node.
Implementation
def lookup_node(path)
@end_tags.reverse_each do |state|
return state.node.lookup_node(path) if state.node.respond_to?(:lookup_node)
end
end
def content
The content of the node
Implementation
def content
@end_tags.last.content
end
def parent
Return the enclosing rendering state.
Signature
-
returns
Builder | Nil The parent rendering state.
Implementation
def parent
@end_tags[-2]
end