utopia 2.30.2 → 2.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/bake/utopia/server.rb +1 -1
- data/bake/utopia/site.rb +3 -3
- data/context/getting-started.md +93 -0
- data/context/index.yaml +32 -0
- data/context/integrating-with-javascript.md +75 -0
- data/context/middleware.md +157 -0
- data/context/server-setup.md +116 -0
- data/context/updating-utopia.md +69 -0
- data/context/what-is-xnode.md +41 -0
- data/lib/utopia/content/document.rb +39 -37
- data/lib/utopia/content/link.rb +1 -2
- data/lib/utopia/content/links.rb +2 -2
- data/lib/utopia/content/markup.rb +10 -10
- data/lib/utopia/content/middleware.rb +195 -0
- data/lib/utopia/content/namespace.rb +1 -1
- data/lib/utopia/content/node.rb +1 -1
- data/lib/utopia/content/response.rb +1 -1
- data/lib/utopia/content/tags.rb +1 -1
- data/lib/utopia/content.rb +4 -186
- data/lib/utopia/controller/actions.md +8 -8
- data/lib/utopia/controller/actions.rb +1 -1
- data/lib/utopia/controller/base.rb +4 -4
- data/lib/utopia/controller/middleware.rb +133 -0
- data/lib/utopia/controller/respond.rb +2 -46
- data/lib/utopia/controller/responder.rb +103 -0
- data/lib/utopia/controller/rewrite.md +2 -2
- data/lib/utopia/controller/rewrite.rb +1 -1
- data/lib/utopia/controller/variables.rb +11 -5
- data/lib/utopia/controller.rb +4 -126
- data/lib/utopia/exceptions/mailer.rb +4 -4
- data/lib/utopia/extensions/array_split.rb +2 -2
- data/lib/utopia/extensions/date_comparisons.rb +3 -3
- data/lib/utopia/import_map.rb +374 -0
- data/lib/utopia/localization/middleware.rb +173 -0
- data/lib/utopia/localization/wrapper.rb +52 -0
- data/lib/utopia/localization.rb +4 -202
- data/lib/utopia/path.rb +26 -11
- data/lib/utopia/redirection.rb +2 -2
- data/lib/utopia/session/lazy_hash.rb +1 -1
- data/lib/utopia/session/middleware.rb +218 -0
- data/lib/utopia/session/serialization.rb +1 -1
- data/lib/utopia/session.rb +4 -205
- data/lib/utopia/static/local_file.rb +19 -19
- data/lib/utopia/static/middleware.rb +120 -0
- data/lib/utopia/static/mime_types.rb +1 -1
- data/lib/utopia/static.rb +4 -108
- data/lib/utopia/version.rb +1 -1
- data/lib/utopia.rb +1 -0
- data/readme.md +7 -0
- data/releases.md +7 -0
- data/setup/site/config.ru +1 -1
- data.tar.gz.sig +0 -0
- metadata +31 -4
- metadata.gz.sig +0 -0
- data/lib/utopia/locale.rb +0 -29
- data/lib/utopia/responder.rb +0 -59
|
@@ -8,7 +8,9 @@ require_relative "response"
|
|
|
8
8
|
require_relative "markup"
|
|
9
9
|
|
|
10
10
|
module Utopia
|
|
11
|
-
|
|
11
|
+
module Content
|
|
12
|
+
DEFERRED_TAG_NAME = "utopia:deferred".freeze
|
|
13
|
+
|
|
12
14
|
# This error is raised if a tag doesn't match up when parsing.
|
|
13
15
|
class UnbalancedTagError < StandardError
|
|
14
16
|
def initialize(tag)
|
|
@@ -81,34 +83,34 @@ module Utopia
|
|
|
81
83
|
def localization
|
|
82
84
|
@localization ||= Utopia::Localization[request]
|
|
83
85
|
end
|
|
84
|
-
|
|
86
|
+
|
|
85
87
|
def parse_markup(markup)
|
|
86
88
|
MarkupParser.parse(markup, self)
|
|
87
89
|
end
|
|
88
|
-
|
|
90
|
+
|
|
89
91
|
# The Rack::Request for this document.
|
|
90
92
|
attr :request
|
|
91
|
-
|
|
93
|
+
|
|
92
94
|
# Per-document global attributes.
|
|
93
95
|
attr :attributes
|
|
94
|
-
|
|
96
|
+
|
|
95
97
|
# The current state, represents a list from outer to inner most tag by traversing {State#parent}.
|
|
96
98
|
# At any point in parsing markup, this is a list of the inner most tag,
|
|
97
99
|
# then the next outer tag, etc.
|
|
98
100
|
attr :current
|
|
99
|
-
|
|
101
|
+
|
|
100
102
|
# The first {State} generated by rendering this document. It contains useful information
|
|
101
103
|
# regarding the node and uri used to access the resource.
|
|
102
104
|
attr :first
|
|
103
|
-
|
|
105
|
+
|
|
104
106
|
# End tags represents a list of execution order. This is the order that end tags
|
|
105
107
|
# have appeared when evaluating nodes.
|
|
106
108
|
attr :end_tags
|
|
107
|
-
|
|
109
|
+
|
|
108
110
|
def tag(name, attributes = {})
|
|
109
111
|
# If we provide a block which can give inner data, we are not self-closing.
|
|
110
112
|
tag = Tag.new(name, !block_given?, attributes)
|
|
111
|
-
|
|
113
|
+
|
|
112
114
|
if block_given?
|
|
113
115
|
node = tag_begin(tag)
|
|
114
116
|
yield node
|
|
@@ -117,10 +119,10 @@ module Utopia
|
|
|
117
119
|
tag_complete(tag, node)
|
|
118
120
|
end
|
|
119
121
|
end
|
|
120
|
-
|
|
122
|
+
|
|
121
123
|
def tag_complete(tag, node = nil)
|
|
122
124
|
node ||= lookup_tag(tag)
|
|
123
|
-
|
|
125
|
+
|
|
124
126
|
if node
|
|
125
127
|
tag_begin(tag, node)
|
|
126
128
|
tag_end(tag)
|
|
@@ -128,22 +130,22 @@ module Utopia
|
|
|
128
130
|
@current.tag_complete(tag)
|
|
129
131
|
end
|
|
130
132
|
end
|
|
131
|
-
|
|
133
|
+
|
|
132
134
|
def tag_begin(tag, node = nil)
|
|
133
135
|
node ||= lookup_tag(tag)
|
|
134
|
-
|
|
136
|
+
|
|
135
137
|
if node
|
|
136
138
|
@current = State.new(@current, tag, node)
|
|
137
|
-
|
|
139
|
+
|
|
138
140
|
node.tag_begin(self, state) if node.respond_to?(:tag_begin)
|
|
139
|
-
|
|
141
|
+
|
|
140
142
|
return node
|
|
141
143
|
end
|
|
142
|
-
|
|
144
|
+
|
|
143
145
|
# raise ArgumentError.new("tag_begin: #{tag} is tag.self_closed?") if tag.self_closed?
|
|
144
|
-
|
|
146
|
+
|
|
145
147
|
@current.tag_begin(tag)
|
|
146
|
-
|
|
148
|
+
|
|
147
149
|
return nil
|
|
148
150
|
end
|
|
149
151
|
|
|
@@ -152,35 +154,35 @@ module Utopia
|
|
|
152
154
|
end
|
|
153
155
|
|
|
154
156
|
alias cdata write
|
|
155
|
-
|
|
157
|
+
|
|
156
158
|
def text(string)
|
|
157
159
|
@current.text(string)
|
|
158
160
|
end
|
|
159
|
-
|
|
161
|
+
|
|
160
162
|
def tag_end(tag = nil)
|
|
161
163
|
# Determine if the current state contains tags that need to be completed, or if the state itself is finished.
|
|
162
164
|
if @current.empty?
|
|
163
165
|
if node = @current.node
|
|
164
166
|
node.tag_end(self, @current) if node.respond_to?(:tag_end)
|
|
165
167
|
end
|
|
166
|
-
|
|
168
|
+
|
|
167
169
|
@end_tags << @current
|
|
168
170
|
buffer = @current.call(self)
|
|
169
|
-
|
|
171
|
+
|
|
170
172
|
@current = @current.parent
|
|
171
173
|
@end_tags.pop
|
|
172
|
-
|
|
174
|
+
|
|
173
175
|
@current.write(buffer) if @current
|
|
174
|
-
|
|
176
|
+
|
|
175
177
|
return buffer
|
|
176
178
|
else
|
|
177
179
|
# raise ArgumentError.new("tag_begin: #{tag} is tag.self_closed?") if tag.self_closed?
|
|
178
180
|
@current.tag_end(tag)
|
|
179
181
|
end
|
|
180
|
-
|
|
182
|
+
|
|
181
183
|
return nil
|
|
182
184
|
end
|
|
183
|
-
|
|
185
|
+
|
|
184
186
|
def render_node(node, attributes = {})
|
|
185
187
|
@current = State.new(@current, nil, node, attributes)
|
|
186
188
|
|
|
@@ -190,7 +192,7 @@ module Utopia
|
|
|
190
192
|
# This returns the content of rendering the tag:
|
|
191
193
|
return tag_end
|
|
192
194
|
end
|
|
193
|
-
|
|
195
|
+
|
|
194
196
|
# 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.
|
|
195
197
|
# @return [Node] The node for the given tag.
|
|
196
198
|
def lookup_tag(tag)
|
|
@@ -223,7 +225,7 @@ module Utopia
|
|
|
223
225
|
def content
|
|
224
226
|
@end_tags.last.content
|
|
225
227
|
end
|
|
226
|
-
|
|
228
|
+
|
|
227
229
|
def parent
|
|
228
230
|
@end_tags[-2]
|
|
229
231
|
end
|
|
@@ -244,7 +246,7 @@ module Utopia
|
|
|
244
246
|
|
|
245
247
|
@tags = []
|
|
246
248
|
end
|
|
247
|
-
|
|
249
|
+
|
|
248
250
|
attr :parent
|
|
249
251
|
attr :attributes
|
|
250
252
|
attr :content
|
|
@@ -252,9 +254,9 @@ module Utopia
|
|
|
252
254
|
|
|
253
255
|
# A list of all tags in order of rendering them, which have not been finished yet.
|
|
254
256
|
attr :tags
|
|
255
|
-
|
|
257
|
+
|
|
256
258
|
attr :deferred
|
|
257
|
-
|
|
259
|
+
|
|
258
260
|
def defer(value = nil, &block)
|
|
259
261
|
@deferred << block
|
|
260
262
|
|
|
@@ -264,7 +266,7 @@ module Utopia
|
|
|
264
266
|
def [](key)
|
|
265
267
|
@attributes[key]
|
|
266
268
|
end
|
|
267
|
-
|
|
269
|
+
|
|
268
270
|
def call(document)
|
|
269
271
|
@content = @buffer
|
|
270
272
|
@buffer = XRB::MarkupString.new.force_encoding(Encoding::UTF_8)
|
|
@@ -277,15 +279,15 @@ module Utopia
|
|
|
277
279
|
|
|
278
280
|
return @buffer
|
|
279
281
|
end
|
|
280
|
-
|
|
282
|
+
|
|
281
283
|
def write(string)
|
|
282
284
|
@buffer << string
|
|
283
285
|
end
|
|
284
|
-
|
|
286
|
+
|
|
285
287
|
def text(string)
|
|
286
288
|
XRB::Markup.append(@buffer, string)
|
|
287
289
|
end
|
|
288
|
-
|
|
290
|
+
|
|
289
291
|
def tag_complete(tag)
|
|
290
292
|
tag.write(@buffer)
|
|
291
293
|
end
|
|
@@ -294,14 +296,14 @@ module Utopia
|
|
|
294
296
|
def empty?
|
|
295
297
|
@tags.empty?
|
|
296
298
|
end
|
|
297
|
-
|
|
299
|
+
|
|
298
300
|
def tag_begin(tag)
|
|
299
301
|
# raise ArgumentError.new("tag_begin: #{tag} is tag.self_closed?") if tag.self_closed?
|
|
300
302
|
|
|
301
303
|
@tags << tag
|
|
302
304
|
tag.write_opening_tag(@buffer)
|
|
303
305
|
end
|
|
304
|
-
|
|
306
|
+
|
|
305
307
|
def tag_end(tag)
|
|
306
308
|
raise UnbalancedTagError.new(tag) unless @tags.pop.name == tag.name
|
|
307
309
|
tag.write_closing_tag(@buffer)
|
data/lib/utopia/content/link.rb
CHANGED
|
@@ -11,10 +11,9 @@ require "xrb/builder"
|
|
|
11
11
|
require "xrb/strings"
|
|
12
12
|
|
|
13
13
|
require_relative "../path"
|
|
14
|
-
require_relative "../locale"
|
|
15
14
|
|
|
16
15
|
module Utopia
|
|
17
|
-
|
|
16
|
+
module Content
|
|
18
17
|
# Represents a link to some content with associated metadata.
|
|
19
18
|
class Link
|
|
20
19
|
# @param kind [Symbol] the kind of link.
|
data/lib/utopia/content/links.rb
CHANGED
|
@@ -8,7 +8,7 @@ require_relative "link"
|
|
|
8
8
|
require "concurrent/map"
|
|
9
9
|
|
|
10
10
|
module Utopia
|
|
11
|
-
|
|
11
|
+
module Content
|
|
12
12
|
# The file extension for markup nodes on disk.
|
|
13
13
|
XNODE_EXTENSION = ".xnode"
|
|
14
14
|
INDEX = "index"
|
|
@@ -114,7 +114,7 @@ module Utopia
|
|
|
114
114
|
def symbolize_keys(map)
|
|
115
115
|
# Second level attributes should be symbolic:
|
|
116
116
|
map.each do |key, info|
|
|
117
|
-
map[key] = info.each_with_object({})
|
|
117
|
+
map[key] = info.each_with_object({}){|(k,v),result| result[k.to_sym] = v}
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
return map
|
|
@@ -9,7 +9,7 @@ require "xrb/strings"
|
|
|
9
9
|
require "xrb/tag"
|
|
10
10
|
|
|
11
11
|
module Utopia
|
|
12
|
-
|
|
12
|
+
module Content
|
|
13
13
|
Tag = XRB::Tag
|
|
14
14
|
|
|
15
15
|
# A hash which forces all keys to be symbols and fails with KeyError when strings are used.
|
|
@@ -60,7 +60,7 @@ module Utopia
|
|
|
60
60
|
@opening_tag = opening_tag
|
|
61
61
|
@closing_tag = closing_tag
|
|
62
62
|
end
|
|
63
|
-
|
|
63
|
+
|
|
64
64
|
attr :buffer
|
|
65
65
|
attr :opening_tag
|
|
66
66
|
attr :closing_tag
|
|
@@ -108,15 +108,15 @@ module Utopia
|
|
|
108
108
|
raise UnbalancedTagError.new(@buffer, tag)
|
|
109
109
|
end
|
|
110
110
|
end
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
def open_tag_begin(name, offset)
|
|
113
113
|
@current = ParsedTag.new(name, offset)
|
|
114
114
|
end
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
def attribute(key, value)
|
|
117
117
|
@current.tag.attributes[key] = value
|
|
118
118
|
end
|
|
119
|
-
|
|
119
|
+
|
|
120
120
|
def open_tag_end(self_closing)
|
|
121
121
|
if self_closing
|
|
122
122
|
@current.tag.closed = true
|
|
@@ -128,7 +128,7 @@ module Utopia
|
|
|
128
128
|
|
|
129
129
|
@current = nil
|
|
130
130
|
end
|
|
131
|
-
|
|
131
|
+
|
|
132
132
|
def close_tag(name, offset)
|
|
133
133
|
@current = @stack.pop
|
|
134
134
|
tag = @current.tag
|
|
@@ -143,19 +143,19 @@ module Utopia
|
|
|
143
143
|
def doctype(string)
|
|
144
144
|
@delegate.write(string)
|
|
145
145
|
end
|
|
146
|
-
|
|
146
|
+
|
|
147
147
|
def comment(string)
|
|
148
148
|
@delegate.write(string)
|
|
149
149
|
end
|
|
150
|
-
|
|
150
|
+
|
|
151
151
|
def instruction(string)
|
|
152
152
|
@delegate.write(string)
|
|
153
153
|
end
|
|
154
|
-
|
|
154
|
+
|
|
155
155
|
def cdata(string)
|
|
156
156
|
@delegate.write(string[9..-4])
|
|
157
157
|
end
|
|
158
|
-
|
|
158
|
+
|
|
159
159
|
def text(string)
|
|
160
160
|
@delegate.text(string)
|
|
161
161
|
end
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Released under the MIT License.
|
|
4
|
+
# Copyright, 2009-2025, by Samuel Williams.
|
|
5
|
+
|
|
6
|
+
require_relative "../middleware"
|
|
7
|
+
require_relative "../localization"
|
|
8
|
+
|
|
9
|
+
require_relative "links"
|
|
10
|
+
require_relative "node"
|
|
11
|
+
require_relative "markup"
|
|
12
|
+
require_relative "tags"
|
|
13
|
+
|
|
14
|
+
require "xrb/template"
|
|
15
|
+
require "concurrent/map"
|
|
16
|
+
require "traces/provider"
|
|
17
|
+
|
|
18
|
+
module Utopia
|
|
19
|
+
module Content
|
|
20
|
+
# A middleware which serves dynamically generated content based on markup files.
|
|
21
|
+
class Middleware
|
|
22
|
+
CONTENT_NAMESPACE = "content".freeze
|
|
23
|
+
UTOPIA_NAMESPACE = "utopia".freeze
|
|
24
|
+
CONTENT_TAG_NAME = "utopia:content".freeze
|
|
25
|
+
|
|
26
|
+
# @param root [String] The content root where pages will be generated from.
|
|
27
|
+
# @param namespaces [Hash<String,Library>] Tag namespaces for dynamic tag lookup.
|
|
28
|
+
def initialize(app, root: Utopia::default_root, namespaces: {})
|
|
29
|
+
@app = app
|
|
30
|
+
@root = root
|
|
31
|
+
|
|
32
|
+
@template_cache = Concurrent::Map.new
|
|
33
|
+
@node_cache = Concurrent::Map.new
|
|
34
|
+
|
|
35
|
+
@links = Links.new(@root)
|
|
36
|
+
|
|
37
|
+
@namespaces = namespaces
|
|
38
|
+
|
|
39
|
+
# Default content namespace for dynamic path based lookup:
|
|
40
|
+
@namespaces[CONTENT_NAMESPACE] ||= self.method(:content_tag)
|
|
41
|
+
|
|
42
|
+
# The core namespace for utopia specific functionality:
|
|
43
|
+
@namespaces[UTOPIA_NAMESPACE] ||= Tags
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def freeze
|
|
47
|
+
return self if frozen?
|
|
48
|
+
|
|
49
|
+
@root.freeze
|
|
50
|
+
@namespaces.values.each(&:freeze)
|
|
51
|
+
@namespaces.freeze
|
|
52
|
+
|
|
53
|
+
super
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
attr :root
|
|
57
|
+
|
|
58
|
+
# TODO we should remove this method and expose `@links` directly.
|
|
59
|
+
def links(path, **options)
|
|
60
|
+
@links.index(path, **options)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def fetch_template(path)
|
|
64
|
+
@template_cache.fetch_or_store(path.to_s) do
|
|
65
|
+
XRB::Template.load_file(path)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Look up a named tag such as `<entry />` or `<content:page>...`
|
|
70
|
+
def lookup_tag(qualified_name, node)
|
|
71
|
+
namespace, name = XRB::Tag.split(qualified_name)
|
|
72
|
+
|
|
73
|
+
if library = @namespaces[namespace]
|
|
74
|
+
library.call(name, node)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @param path [Path] the request path is an absolute uri path, e.g. `/foo/bar`. If an xnode file exists on disk for this exact path, it is instantiated, otherwise nil.
|
|
79
|
+
def lookup_node(path, locale = nil)
|
|
80
|
+
resolve_link(
|
|
81
|
+
@links.for(path, locale)
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def resolve_link(link)
|
|
86
|
+
if full_path = link&.full_path(@root)
|
|
87
|
+
if File.exist?(full_path)
|
|
88
|
+
return Node.new(self, link.path, link.path, full_path)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def respond(link, request)
|
|
94
|
+
if node = resolve_link(link)
|
|
95
|
+
attributes = request.env.fetch(VARIABLES_KEY, {}).to_hash
|
|
96
|
+
|
|
97
|
+
return node.process!(request, attributes)
|
|
98
|
+
elsif redirect_uri = link[:uri]
|
|
99
|
+
return [307, {HTTP::LOCATION => redirect_uri}, []]
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def call(env)
|
|
104
|
+
request = Rack::Request.new(env)
|
|
105
|
+
path = Path.create(request.path_info)
|
|
106
|
+
|
|
107
|
+
# Check if the request is to a non-specific index. This only works for requests with a given name:
|
|
108
|
+
basename = path.basename
|
|
109
|
+
directory_path = File.join(@root, path.dirname.components, basename)
|
|
110
|
+
|
|
111
|
+
# If the request for /foo/bar is actually a directory, rewrite it to /foo/bar/index:
|
|
112
|
+
if File.directory? directory_path
|
|
113
|
+
index_path = [basename, INDEX]
|
|
114
|
+
|
|
115
|
+
return [307, {HTTP::LOCATION => path.dirname.join(index_path).to_s}, []]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
locale = env[Localization::CURRENT_LOCALE_KEY]
|
|
119
|
+
if link = @links.for(path, locale)
|
|
120
|
+
if response = self.respond(link, request)
|
|
121
|
+
return response
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
return @app.call(env)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
def lookup_content(name, parent_path)
|
|
131
|
+
if String === name && name.index("/")
|
|
132
|
+
name = Path.create(name)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
if Path === name
|
|
136
|
+
name = parent_path + name
|
|
137
|
+
name_path = name.components.dup
|
|
138
|
+
name_path[-1] += XNODE_EXTENSION
|
|
139
|
+
else
|
|
140
|
+
name_path = name + XNODE_EXTENSION
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
components = parent_path.components.dup
|
|
144
|
+
|
|
145
|
+
while components.any?
|
|
146
|
+
tag_path = File.join(@root, components, name_path)
|
|
147
|
+
|
|
148
|
+
if File.exist? tag_path
|
|
149
|
+
return Node.new(self, Path[components] + name, parent_path + name, tag_path)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
if String === name_path
|
|
153
|
+
tag_path = File.join(@root, components, "_" + name_path)
|
|
154
|
+
|
|
155
|
+
if File.exist? tag_path
|
|
156
|
+
return Node.new(self, Path[components] + name, parent_path + name, tag_path)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
components.pop
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
return nil
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def content_tag(name, node)
|
|
167
|
+
full_path = node.parent_path + name
|
|
168
|
+
|
|
169
|
+
name = full_path.pop
|
|
170
|
+
|
|
171
|
+
# If the current node is called 'foo', we can't lookup 'foo' in the current directory or we will have infinite recursion.
|
|
172
|
+
while full_path.last == name
|
|
173
|
+
full_path.pop
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
cache_key = full_path + name
|
|
177
|
+
|
|
178
|
+
@node_cache.fetch_or_store(cache_key) do
|
|
179
|
+
lookup_content(name, full_path)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
Traces::Provider(Middleware) do
|
|
185
|
+
def respond(link, request)
|
|
186
|
+
attributes = {
|
|
187
|
+
"link.key" => link.key,
|
|
188
|
+
"link.href" => link.href
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
Traces.trace("utopia.content.middleware.respond", attributes: attributes) {super}
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
data/lib/utopia/content/node.rb
CHANGED