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.



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

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.



95
96
97
# File 'lib/utopia/content/document.rb', line 95

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.



100
101
102
# File 'lib/utopia/content/document.rb', line 100

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.



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

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.



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

def first
  @first
end

#requestObject (readonly)

The Rack::Request for this document.



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

def request
  @request
end

Class Method Details

.render(node, request, attributes) ⇒ Object



27
28
29
# File 'lib/utopia/content/document.rb', line 27

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

Instance Method Details

#[](key) ⇒ Object



64
65
66
# File 'lib/utopia/content/document.rb', line 64

def [] key
	@attributes[key]
end

#[]=(key, value) ⇒ Object



68
69
70
# File 'lib/utopia/content/document.rb', line 68

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



60
61
62
# File 'lib/utopia/content/document.rb', line 60

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



225
226
227
# File 'lib/utopia/content/document.rb', line 225

def content
	@end_tags.last.content
end

#controllerObject

A helper method for accessing controller variables from view:



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

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

#localizationObject



83
84
85
# File 'lib/utopia/content/document.rb', line 83

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.



218
219
220
221
222
# File 'lib/utopia/content/document.rb', line 218

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.



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

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



229
230
231
# File 'lib/utopia/content/document.rb', line 229

def parent
	@end_tags[-2]
end

#parse_markup(markup) ⇒ Object



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

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

#render!(node, attributes) ⇒ Object



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

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

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



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

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



44
45
46
# File 'lib/utopia/content/document.rb', line 44

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

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



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

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



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

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



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

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



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

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



158
159
160
# File 'lib/utopia/content/document.rb', line 158

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

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



152
153
154
# File 'lib/utopia/content/document.rb', line 152

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