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.

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.



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

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.



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

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.



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

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.



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

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.



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

def first
  @first
end

#requestObject (readonly)

The Rack::Request for this document.



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

def request
  @request
end

Class Method Details

.render(node, request, attributes) ⇒ Object



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

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

Instance Method Details

#[](key) ⇒ Object



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

def [] key
  @attributes[key]
end

#[]=(key, value) ⇒ Object



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

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



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

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



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

def content
  @end_tags.last.content
end

#controllerObject

A helper method for accessing controller variables from view:



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

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

#localizationObject



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

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.



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

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.



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

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



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

def parent
  @end_tags[-2]
end

#parse_markup(markup) ⇒ Object



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

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

#render!(node, attributes) ⇒ Object



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

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

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



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

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



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

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

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



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

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



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

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



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

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



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

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



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

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

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



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

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