UtopiaSourceUtopiaContentDocument

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 node Utopia::Content::Node

The content node.

parameter request Rack::Request

The request.

parameter attributes Hash

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 request Rack::Request

The request.

parameter attributes Hash

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 key String | 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 key String | Symbol

The lookup key.

parameter value Object

The value to assign.

returns Object

The assigned value.

Implementation

def []= key, value
	@attributes[key] = value
end

def render!(node, attributes)

Render.

Signature

parameter node Utopia::Content::Node

The content node.

parameter attributes Hash

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 markup String

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.

attr :end_tags

End tags represents a list of execution order. This is the order that end tags have appeared when evaluating nodes.

def tag(name, attributes = {})

Render a complete or block-delimited tag.

Signature

parameter name String

The name.

parameter attributes Hash

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 tag XRB::Tag

The tag.

parameter node Utopia::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 tag XRB::Tag

The tag.

parameter node Utopia::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 string String

The string.

returns String

The current output buffer.

Implementation

def write(string)
	@current.write(string)
end

def text(string)

Process text content.

Signature

parameter string String

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 tag XRB::Tag | Nil

The nested tag to close.

returns String | Nil

The completed node output, or nil after 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 node Utopia::Content::Node

The content node.

parameter attributes Hash

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