syntropy 0.38.0 → 0.39.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/CHANGELOG.md +16 -0
- data/README.md +2 -0
- data/TODO.md +12 -3
- data/cmd/console.rb +2 -2
- data/cmd/serve.rb +3 -4
- data/cmd/test.rb +12 -4
- data/examples/blog/app/posts/[id]/edit.rb +3 -3
- data/examples/blog/app/posts/[id]/index.rb +8 -10
- data/examples/blog/app/posts/index.rb +6 -8
- data/examples/blog/app/posts/new.rb +3 -3
- data/lib/syntropy/app.rb +25 -59
- data/lib/syntropy/controller_extensions.rb +3 -22
- data/lib/syntropy/markdown.rb +115 -11
- data/lib/syntropy/module_loader.rb +85 -64
- data/lib/syntropy/request/request_info.rb +8 -0
- data/lib/syntropy/request/response.rb +1 -0
- data/lib/syntropy/test.rb +11 -2
- data/lib/syntropy/version.rb +1 -1
- data/lib/syntropy.rb +2 -1
- data/test/fixtures/app/_layout/default.rb +1 -1
- data/test/fixtures/app/_layout/kuku.rb +8 -0
- data/test/fixtures/app/_lib/circular/a.rb +2 -0
- data/test/fixtures/app/_lib/circular/b.rb +2 -0
- data/test/fixtures/app/_lib/circular/c.rb +2 -0
- data/test/fixtures/app/mod/concurrent.rb +9 -0
- data/test/test_app.rb +1 -1
- data/test/test_caching.rb +3 -3
- data/test/test_markdown.rb +268 -0
- data/test/test_module_loader.rb +28 -3
- data/test/test_request.rb +7 -0
- data/test/test_response.rb +30 -0
- data/test/test_schema.rb +1 -0
- metadata +7 -1
|
@@ -21,7 +21,7 @@ module Syntropy
|
|
|
21
21
|
# reference `_lib/foo` will point to a module residing in
|
|
22
22
|
# `/my_site/_lib/foo.rb`.
|
|
23
23
|
class ModuleLoader
|
|
24
|
-
attr_reader :modules
|
|
24
|
+
attr_reader :modules, :app_root
|
|
25
25
|
|
|
26
26
|
# Instantiates a module loader
|
|
27
27
|
#
|
|
@@ -30,10 +30,14 @@ module Syntropy
|
|
|
30
30
|
# @return [void]
|
|
31
31
|
def initialize(env, extensions: nil)
|
|
32
32
|
@env = env
|
|
33
|
+
@machine = env[:machine]
|
|
33
34
|
@app_root = env[:app_root]
|
|
34
35
|
@modules = {} # maps ref to module entry
|
|
35
36
|
@fn_map = {} # maps filename to ref
|
|
36
37
|
@extensions = extensions
|
|
38
|
+
@loading = Set.new
|
|
39
|
+
@lock = UM::Mutex.new
|
|
40
|
+
@lock_holder = nil
|
|
37
41
|
end
|
|
38
42
|
|
|
39
43
|
# Loads a module (if not already loaded) and returns its export value.
|
|
@@ -41,14 +45,16 @@ module Syntropy
|
|
|
41
45
|
# @param ref [String] module reference
|
|
42
46
|
# @return [any] export value
|
|
43
47
|
def load(ref, raise_on_missing: true)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
entry =
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
lock do
|
|
49
|
+
ref = "/#{ref}" if ref !~ /^\//
|
|
50
|
+
if !(entry = @modules[ref])
|
|
51
|
+
entry = load_module(ref, raise_on_missing:)
|
|
52
|
+
return if !entry
|
|
53
|
+
|
|
54
|
+
@modules[ref] = entry
|
|
55
|
+
end
|
|
56
|
+
entry[:export_value]
|
|
50
57
|
end
|
|
51
|
-
entry[:export_value]
|
|
52
58
|
end
|
|
53
59
|
|
|
54
60
|
# Returns a list of modules found in the given relative path. The module
|
|
@@ -70,13 +76,27 @@ module Syntropy
|
|
|
70
76
|
# @param fn [String] module filename
|
|
71
77
|
# @return [void]
|
|
72
78
|
def invalidate_fn(fn)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
lock do
|
|
80
|
+
ref = @fn_map[fn]
|
|
81
|
+
invalidate_ref(ref) if ref
|
|
82
|
+
invalidate_collection_modules
|
|
83
|
+
end
|
|
76
84
|
end
|
|
77
85
|
|
|
78
86
|
private
|
|
79
87
|
|
|
88
|
+
# Synchronizes access to the module loader state
|
|
89
|
+
def lock
|
|
90
|
+
return yield if @lock_holder == Fiber.current
|
|
91
|
+
|
|
92
|
+
@machine.synchronize(@lock) do
|
|
93
|
+
@lock_holder = Fiber.current
|
|
94
|
+
yield
|
|
95
|
+
ensure
|
|
96
|
+
@lock_holder = nil
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
80
100
|
# Invalidates a module by its reference, normally following a change to the
|
|
81
101
|
# underlying file (in order to cause reloading of the module). The module
|
|
82
102
|
# will be removed from the modules map, as well as modules dependending on
|
|
@@ -131,14 +151,21 @@ module Syntropy
|
|
|
131
151
|
return
|
|
132
152
|
end
|
|
133
153
|
|
|
154
|
+
raise Syntropy::Error, "Circular dependency detected" if @loading.include?(ref)
|
|
155
|
+
do_load_module(ref, fn, raise_on_missing:)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def do_load_module(ref, fn, raise_on_missing:)
|
|
159
|
+
@loading << ref
|
|
134
160
|
@fn_map[fn] = ref
|
|
135
|
-
code =
|
|
161
|
+
code = read_file(fn)
|
|
136
162
|
env = @env.merge(module_loader: self, ref: clean_ref(ref))
|
|
137
|
-
mod = Syntropy::ModuleContext.
|
|
163
|
+
mod = Syntropy::ModuleContext.new(env, code, fn, @extensions)
|
|
138
164
|
add_dependencies(ref, mod.__dependencies__)
|
|
139
165
|
export_value = transform_module_export_value(
|
|
140
166
|
mod.__export_value__, fn, raise_on_missing:
|
|
141
167
|
)
|
|
168
|
+
@env[:logger]&.info(message: "Loaded module at #{fn}")
|
|
142
169
|
|
|
143
170
|
{
|
|
144
171
|
fn: fn,
|
|
@@ -146,6 +173,19 @@ module Syntropy
|
|
|
146
173
|
export_value: export_value,
|
|
147
174
|
reverse_deps: []
|
|
148
175
|
}
|
|
176
|
+
rescue StandardError, SyntaxError => e
|
|
177
|
+
env[:logger]&.error(message: "Error while loading module at #{fn}", error: e)
|
|
178
|
+
e.is_a?(SyntaxError) ? handle_syntax_error(fn, e) : (raise e)
|
|
179
|
+
ensure
|
|
180
|
+
@loading.delete(ref)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def read_file(fn)
|
|
184
|
+
@machine.open(fn, UM::O_RDONLY) { |fd|
|
|
185
|
+
buf = +''
|
|
186
|
+
res = @machine.read(fd, buf, 1 << 20)
|
|
187
|
+
buf
|
|
188
|
+
}
|
|
149
189
|
end
|
|
150
190
|
|
|
151
191
|
# Cleans up a module reference specifier, turning /index into /
|
|
@@ -174,6 +214,17 @@ module Syntropy
|
|
|
174
214
|
export_value
|
|
175
215
|
end
|
|
176
216
|
end
|
|
217
|
+
|
|
218
|
+
def handle_syntax_error(fn, e)
|
|
219
|
+
$stderr.puts("\n#{e.message}") if !Syntropy.test_mode
|
|
220
|
+
m = e.message.match(/^(.+): syntax/)
|
|
221
|
+
raise e if !m
|
|
222
|
+
|
|
223
|
+
location = m[1]
|
|
224
|
+
e2 = SyntaxError.new("Syntax errors found in module at #{fn}")
|
|
225
|
+
e2.set_backtrace([location] + e.backtrace)
|
|
226
|
+
raise e2
|
|
227
|
+
end
|
|
177
228
|
end
|
|
178
229
|
|
|
179
230
|
# The Syntropy::ModuleContext class provides a context for loading a module. A
|
|
@@ -193,44 +244,14 @@ module Syntropy
|
|
|
193
244
|
# is set to `self`, and may be used to refer to various methods defined in the
|
|
194
245
|
# module.
|
|
195
246
|
class ModuleContext
|
|
196
|
-
# Loads a module, returning the module instance
|
|
197
|
-
# @param env [Hash] app environment
|
|
198
|
-
# @param code [String] module source code
|
|
199
|
-
# @param fn [String] module file name
|
|
200
|
-
# @param extensions [Module, Array<Module>] extension module(s)
|
|
201
|
-
# @return [Syntropy::ModuleContext] created module context
|
|
202
|
-
def self.load(env, code, fn, extensions)
|
|
203
|
-
mod = new(env)
|
|
204
|
-
apply_extensions(mod, extensions)
|
|
205
|
-
mod.instance_eval(code, fn)
|
|
206
|
-
env[:logger]&.info(message: "Loaded module at #{fn}")
|
|
207
|
-
mod
|
|
208
|
-
rescue StandardError, SyntaxError => e
|
|
209
|
-
env[:logger]&.error(message: "Error while loading module at #{fn}", error: e)
|
|
210
|
-
e.is_a?(SyntaxError) ? handle_syntax_error(env, e) : (raise e)
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
# Applies the given extension(s) to the given module context.
|
|
214
|
-
#
|
|
215
|
-
# @param mod [Syntropy::ModuleContext] module context
|
|
216
|
-
# @param extensions [Module, Array<Module>] extension module(s)
|
|
217
|
-
def self.apply_extensions(mod, extensions)
|
|
218
|
-
case extensions
|
|
219
|
-
when Array
|
|
220
|
-
extensions.each { mod.extend(it) }
|
|
221
|
-
when Module
|
|
222
|
-
mod.extend(extensions)
|
|
223
|
-
when nil # return
|
|
224
|
-
else
|
|
225
|
-
raise Syntropy::Error, "Invalid module extensions: #{extensions.inspect}"
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
|
|
229
247
|
# Initializes a module with the given environment hash.
|
|
230
248
|
#
|
|
231
249
|
# @param env [Hash] environment hash
|
|
250
|
+
# @param code [String] module source code
|
|
251
|
+
# @param fn [String] module filename
|
|
252
|
+
# @param extensions [Array<Module>, Module]
|
|
232
253
|
# @return [void]
|
|
233
|
-
def initialize(env)
|
|
254
|
+
def initialize(env, code, fn, extensions)
|
|
234
255
|
@env = env
|
|
235
256
|
@machine = env[:machine]
|
|
236
257
|
@module_loader = env[:module_loader]
|
|
@@ -239,18 +260,13 @@ module Syntropy
|
|
|
239
260
|
@logger = env[:logger]
|
|
240
261
|
@__dependencies__ = []
|
|
241
262
|
singleton_class.const_set(:MODULE, self)
|
|
263
|
+
|
|
264
|
+
apply_extensions(extensions)
|
|
265
|
+
instance_eval(code, fn)
|
|
242
266
|
end
|
|
243
267
|
|
|
244
268
|
attr_reader :__export_value__, :__dependencies__
|
|
245
269
|
|
|
246
|
-
# Returns a list of pages found at the given ref.
|
|
247
|
-
#
|
|
248
|
-
# @param ref [String] directory reference
|
|
249
|
-
# @return [Array] array of pages found in directory
|
|
250
|
-
def page_list(ref)
|
|
251
|
-
Syntropy.page_list(@env, ref)
|
|
252
|
-
end
|
|
253
|
-
|
|
254
270
|
# Returns true if the module is a collection module. See also
|
|
255
271
|
# #collection_module!
|
|
256
272
|
#
|
|
@@ -342,15 +358,20 @@ module Syntropy
|
|
|
342
358
|
Syntropy::App.new(**env)
|
|
343
359
|
end
|
|
344
360
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
361
|
+
# Applies the given extension(s) to the given module context.
|
|
362
|
+
#
|
|
363
|
+
# @param extensions [Module, Array<Module>] extension module(s)
|
|
364
|
+
# @return [void]
|
|
365
|
+
def apply_extensions(extensions)
|
|
366
|
+
case extensions
|
|
367
|
+
when Array
|
|
368
|
+
extensions.each { extend(it) }
|
|
369
|
+
when Module
|
|
370
|
+
extend(extensions)
|
|
371
|
+
when nil # return
|
|
372
|
+
else
|
|
373
|
+
raise Syntropy::Error, "Invalid module extensions: #{extensions.inspect}"
|
|
374
|
+
end
|
|
354
375
|
end
|
|
355
376
|
end
|
|
356
377
|
end
|
|
@@ -228,6 +228,14 @@ module Syntropy
|
|
|
228
228
|
nil
|
|
229
229
|
end
|
|
230
230
|
|
|
231
|
+
# Expands a relative path based on the request's path.
|
|
232
|
+
#
|
|
233
|
+
# param rel_path [String]
|
|
234
|
+
# @return [String]
|
|
235
|
+
def rel(rel_path)
|
|
236
|
+
File.expand_path(File.join(path, rel_path))
|
|
237
|
+
end
|
|
238
|
+
|
|
231
239
|
private
|
|
232
240
|
|
|
233
241
|
# Parses an accept string into an array of accepted MIME types.
|
data/lib/syntropy/test.rb
CHANGED
|
@@ -12,6 +12,14 @@ module Syntropy
|
|
|
12
12
|
HTTP = Syntropy::HTTP
|
|
13
13
|
|
|
14
14
|
class << self
|
|
15
|
+
def global_env=(env)
|
|
16
|
+
@@global_env = env
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def global_env
|
|
20
|
+
@@global_env
|
|
21
|
+
end
|
|
22
|
+
|
|
15
23
|
# Gets/sets app environment for tests
|
|
16
24
|
attr_accessor :env
|
|
17
25
|
end
|
|
@@ -22,7 +30,8 @@ module Syntropy
|
|
|
22
30
|
#
|
|
23
31
|
# @return [Hash] test app environment
|
|
24
32
|
def env
|
|
25
|
-
self.class
|
|
33
|
+
klass = self.class
|
|
34
|
+
klass.env || klass.global_env
|
|
26
35
|
end
|
|
27
36
|
|
|
28
37
|
# Loads and returns a module with the given reference.
|
|
@@ -122,7 +131,7 @@ module Syntropy
|
|
|
122
131
|
#
|
|
123
132
|
# @return [void]
|
|
124
133
|
def setup
|
|
125
|
-
env = self.
|
|
134
|
+
env = self.env
|
|
126
135
|
raise 'Environment not set' if !env
|
|
127
136
|
|
|
128
137
|
Syntropy.load_config(env)
|
data/lib/syntropy/version.rb
CHANGED
data/lib/syntropy.rb
CHANGED
data/test/test_app.rb
CHANGED
|
@@ -100,7 +100,7 @@ class AppTest < Minitest::Test
|
|
|
100
100
|
assert_equal 'About', req.response_body.chomp
|
|
101
101
|
|
|
102
102
|
req = @test_harness.request(':method' => 'GET', ':path' => '/test/about/foo')
|
|
103
|
-
assert_equal '<!DOCTYPE html><html><head
|
|
103
|
+
assert_equal '<!DOCTYPE html><html><head></head><body><p>Hello from Markdown</p></body></html>', req.response_body.gsub(/\n/, '')
|
|
104
104
|
|
|
105
105
|
req = @test_harness.request(':method' => 'HEAD', ':path' => '/test/about/foo')
|
|
106
106
|
assert_nil req.response_body
|
data/test/test_caching.rb
CHANGED
|
@@ -75,7 +75,7 @@ class CachingTest < Minitest::Test
|
|
|
75
75
|
@adapter.serve_request
|
|
76
76
|
response = read_client_side
|
|
77
77
|
|
|
78
|
-
expected = "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\nCache-Control: max-age=
|
|
78
|
+
expected = "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\nCache-Control: max-age=604800\r\nEtag: #{etag}\r\nLast-Modified: #{last_modified}\r\nContent-Type: text/css\r\n\r\n#{size.to_s(16)}\r\n#{content}\r\n0\r\n\r\n"
|
|
79
79
|
assert_equal expected, response
|
|
80
80
|
end
|
|
81
81
|
|
|
@@ -92,7 +92,7 @@ class CachingTest < Minitest::Test
|
|
|
92
92
|
@adapter.serve_request
|
|
93
93
|
response = read_client_side
|
|
94
94
|
|
|
95
|
-
expected = "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\nCache-Control: max-age=
|
|
95
|
+
expected = "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\nCache-Control: max-age=604800\r\nEtag: #{etag}\r\nLast-Modified: #{last_modified}\r\nContent-Type: text/css\r\n\r\n#{size.to_s(16)}\r\n#{content}\r\n0\r\n\r\n"
|
|
96
96
|
assert_equal expected, response
|
|
97
97
|
|
|
98
98
|
# good etag
|
|
@@ -118,7 +118,7 @@ class CachingTest < Minitest::Test
|
|
|
118
118
|
@adapter.serve_request
|
|
119
119
|
response = read_client_side
|
|
120
120
|
|
|
121
|
-
expected = "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\nCache-Control: max-age=
|
|
121
|
+
expected = "HTTP/1.1 200\r\nTransfer-Encoding: chunked\r\nCache-Control: max-age=604800\r\nEtag: #{etag}\r\nLast-Modified: #{last_modified}\r\nContent-Type: text/css\r\n\r\n#{size.to_s(16)}\r\n#{content}\r\n0\r\n\r\n"
|
|
122
122
|
assert_equal expected, response
|
|
123
123
|
|
|
124
124
|
# good etag
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'helper'
|
|
4
|
+
require 'securerandom'
|
|
5
|
+
|
|
6
|
+
class MarkdownParseTest < Minitest::Test
|
|
7
|
+
def test_markdown_parse_md
|
|
8
|
+
md = <<~MD
|
|
9
|
+
---
|
|
10
|
+
foo: bar
|
|
11
|
+
---
|
|
12
|
+
foo *bar*
|
|
13
|
+
MD
|
|
14
|
+
|
|
15
|
+
h = {}
|
|
16
|
+
atts, md2 = Syntropy::Markdown.parse_md(h, md)
|
|
17
|
+
|
|
18
|
+
assert_equal h, atts
|
|
19
|
+
assert_equal({ foo: 'bar' }, atts)
|
|
20
|
+
assert_equal "foo *bar*\n", md2
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_markdown_parse_file
|
|
24
|
+
FileUtils.mkdir_p('/tmp/test')
|
|
25
|
+
name = "2008-06-14-test-#{SecureRandom.hex(8)}"
|
|
26
|
+
path = "/tmp/test/#{name}.md"
|
|
27
|
+
IO.write(
|
|
28
|
+
path,
|
|
29
|
+
<<~MD
|
|
30
|
+
---
|
|
31
|
+
bar: baz
|
|
32
|
+
---
|
|
33
|
+
bar *baz*
|
|
34
|
+
MD
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
env = { app_root: '/' }
|
|
38
|
+
atts, md = Syntropy::Markdown.parse_file(path, env)
|
|
39
|
+
assert_equal "/tmp/test/#{name}", atts[:url]
|
|
40
|
+
assert_equal 'baz', atts[:bar]
|
|
41
|
+
assert_equal "bar *baz*\n", md
|
|
42
|
+
|
|
43
|
+
env = { app_root: '/tmp' }
|
|
44
|
+
atts, md = Syntropy::Markdown.parse_file(path, env)
|
|
45
|
+
assert_equal "/test/#{name}", atts[:url]
|
|
46
|
+
assert_equal 'baz', atts[:bar]
|
|
47
|
+
assert_equal "bar *baz*\n", md
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class MarkdownControllerTest < Minitest::Test
|
|
52
|
+
HTTP = Syntropy::HTTP
|
|
53
|
+
|
|
54
|
+
def setup
|
|
55
|
+
@machine = UM.new
|
|
56
|
+
@root = File.join(__dir__, 'fixtures/app')
|
|
57
|
+
@env = { app_root: @root, machine: @machine }
|
|
58
|
+
@env[:module_loader] = Syntropy::ModuleLoader.new(@env)
|
|
59
|
+
|
|
60
|
+
@controller = nil
|
|
61
|
+
@test_harness = Syntropy::TestHarness.new(->(req) { @controller.(req) })
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def test_markdown_renderer_no_layout
|
|
65
|
+
md = <<~MD
|
|
66
|
+
foo *bar*
|
|
67
|
+
MD
|
|
68
|
+
|
|
69
|
+
atts = { foo: 'bar', title: 'Title' }
|
|
70
|
+
|
|
71
|
+
@controller = Syntropy::Markdown.make_controller(@env, atts, md)
|
|
72
|
+
req = @test_harness.request(':method' => 'GET', ':path' => '/')
|
|
73
|
+
|
|
74
|
+
assert_equal HTTP::OK, req.response_status
|
|
75
|
+
assert_equal(
|
|
76
|
+
"<!DOCTYPE html><html><head><title>Title</title></head><body><p>foo <em>bar</em></p>\n</body></html>",
|
|
77
|
+
req.response_body
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def test_markdown_renderer_with_layout
|
|
82
|
+
src = <<~MD
|
|
83
|
+
---
|
|
84
|
+
layout: kuku
|
|
85
|
+
---
|
|
86
|
+
## Bar
|
|
87
|
+
MD
|
|
88
|
+
|
|
89
|
+
atts, md = Syntropy::Markdown.parse_md({}, src)
|
|
90
|
+
assert_equal 'kuku', atts[:layout]
|
|
91
|
+
|
|
92
|
+
@controller = Syntropy::Markdown.make_controller(@env, atts, md)
|
|
93
|
+
req = @test_harness.request(':method' => 'GET', ':path' => '/')
|
|
94
|
+
|
|
95
|
+
assert_equal HTTP::OK, req.response_status
|
|
96
|
+
assert_equal(
|
|
97
|
+
"<header><h1>Kuku</h1></header><content><h2 id=\"bar\">Bar</h2>\n</content>",
|
|
98
|
+
req.response_body
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_markdown_renderer_with_missing_module_loader
|
|
103
|
+
src = <<~MD
|
|
104
|
+
---
|
|
105
|
+
layout: kuku
|
|
106
|
+
---
|
|
107
|
+
## Bar
|
|
108
|
+
MD
|
|
109
|
+
|
|
110
|
+
@env[:module_loader] = nil
|
|
111
|
+
atts, md = Syntropy::Markdown.parse_md({}, src)
|
|
112
|
+
assert_equal 'kuku', atts[:layout]
|
|
113
|
+
|
|
114
|
+
@controller = Syntropy::Markdown.make_controller(@env, atts, md)
|
|
115
|
+
assert_raises(Syntropy::Error) {
|
|
116
|
+
req = @test_harness.request(':method' => 'GET', ':path' => '/')
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def test_markdown_renderer_with_invalid_layout
|
|
121
|
+
src = <<~MD
|
|
122
|
+
---
|
|
123
|
+
layout: nunu
|
|
124
|
+
---
|
|
125
|
+
## Bar
|
|
126
|
+
MD
|
|
127
|
+
|
|
128
|
+
atts, md = Syntropy::Markdown.parse_md({}, src)
|
|
129
|
+
assert_equal 'nunu', atts[:layout]
|
|
130
|
+
|
|
131
|
+
@controller = Syntropy::Markdown.make_controller(@env, atts, md)
|
|
132
|
+
assert_raises(Syntropy::Error) {
|
|
133
|
+
req = @test_harness.request(':method' => 'GET', ':path' => '/')
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def test_markdown_renderer_with_code_snipet
|
|
138
|
+
md = <<~MD
|
|
139
|
+
foo
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
foo { bar }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
bar
|
|
146
|
+
MD
|
|
147
|
+
|
|
148
|
+
atts = { foo: 'bar' }
|
|
149
|
+
|
|
150
|
+
@controller = Syntropy::Markdown.make_controller(@env, atts, md)
|
|
151
|
+
req = @test_harness.request(':method' => 'GET', ':path' => '/')
|
|
152
|
+
|
|
153
|
+
assert_equal HTTP::OK, req.response_status
|
|
154
|
+
assert_equal(
|
|
155
|
+
(
|
|
156
|
+
<<~HTML.chomp
|
|
157
|
+
<!DOCTYPE html><html><head></head><body><p>foo</p>
|
|
158
|
+
|
|
159
|
+
<div class=\"language-ruby highlighter-rouge\"><div class=\"highlight\"><pre class=\"highlight\"><code><span class=\"n\">foo</span> <span class=\"p\">{</span> <span class=\"n\">bar</span> <span class=\"p\">}</span>
|
|
160
|
+
</code></pre></div></div>
|
|
161
|
+
|
|
162
|
+
<p>bar</p>
|
|
163
|
+
</body></html>
|
|
164
|
+
HTML
|
|
165
|
+
),
|
|
166
|
+
req.response_body
|
|
167
|
+
)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def test_markdown_renderer_with_embedded_papercraft_simple
|
|
171
|
+
md = <<~MD
|
|
172
|
+
foo
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
# render: true
|
|
176
|
+
foo { bar }
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
bar
|
|
180
|
+
MD
|
|
181
|
+
|
|
182
|
+
atts = { foo: 'bar' }
|
|
183
|
+
|
|
184
|
+
@controller = Syntropy::Markdown.make_controller(@env, atts, md)
|
|
185
|
+
req = @test_harness.request(':method' => 'GET', ':path' => '/')
|
|
186
|
+
|
|
187
|
+
assert_equal HTTP::OK, req.response_status
|
|
188
|
+
assert_equal(
|
|
189
|
+
(
|
|
190
|
+
<<~HTML.chomp
|
|
191
|
+
<!DOCTYPE html><html><head></head><body><p>foo</p>
|
|
192
|
+
|
|
193
|
+
<foo><bar></bar></foo>
|
|
194
|
+
<p>bar</p>
|
|
195
|
+
</body></html>
|
|
196
|
+
HTML
|
|
197
|
+
),
|
|
198
|
+
req.response_body
|
|
199
|
+
)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def test_markdown_renderer_with_embedded_papercraft_env
|
|
203
|
+
md = <<~MD
|
|
204
|
+
```ruby
|
|
205
|
+
# render: true
|
|
206
|
+
h3 @env[:app_root]
|
|
207
|
+
```
|
|
208
|
+
bar
|
|
209
|
+
MD
|
|
210
|
+
|
|
211
|
+
atts = { foo: 'bar' }
|
|
212
|
+
|
|
213
|
+
@controller = Syntropy::Markdown.make_controller(@env, atts, md)
|
|
214
|
+
req = @test_harness.request(':method' => 'GET', ':path' => '/')
|
|
215
|
+
|
|
216
|
+
assert_equal HTTP::OK, req.response_status
|
|
217
|
+
assert_equal(
|
|
218
|
+
(
|
|
219
|
+
<<~HTML.chomp
|
|
220
|
+
<!DOCTYPE html><html><head></head><body><h3>#{@env[:app_root]}</h3>
|
|
221
|
+
<p>bar</p>
|
|
222
|
+
</body></html>
|
|
223
|
+
HTML
|
|
224
|
+
),
|
|
225
|
+
req.response_body
|
|
226
|
+
)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def test_markdown_renderer_with_embedded_papercraft_dynamic
|
|
230
|
+
md = <<~MD
|
|
231
|
+
```ruby
|
|
232
|
+
# render: true
|
|
233
|
+
@env[:count] ||= 0
|
|
234
|
+
h3 (@env[:count] += 1)
|
|
235
|
+
```
|
|
236
|
+
bar
|
|
237
|
+
MD
|
|
238
|
+
|
|
239
|
+
atts = { foo: 'bar' }
|
|
240
|
+
|
|
241
|
+
@controller = Syntropy::Markdown.make_controller(@env, atts, md)
|
|
242
|
+
req = @test_harness.request(':method' => 'GET', ':path' => '/')
|
|
243
|
+
assert_equal HTTP::OK, req.response_status
|
|
244
|
+
assert_equal(
|
|
245
|
+
(
|
|
246
|
+
<<~HTML.chomp
|
|
247
|
+
<!DOCTYPE html><html><head></head><body><h3>1</h3>
|
|
248
|
+
<p>bar</p>
|
|
249
|
+
</body></html>
|
|
250
|
+
HTML
|
|
251
|
+
),
|
|
252
|
+
req.response_body
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
req = @test_harness.request(':method' => 'GET', ':path' => '/')
|
|
256
|
+
assert_equal HTTP::OK, req.response_status
|
|
257
|
+
assert_equal(
|
|
258
|
+
(
|
|
259
|
+
<<~HTML.chomp
|
|
260
|
+
<!DOCTYPE html><html><head></head><body><h3>2</h3>
|
|
261
|
+
<p>bar</p>
|
|
262
|
+
</body></html>
|
|
263
|
+
HTML
|
|
264
|
+
),
|
|
265
|
+
req.response_body
|
|
266
|
+
)
|
|
267
|
+
end
|
|
268
|
+
end
|