Class: Utopia::Content::Document

Inherits:
Response
  • Object
show all
Defined in:
lib/utopia/content/document.rb

Overview

A single request through content middleware. We use a struct to hide instance varibles since we instance_exec within this context.

Constant Summary

Constants included from Response

Response::CONTENT_TYPE, Response::LOCATION, Response::NotFound

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Response

[], html, redirect, text, wrap

Constructor Details

#initialize(request, attributes = {}, localization: request&.localization) ⇒ Document

Initialize a document for a protocol request.



43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/utopia/content/document.rb', line 43

def initialize(request, attributes = {}, localization: request&.localization)
	@request = request
	@localization = localization
	
	@attributes = attributes
	
	@first = nil
	@current = nil
	@end_tags = []
	
	super()
end

Instance Attribute Details

#attributesObject (readonly)

Per-document global attributes.



124
125
126
# File 'lib/utopia/content/document.rb', line 124

def attributes
  @attributes
end

#currentObject (readonly)

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.



129
130
131
# File 'lib/utopia/content/document.rb', line 129

def current
  @current
end

#end_tagsObject (readonly)

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



137
138
139
# File 'lib/utopia/content/document.rb', line 137

def end_tags
  @end_tags
end

#firstObject (readonly)

The first State generated by rendering this document. It contains useful information regarding the node and uri used to access the resource.



133
134
135
# File 'lib/utopia/content/document.rb', line 133

def first
  @first
end

#requestObject (readonly)

The request for this document.



121
122
123
# File 'lib/utopia/content/document.rb', line 121

def request
  @request
end

Class Method Details

.render(node, request, attributes, localization: request&.localization) ⇒ Object

Render a content node into a new document.



35
36
37
# File 'lib/utopia/content/document.rb', line 35

def self.render(node, request, attributes, localization: request&.localization)
	self.new(request, attributes, localization: localization).render!(node, attributes)
end

Instance Method Details

#[](key) ⇒ Object

Fetch a document-global attribute.



80
81
82
# File 'lib/utopia/content/document.rb', line 80

def [] key
	@attributes[key]
end

#[]=(key, value) ⇒ Object

Assign a document-global attribute.



88
89
90
# File 'lib/utopia/content/document.rb', line 88

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

#base_uri(relative_to = self.current_base_uri_path) ⇒ Object

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" "../.."


73
74
75
# File 'lib/utopia/content/document.rb', line 73

def base_uri(relative_to = self.current_base_uri_path)
	Path[relative_to].dirname.shortest_path(request_path)
end

#contentObject

The content of the node



280
281
282
# File 'lib/utopia/content/document.rb', line 280

def content
	@end_tags.last.content
end

#controllerObject

A helper method for accessing controller variables from view:



103
104
105
# File 'lib/utopia/content/document.rb', line 103

def controller
	@controller ||= Utopia::Controller[request]
end

#localizationObject

Return the selected localization preferences for this document.



109
110
111
# File 'lib/utopia/content/document.rb', line 109

def localization
	@localization
end

#lookup_node(path) ⇒ Node

Lookup a node with the given path relative to the current node.

Returns:

  • (Node)

    The node if could be found.



273
274
275
276
277
# File 'lib/utopia/content/document.rb', line 273

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

#lookup_tag(tag) ⇒ Node

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.

Returns:

  • (Node)

    The node for the given tag.



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/utopia/content/document.rb', line 253

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

#parentObject

Return the enclosing rendering state.



286
287
288
# File 'lib/utopia/content/document.rb', line 286

def parent
	@end_tags[-2]
end

#parse_markup(markup) ⇒ Object

Parse markup into this document.



116
117
118
# File 'lib/utopia/content/document.rb', line 116

def parse_markup(markup)
	MarkupParser.parse(markup, self)
end

#render!(node, attributes) ⇒ Object

Render.



96
97
98
99
100
# File 'lib/utopia/content/document.rb', line 96

def render!(node, attributes)
	@body << render_node(node, attributes)
	
	return self
end

#render_node(node, attributes = {}) ⇒ Object

Render a content node within the current builder state.



241
242
243
244
245
246
247
248
249
# File 'lib/utopia/content/document.rb', line 241

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

#request_pathObject



57
58
59
# File 'lib/utopia/content/document.rb', line 57

def request_path
	request.request_path
end

#tag(name, attributes = {}) ⇒ Object

Render a complete or block-delimited tag.



144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/utopia/content/document.rb', line 144

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

#tag_begin(tag, node = nil) ⇒ Object

Begin a tag through a matching content node or the current builder.



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/utopia/content/document.rb', line 176

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

#tag_complete(tag, node = nil) ⇒ Object

Render a complete tag through a matching content node or the current builder.



161
162
163
164
165
166
167
168
169
170
# File 'lib/utopia/content/document.rb', line 161

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

#tag_end(tag = nil) ⇒ Object

Complete the current content node or close a nested markup tag.



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/utopia/content/document.rb', line 213

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

#text(string) ⇒ Object

Process text content.



206
207
208
# File 'lib/utopia/content/document.rb', line 206

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

#write(string) ⇒ Object Also known as: cdata

Append raw content to the current builder.



197
198
199
# File 'lib/utopia/content/document.rb', line 197

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