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.

Defined Under Namespace

Classes: State

Instance Attribute Summary collapse

Attributes inherited from Response

#body, #headers, #status

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Response

#cache!, #content_type=, #do_not_cache!, #lookup, #to_a

Constructor Details

#initialize(request, attributes = {}) ⇒ Document

Returns a new instance of Document.



29
30
31
32
33
34
35
36
37
38
39
# File 'lib/utopia/content/document.rb', line 29

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

Instance Attribute Details

#attributesObject (readonly)

Per-document global attributes.



93
94
95
# File 'lib/utopia/content/document.rb', line 93

def attributes
  @attributes
end

#currentObject (readonly)

The current state, represents a list from outer to inner most tag by traversing Utopia::Content::Document::State#parent. At any point in parsing markup, this is a list of the inner most tag, then the next outer tag, etc.



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

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.



106
107
108
# File 'lib/utopia/content/document.rb', line 106

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.



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

def first
  @first
end

#requestObject (readonly)

The Rack::Request for this document.



90
91
92
# File 'lib/utopia/content/document.rb', line 90

def request
  @request
end

Class Method Details

.render(node, request, attributes) ⇒ Object



25
26
27
# File 'lib/utopia/content/document.rb', line 25

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

Instance Method Details

#[](key) ⇒ Object



62
63
64
# File 'lib/utopia/content/document.rb', line 62

def [] key
	@attributes[key]
end

#[]=(key, value) ⇒ Object



66
67
68
# File 'lib/utopia/content/document.rb', line 66

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” | “../..” |



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

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



223
224
225
# File 'lib/utopia/content/document.rb', line 223

def content
	@end_tags.last.content
end

#controllerObject

A helper method for accessing controller variables from view:



77
78
79
# File 'lib/utopia/content/document.rb', line 77

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

#localizationObject



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

def localization
	@localization ||= Utopia::Localization[request]
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.



216
217
218
219
220
# File 'lib/utopia/content/document.rb', line 216

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.



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/utopia/content/document.rb', line 196

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



227
228
229
# File 'lib/utopia/content/document.rb', line 227

def parent
	@end_tags[-2]
end

#parse_markup(markup) ⇒ Object



85
86
87
# File 'lib/utopia/content/document.rb', line 85

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

#render!(node, attributes) ⇒ Object



70
71
72
73
74
# File 'lib/utopia/content/document.rb', line 70

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

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



184
185
186
187
188
189
190
191
192
# File 'lib/utopia/content/document.rb', line 184

def render_node(node, attributes = {})
	@current = State.new(@current, nil, node, attributes)
	
	# 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



42
43
44
# File 'lib/utopia/content/document.rb', line 42

def request_path
	Path[request.env["REQUEST_PATH"]]
end

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



108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/utopia/content/document.rb', line 108

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



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/utopia/content/document.rb', line 132

def tag_begin(tag, node = nil)
	node ||= lookup_tag(tag)

	if node
		@current = State.new(@current, tag, node)

		node.tag_begin(self, state) 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



121
122
123
124
125
126
127
128
129
130
# File 'lib/utopia/content/document.rb', line 121

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



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/utopia/content/document.rb', line 160

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



156
157
158
# File 'lib/utopia/content/document.rb', line 156

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

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



150
151
152
# File 'lib/utopia/content/document.rb', line 150

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