utopia 1.9.11 → 2.0.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
- data/.codeclimate.yml +3 -2
- data/.gitignore +4 -1
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/.yardopts +2 -0
- data/Gemfile +8 -1
- data/README.md +2 -2
- data/Rakefile +10 -10
- data/benchmarks/call_vs_check.rb +36 -0
- data/benchmarks/const_vs_hash.rb +33 -0
- data/documentation/Gemfile +5 -0
- data/documentation/Guardfile +20 -0
- data/documentation/config.ru +6 -13
- data/documentation/config/puma.rb +20 -0
- data/documentation/pages/_editor.xnode +64 -0
- data/documentation/pages/_heading.xnode +2 -2
- data/documentation/pages/_page.xnode +1 -2
- data/documentation/pages/errors/exception.xnode +3 -3
- data/documentation/pages/errors/file-not-found.xnode +3 -3
- data/documentation/pages/wiki/bower-integration/content.md +1 -1
- data/documentation/pages/wiki/content.md +6 -8
- data/documentation/pages/wiki/controller.rb +3 -3
- data/documentation/pages/wiki/edit.xnode +7 -19
- data/documentation/pages/wiki/middleware/content/content.md +4 -10
- data/documentation/pages/wiki/{controller → middleware/controller}/actions/content.md +0 -0
- data/documentation/pages/wiki/{controller → middleware/controller}/links.yaml +0 -0
- data/documentation/pages/wiki/{controller → middleware/controller}/rewrite/content.md +3 -3
- data/documentation/pages/wiki/show.xnode +4 -6
- data/documentation/pages/wiki/updating-utopia/content.md +55 -0
- data/documentation/pages/wiki/your-first-page/content.md +5 -3
- data/documentation/public/materials +1 -0
- data/lib/utopia.rb +3 -4
- data/lib/utopia/command.rb +4 -284
- data/lib/utopia/command/server.rb +115 -0
- data/lib/utopia/command/setup.rb +78 -0
- data/lib/utopia/command/site.rb +183 -0
- data/lib/utopia/content.rb +83 -59
- data/lib/utopia/content/{transaction.rb → document.rb} +116 -110
- data/lib/utopia/content/link.rb +7 -2
- data/lib/utopia/content/links.rb +2 -1
- data/lib/utopia/content/markup.rb +7 -2
- data/lib/utopia/{tags/deferred.rb → content/namespace.rb} +25 -6
- data/lib/utopia/content/node.rb +74 -76
- data/lib/utopia/content/response.rb +22 -3
- data/lib/utopia/content/tags.rb +66 -0
- data/lib/utopia/controller.rb +10 -18
- data/lib/utopia/controller/actions.rb +10 -0
- data/lib/utopia/controller/base.rb +2 -1
- data/lib/utopia/controller/respond.rb +1 -1
- data/lib/utopia/controller/rewrite.rb +8 -4
- data/lib/utopia/exceptions.rb +1 -0
- data/lib/utopia/exceptions/handler.rb +7 -2
- data/lib/utopia/exceptions/mailer.rb +33 -12
- data/lib/utopia/{tags/node.rb → extensions/array_split.rb} +11 -9
- data/lib/utopia/{tags/environment.rb → extensions/date_comparisons.rb} +24 -14
- data/lib/utopia/http.rb +2 -0
- data/lib/utopia/locale.rb +1 -0
- data/lib/utopia/localization.rb +37 -28
- data/lib/utopia/logger.rb +1 -0
- data/lib/utopia/logger/compact_formatter.rb +1 -0
- data/lib/utopia/middleware.rb +11 -1
- data/lib/utopia/path.rb +1 -0
- data/lib/utopia/path/matcher.rb +14 -2
- data/lib/utopia/redirection.rb +13 -16
- data/lib/utopia/session.rb +14 -6
- data/lib/utopia/setup.rb +3 -1
- data/lib/utopia/static.rb +11 -12
- data/lib/utopia/version.rb +1 -1
- data/setup/server/git/hooks/post-receive +0 -4
- data/setup/site/.gitignore +9 -0
- data/setup/site/.rspec +1 -0
- data/setup/site/Gemfile +4 -0
- data/setup/site/Guardfile +17 -0
- data/setup/site/Rakefile +2 -2
- data/setup/site/config.ru +5 -12
- data/setup/site/pages/_heading.xnode +2 -2
- data/setup/site/pages/_page.xnode +1 -1
- data/setup/site/pages/errors/exception.xnode +3 -3
- data/setup/site/pages/errors/file-not-found.xnode +3 -3
- data/setup/site/pages/welcome/index.xnode +3 -3
- data/setup/site/public/_static/site.css +4 -0
- data/setup/site/spec/spec_helper.rb +29 -0
- data/setup/site/tasks/deploy.rake +13 -0
- data/setup/site/tasks/development.rake +34 -0
- data/setup/site/tasks/environment.rake +17 -0
- data/spec/mock_node.rb +15 -0
- data/spec/spec_helper.rb +29 -0
- data/{lib/utopia/extensions/date.rb → spec/utopia/content/document_spec.rb} +31 -21
- data/spec/utopia/content/markup_spec.rb +2 -2
- data/spec/utopia/content/{tag_spec.rb → namespace_spec.rb} +17 -10
- data/spec/utopia/content/tags_spec.rb +80 -0
- data/spec/utopia/content_spec.rb +1 -1
- data/spec/utopia/content_spec.ru +1 -6
- data/spec/utopia/content_spec/_heading.xnode +1 -1
- data/spec/utopia/content_spec/content/test-partial.xnode +1 -1
- data/spec/utopia/content_spec/index.xnode +1 -1
- data/spec/utopia/controller/middleware_spec.ru +1 -3
- data/spec/utopia/controller/respond_spec.rb +2 -22
- data/spec/utopia/controller/respond_spec.ru +1 -5
- data/spec/utopia/controller/respond_spec/errors/file-not-found.xnode +7 -6
- data/spec/utopia/exceptions/handler_spec.ru +1 -2
- data/spec/utopia/exceptions/mailer_spec.ru +1 -2
- data/spec/utopia/extensions_spec.rb +2 -2
- data/spec/utopia/localization_spec.ru +1 -2
- data/spec/utopia/performance_spec.rb +2 -6
- data/spec/utopia/performance_spec/config.ru +5 -12
- data/spec/utopia/performance_spec/pages/_heading.xnode +2 -2
- data/spec/utopia/performance_spec/pages/_page.xnode +1 -1
- data/spec/utopia/performance_spec/pages/errors/exception.xnode +3 -3
- data/spec/utopia/performance_spec/pages/errors/file-not-found.xnode +3 -3
- data/spec/utopia/performance_spec/pages/welcome/index.xnode +3 -3
- data/spec/utopia/setup_spec.rb +79 -15
- data/utopia.gemspec +3 -3
- metadata +41 -27
- data/.simplecov +0 -9
- data/documentation/pages/welcome/index.xnode +0 -41
- data/lib/utopia/content/tag.rb +0 -90
- data/lib/utopia/extensions/array.rb +0 -29
- data/lib/utopia/tags/override.rb +0 -33
- data/setup/site/.simplecov +0 -9
- data/setup/site/tasks/test.rake +0 -10
- data/setup/site/tasks/utopia.rake +0 -41
- data/spec/utopia/controller/respond_spec/rewrite/controller.rb +0 -12
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
# THE SOFTWARE.
|
|
20
20
|
|
|
21
21
|
require_relative 'links'
|
|
22
|
-
|
|
23
22
|
require_relative 'response'
|
|
23
|
+
require_relative 'markup'
|
|
24
24
|
|
|
25
25
|
module Utopia
|
|
26
26
|
class Content
|
|
@@ -35,26 +35,38 @@ module Utopia
|
|
|
35
35
|
attr :tag
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
DEFERRED_TAG_NAME = "deferred".freeze
|
|
39
|
-
CONTENT_TAG_NAME = "content".freeze
|
|
40
|
-
|
|
41
38
|
# A single request through content middleware. We use a struct to hide instance varibles since we instance_exec within this context.
|
|
42
|
-
class
|
|
39
|
+
class Document < Response
|
|
40
|
+
def self.render(node, request, attributes)
|
|
41
|
+
self.new(request, attributes).render!(node, attributes)
|
|
42
|
+
end
|
|
43
|
+
|
|
43
44
|
def initialize(request, attributes = {})
|
|
44
45
|
@request = request
|
|
45
46
|
|
|
46
47
|
@attributes = attributes
|
|
47
48
|
|
|
48
|
-
@
|
|
49
|
+
@first = nil
|
|
50
|
+
@current = nil
|
|
49
51
|
@end_tags = []
|
|
50
52
|
|
|
51
|
-
# TODO: Provide way to set encoding and content type.
|
|
52
|
-
#@encoding = Encoding::UTF_8
|
|
53
|
-
#self.content_type = "text/html; charset=#{@encoding.name}"
|
|
54
|
-
|
|
55
53
|
super()
|
|
56
54
|
end
|
|
57
55
|
|
|
56
|
+
def [] key
|
|
57
|
+
@attributes[key]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def []= key, value
|
|
61
|
+
@attributes[key] = value
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def render!(node, attributes)
|
|
65
|
+
@body << render_node(node, attributes)
|
|
66
|
+
|
|
67
|
+
return self
|
|
68
|
+
end
|
|
69
|
+
|
|
58
70
|
# A helper method for accessing controller variables from view:
|
|
59
71
|
def controller
|
|
60
72
|
@controller ||= Utopia::Controller[request]
|
|
@@ -68,134 +80,137 @@ module Utopia
|
|
|
68
80
|
MarkupParser.parse(markup, self)
|
|
69
81
|
end
|
|
70
82
|
|
|
71
|
-
# The Rack::Request for this
|
|
83
|
+
# The Rack::Request for this document.
|
|
72
84
|
attr :request
|
|
73
85
|
|
|
74
|
-
# Per-
|
|
86
|
+
# Per-document global attributes.
|
|
75
87
|
attr :attributes
|
|
76
88
|
|
|
77
|
-
#
|
|
78
|
-
# At any point in parsing markup,
|
|
79
|
-
# then the next outer tag, etc.
|
|
80
|
-
attr :
|
|
89
|
+
# The current state, represents a list from outer to inner most tag by traversing {State#parent}.
|
|
90
|
+
# At any point in parsing markup, this is a list of the inner most tag,
|
|
91
|
+
# then the next outer tag, etc.
|
|
92
|
+
attr :current
|
|
93
|
+
|
|
94
|
+
# The first {State} generated by rendering this document. It contains useful information
|
|
95
|
+
# regarding the node and uri used to access the resource.
|
|
96
|
+
attr :first
|
|
81
97
|
|
|
82
98
|
# End tags represents a list of execution order. This is the order that end tags
|
|
83
99
|
# have appeared when evaluating nodes.
|
|
84
100
|
attr :end_tags
|
|
85
101
|
|
|
86
|
-
def tag(name, attributes = {}
|
|
102
|
+
def tag(name, attributes = {})
|
|
87
103
|
# If we provide a block which can give inner data, we are not self-closing.
|
|
88
104
|
tag = Tag.new(name, !block_given?, attributes)
|
|
89
105
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
106
|
+
if block_given?
|
|
107
|
+
node = tag_begin(tag)
|
|
108
|
+
yield node
|
|
109
|
+
tag_end(tag)
|
|
110
|
+
else
|
|
111
|
+
tag_complete(tag, node)
|
|
112
|
+
end
|
|
95
113
|
end
|
|
96
114
|
|
|
97
115
|
def tag_complete(tag, node = nil)
|
|
98
|
-
|
|
99
|
-
current.write(content)
|
|
100
|
-
else
|
|
101
|
-
node ||= lookup(tag)
|
|
116
|
+
node ||= lookup_tag(tag)
|
|
102
117
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
end
|
|
118
|
+
if node
|
|
119
|
+
tag_begin(tag, node)
|
|
120
|
+
tag_end(tag)
|
|
121
|
+
else
|
|
122
|
+
@current.tag_complete(tag)
|
|
109
123
|
end
|
|
110
124
|
end
|
|
111
125
|
|
|
112
126
|
def tag_begin(tag, node = nil)
|
|
113
|
-
node ||=
|
|
127
|
+
node ||= lookup_tag(tag)
|
|
114
128
|
|
|
115
129
|
if node
|
|
116
|
-
|
|
117
|
-
self.begin_tags << state
|
|
130
|
+
@current = State.new(@current, tag, node)
|
|
118
131
|
|
|
119
|
-
if node.respond_to?
|
|
120
|
-
node.tag_begin(self, state)
|
|
121
|
-
end
|
|
132
|
+
node.tag_begin(self, state) if node.respond_to?(:tag_begin)
|
|
122
133
|
|
|
123
134
|
return node
|
|
124
135
|
end
|
|
125
136
|
|
|
126
|
-
|
|
137
|
+
# raise ArgumentError.new("tag_begin: #{tag} is tag.self_closed?") if tag.self_closed?
|
|
138
|
+
|
|
139
|
+
@current.tag_begin(tag)
|
|
127
140
|
|
|
128
141
|
return nil
|
|
129
142
|
end
|
|
130
143
|
|
|
131
144
|
def write(string)
|
|
132
|
-
current.write(string)
|
|
145
|
+
@current.write(string)
|
|
133
146
|
end
|
|
134
147
|
|
|
135
148
|
alias cdata write
|
|
136
149
|
|
|
137
150
|
def text(string)
|
|
138
|
-
current.text(string)
|
|
151
|
+
@current.text(string)
|
|
139
152
|
end
|
|
140
153
|
|
|
141
154
|
def tag_end(tag = nil)
|
|
142
|
-
#
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if top.node.respond_to? :tag_end
|
|
147
|
-
top.node.tag_end(self, top)
|
|
155
|
+
# Determine if the current state contains tags that need to be completed, or if the state itself is finished.
|
|
156
|
+
if @current.empty?
|
|
157
|
+
if node = @current.node
|
|
158
|
+
node.tag_end(self, @current) if node.respond_to?(:tag_end)
|
|
148
159
|
end
|
|
149
160
|
|
|
150
|
-
|
|
151
|
-
buffer =
|
|
161
|
+
@end_tags << @current
|
|
162
|
+
buffer = @current.call(self)
|
|
152
163
|
|
|
153
|
-
|
|
154
|
-
|
|
164
|
+
@current = @current.parent
|
|
165
|
+
@end_tags.pop
|
|
155
166
|
|
|
156
|
-
if current
|
|
157
|
-
current.write(buffer)
|
|
158
|
-
end
|
|
167
|
+
@current.write(buffer) if @current
|
|
159
168
|
|
|
160
169
|
return buffer
|
|
161
170
|
else
|
|
162
|
-
|
|
171
|
+
# raise ArgumentError.new("tag_begin: #{tag} is tag.self_closed?") if tag.self_closed?
|
|
172
|
+
@current.tag_end(tag)
|
|
163
173
|
end
|
|
164
174
|
|
|
165
175
|
return nil
|
|
166
176
|
end
|
|
167
|
-
|
|
168
|
-
def render_node(node, attributes = {})
|
|
169
|
-
self.begin_tags << State.new(attributes, node)
|
|
170
177
|
|
|
178
|
+
def render_node(node, attributes = {})
|
|
179
|
+
@current = State.new(@current, nil, node, attributes)
|
|
180
|
+
|
|
181
|
+
# We keep track of the first thing rendered by this document.
|
|
182
|
+
@first ||= @current
|
|
183
|
+
|
|
184
|
+
# This returns the content of rendering the tag:
|
|
171
185
|
return tag_end
|
|
172
186
|
end
|
|
173
187
|
|
|
174
|
-
#
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return state.node.
|
|
188
|
+
# 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.
|
|
189
|
+
# @return [Node] The node for the given tag.
|
|
190
|
+
def lookup_tag(tag)
|
|
191
|
+
# result = tag
|
|
192
|
+
#
|
|
193
|
+
# # This loop works from inner to outer tags, and updates the tag we are currently searching for based on any overrides:
|
|
194
|
+
# @begin_tags.reverse_each do |state|
|
|
195
|
+
# result = state.lookup(result)
|
|
196
|
+
#
|
|
197
|
+
# return result if result.is_a?(Node)
|
|
198
|
+
# end
|
|
199
|
+
|
|
200
|
+
# 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:
|
|
201
|
+
@end_tags.reverse_each do |state|
|
|
202
|
+
return state.node.lookup_tag(tag) if state.node.respond_to?(:lookup_tag)
|
|
189
203
|
end
|
|
190
|
-
|
|
204
|
+
|
|
191
205
|
return nil
|
|
192
206
|
end
|
|
193
207
|
|
|
194
|
-
#
|
|
195
|
-
|
|
196
|
-
|
|
208
|
+
# Lookup a node with the given path relative to the current node.
|
|
209
|
+
# @return [Node] The node if could be found.
|
|
210
|
+
def lookup_node(path)
|
|
211
|
+
@current.node.lookup_node(path)
|
|
197
212
|
end
|
|
198
|
-
|
|
213
|
+
|
|
199
214
|
# The content of the node
|
|
200
215
|
def content
|
|
201
216
|
@end_tags.last.content
|
|
@@ -204,32 +219,30 @@ module Utopia
|
|
|
204
219
|
def parent
|
|
205
220
|
@end_tags[-2]
|
|
206
221
|
end
|
|
207
|
-
|
|
208
|
-
def first
|
|
209
|
-
@begin_tags.first
|
|
210
|
-
end
|
|
211
222
|
end
|
|
212
223
|
|
|
213
|
-
# The state of a single tag being rendered within a
|
|
214
|
-
class
|
|
215
|
-
def initialize(tag, node, attributes = tag.to_hash)
|
|
224
|
+
# The state of a single tag being rendered within a document instance.
|
|
225
|
+
class Document::State
|
|
226
|
+
def initialize(parent, tag, node, attributes = tag.to_hash)
|
|
227
|
+
@parent = parent
|
|
228
|
+
@tag = tag
|
|
216
229
|
@node = node
|
|
230
|
+
@attributes = attributes
|
|
217
231
|
|
|
218
232
|
@buffer = Trenni::MarkupString.new.force_encoding(Encoding::UTF_8)
|
|
233
|
+
@content = nil
|
|
219
234
|
|
|
220
|
-
@
|
|
235
|
+
@deferred = []
|
|
221
236
|
|
|
222
237
|
@tags = []
|
|
223
|
-
@attributes = attributes
|
|
224
|
-
|
|
225
|
-
@content = nil
|
|
226
|
-
@deferred = []
|
|
227
238
|
end
|
|
228
239
|
|
|
240
|
+
attr :parent
|
|
229
241
|
attr :attributes
|
|
230
|
-
attr :overrides
|
|
231
242
|
attr :content
|
|
232
243
|
attr :node
|
|
244
|
+
|
|
245
|
+
# A list of all tags in order of rendering them, which have not been finished yet.
|
|
233
246
|
attr :tags
|
|
234
247
|
|
|
235
248
|
attr :deferred
|
|
@@ -244,28 +257,14 @@ module Utopia
|
|
|
244
257
|
@attributes[key]
|
|
245
258
|
end
|
|
246
259
|
|
|
247
|
-
def
|
|
248
|
-
if override = @overrides[tag.name]
|
|
249
|
-
if override.respond_to? :call
|
|
250
|
-
return override.call(tag)
|
|
251
|
-
elsif String === override
|
|
252
|
-
return Tag.new(override, tag.attributes)
|
|
253
|
-
else
|
|
254
|
-
return override
|
|
255
|
-
end
|
|
256
|
-
else
|
|
257
|
-
return tag
|
|
258
|
-
end
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
def call(transaction)
|
|
260
|
+
def call(document)
|
|
262
261
|
@content = @buffer
|
|
263
262
|
@buffer = Trenni::MarkupString.new.force_encoding(Encoding::UTF_8)
|
|
264
263
|
|
|
265
264
|
if node.respond_to? :call
|
|
266
|
-
node.call(
|
|
265
|
+
node.call(document, self)
|
|
267
266
|
else
|
|
268
|
-
|
|
267
|
+
document.parse_markup(@content)
|
|
269
268
|
end
|
|
270
269
|
|
|
271
270
|
return @buffer
|
|
@@ -276,20 +275,27 @@ module Utopia
|
|
|
276
275
|
end
|
|
277
276
|
|
|
278
277
|
def text(string)
|
|
279
|
-
@buffer
|
|
278
|
+
Trenni::Markup.append(@buffer, string)
|
|
280
279
|
end
|
|
281
280
|
|
|
282
281
|
def tag_complete(tag)
|
|
283
282
|
tag.write(@buffer)
|
|
284
283
|
end
|
|
284
|
+
|
|
285
|
+
# Whether this state has any nested tags.
|
|
286
|
+
def empty?
|
|
287
|
+
@tags.empty?
|
|
288
|
+
end
|
|
285
289
|
|
|
286
290
|
def tag_begin(tag)
|
|
291
|
+
# raise ArgumentError.new("tag_begin: #{tag} is tag.self_closed?") if tag.self_closed?
|
|
292
|
+
|
|
287
293
|
@tags << tag
|
|
288
294
|
tag.write_opening_tag(@buffer)
|
|
289
295
|
end
|
|
290
296
|
|
|
291
297
|
def tag_end(tag)
|
|
292
|
-
raise UnbalancedTagError(tag) unless @tags.pop.name == tag.name
|
|
298
|
+
raise UnbalancedTagError.new(tag) unless @tags.pop.name == tag.name
|
|
293
299
|
tag.write_closing_tag(@buffer)
|
|
294
300
|
end
|
|
295
301
|
end
|
data/lib/utopia/content/link.rb
CHANGED
|
@@ -28,6 +28,7 @@ require_relative '../locale'
|
|
|
28
28
|
|
|
29
29
|
module Utopia
|
|
30
30
|
class Content
|
|
31
|
+
# Represents a link to some content with associated metadata.
|
|
31
32
|
class Link
|
|
32
33
|
def initialize(kind, path, info = nil)
|
|
33
34
|
path = Path.create(path)
|
|
@@ -91,9 +92,13 @@ module Utopia
|
|
|
91
92
|
def to_anchor(**options)
|
|
92
93
|
Trenni::Builder.fragment(options[:builder]) do |builder|
|
|
93
94
|
if href?
|
|
94
|
-
|
|
95
|
+
a_attributes = {
|
|
96
|
+
:class => options.fetch(:class, 'link'),
|
|
97
|
+
:href => relative_href(options[:base]),
|
|
98
|
+
:target => options.fetch(:target, @info[:target])
|
|
99
|
+
}
|
|
95
100
|
|
|
96
|
-
builder.inline('a',
|
|
101
|
+
builder.inline('a', a_attributes) do
|
|
97
102
|
builder.text(options[:content] || title)
|
|
98
103
|
end
|
|
99
104
|
else
|
data/lib/utopia/content/links.rb
CHANGED
|
@@ -22,9 +22,10 @@ require_relative 'link'
|
|
|
22
22
|
|
|
23
23
|
module Utopia
|
|
24
24
|
class Content
|
|
25
|
+
# The file extension for markup nodes on disk.
|
|
25
26
|
XNODE_EXTENSION = '.xnode'.freeze
|
|
26
27
|
|
|
27
|
-
#
|
|
28
|
+
# Represents a list of {Link} instances relating to the structure of the content. They are formed from the `links.yaml` file and the actual directory structure on disk.
|
|
28
29
|
class Links
|
|
29
30
|
def self.for(root, path, locale = nil)
|
|
30
31
|
links = self.new(root, path.dirname)
|
|
@@ -21,11 +21,13 @@
|
|
|
21
21
|
require 'trenni/parsers'
|
|
22
22
|
require 'trenni/entities'
|
|
23
23
|
require 'trenni/strings'
|
|
24
|
-
|
|
25
|
-
require_relative 'tag'
|
|
24
|
+
require 'trenni/tag'
|
|
26
25
|
|
|
27
26
|
module Utopia
|
|
28
27
|
class Content
|
|
28
|
+
Tag = Trenni::Tag
|
|
29
|
+
|
|
30
|
+
# A hash which forces all keys to be symbols and fails with KeyError when strings are used.
|
|
29
31
|
class SymbolicHash < Hash
|
|
30
32
|
def [] key
|
|
31
33
|
raise KeyError.new("attribute #{key} is a string, prefer a symbol") if key.is_a? String
|
|
@@ -49,7 +51,9 @@ module Utopia
|
|
|
49
51
|
end
|
|
50
52
|
end
|
|
51
53
|
|
|
54
|
+
# Provides a high level interface for parsing markup.
|
|
52
55
|
class MarkupParser
|
|
56
|
+
# A tag generated by parsing markup.
|
|
53
57
|
class ParsedTag
|
|
54
58
|
def initialize(name, offset)
|
|
55
59
|
@offset = offset
|
|
@@ -64,6 +68,7 @@ module Utopia
|
|
|
64
68
|
end
|
|
65
69
|
end
|
|
66
70
|
|
|
71
|
+
# The name of a closing tag fails to match up with the corresponding opening tag.
|
|
67
72
|
class UnbalancedTagError < StandardError
|
|
68
73
|
def initialize(buffer, opening_tag, closing_tag = nil)
|
|
69
74
|
@buffer = buffer
|
|
@@ -19,14 +19,33 @@
|
|
|
19
19
|
# THE SOFTWARE.
|
|
20
20
|
|
|
21
21
|
module Utopia
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
class Content
|
|
23
|
+
# A namespace which contains tags which can be rendered within a {Document}.
|
|
24
|
+
module Namespace
|
|
25
|
+
def self.extended(other)
|
|
26
|
+
other.class_exec do
|
|
27
|
+
@named = {}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
attr :named
|
|
32
|
+
|
|
33
|
+
def freeze
|
|
34
|
+
return self if frozen?
|
|
26
35
|
|
|
27
|
-
|
|
36
|
+
@named.freeze
|
|
37
|
+
@named.values.each(&:freeze)
|
|
28
38
|
|
|
29
|
-
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def tag(name, klass = nil, &block)
|
|
43
|
+
@named[name] = klass || block
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @return [Node] The node which should be used to render the named tag.
|
|
47
|
+
def call(name, node)
|
|
48
|
+
@named[name]
|
|
30
49
|
end
|
|
31
50
|
end
|
|
32
51
|
end
|