impression 0.3 → 0.7
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 +18 -0
- data/Gemfile.lock +10 -13
- data/examples/hello.rb +13 -0
- data/impression.gemspec +6 -7
- data/lib/impression/file_tree.rb +33 -3
- data/lib/impression/jamstack.rb +90 -29
- data/lib/impression/version.rb +1 -1
- data/test/jamstack/_layouts/default.rb +1 -1
- data/test/jamstack/articles/2008-06-14-manu.md +6 -0
- data/test/jamstack/articles/2009-06-12-noatche.md +6 -0
- data/test/jamstack/articles/a.md +1 -1
- data/test/jamstack/foobar.rb +10 -0
- data/test/jamstack/index.md +1 -0
- data/test/test_file_tree.rb +38 -0
- data/test/test_jamstack.rb +114 -11
- metadata +13 -49
- data/lib/impression/pages.rb +0 -239
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 871b003a25f9dd74ea137c483c2c208bdad9cf505d544c085f1f84eff394a278
|
4
|
+
data.tar.gz: 55623ee42bbc7213ef37c6632b1d4807b882301d133bc324607878857b7110c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29437cfb294efff3d90c7bcfa82bf05bba184b0ea4f68f13097650c2af17440e2d1294d95e06c17b981a4066c58dde70ac77fd3b8fbacb9ca7a676d08dfc71be
|
7
|
+
data.tar.gz: a787832534b192c37bc936f96a0c7b92c97b516dc2857b49fbefedab3a152c73301b6ce09e6b1bf581a77dd14bd8a2fa455c84e693b1ac29d5eb74208058bf33
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
# 0.7 2022-01-23
|
2
|
+
|
3
|
+
- Update Papercraft, Refactor Jamstack resource
|
4
|
+
|
5
|
+
# 0.6 2022-01-22
|
6
|
+
|
7
|
+
- Unify page_list entries and page_info cache entries (#10)
|
8
|
+
- Refactor FileTree#path_info to return more information
|
9
|
+
|
10
|
+
# 0.5 2022-01-20
|
11
|
+
|
12
|
+
- Pass resource and request to rendered templates (#8)
|
13
|
+
- Implement Jamstack#page_list method (#7)
|
14
|
+
|
15
|
+
# 0.4 2022-01-19
|
16
|
+
|
17
|
+
- Remove deprecated Pages code (#6)
|
18
|
+
|
1
19
|
# 0.3 2022-01-19
|
2
20
|
|
3
21
|
- Implement basic Jamstack resource (#5)
|
data/Gemfile.lock
CHANGED
@@ -1,14 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
impression (0.
|
5
|
-
kramdown (~> 2.3.0)
|
6
|
-
kramdown-parser-gfm (~> 1.1.0)
|
4
|
+
impression (0.7)
|
7
5
|
modulation (~> 1.1)
|
8
|
-
papercraft (~> 0.
|
6
|
+
papercraft (~> 0.17)
|
9
7
|
polyphony (~> 0.73.1)
|
10
|
-
qeweney (~> 0.
|
11
|
-
rouge (~> 3.26.0)
|
8
|
+
qeweney (~> 0.16)
|
12
9
|
tipi (~> 0.45)
|
13
10
|
|
14
11
|
GEM
|
@@ -53,20 +50,20 @@ GEM
|
|
53
50
|
localhost (1.1.9)
|
54
51
|
minitest (5.11.3)
|
55
52
|
modulation (1.1)
|
56
|
-
msgpack (1.4.
|
53
|
+
msgpack (1.4.4)
|
57
54
|
multipart-post (2.1.1)
|
58
|
-
papercraft (0.
|
59
|
-
escape_utils (
|
60
|
-
kramdown (~> 2.3.
|
55
|
+
papercraft (0.17)
|
56
|
+
escape_utils (~> 1.2.1)
|
57
|
+
kramdown (~> 2.3.1)
|
61
58
|
kramdown-parser-gfm (~> 1.1.0)
|
62
|
-
rouge (~> 3.
|
59
|
+
rouge (~> 3.27.0)
|
63
60
|
polyphony (0.73.1)
|
64
|
-
qeweney (0.
|
61
|
+
qeweney (0.16)
|
65
62
|
escape_utils (~> 1.2.1)
|
66
63
|
rack (2.2.3)
|
67
64
|
rake (12.3.3)
|
68
65
|
rexml (3.2.5)
|
69
|
-
rouge (3.
|
66
|
+
rouge (3.27.0)
|
70
67
|
ruby2_keywords (0.0.5)
|
71
68
|
simplecov (0.17.1)
|
72
69
|
docile (~> 1.1)
|
data/examples/hello.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'impression'
|
2
|
+
|
3
|
+
app = Impression.app do
|
4
|
+
mount '/' => text_response('Hello, world!')
|
5
|
+
end
|
6
|
+
|
7
|
+
# class App < Impression::Resource
|
8
|
+
# def route(req)
|
9
|
+
# @response ||= text_response('Hello, world!')
|
10
|
+
# end
|
11
|
+
# end
|
12
|
+
|
13
|
+
# run { |req| req.respond_text('Hello, world!') }
|
data/impression.gemspec
CHANGED
@@ -10,7 +10,10 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.files = `git ls-files`.split
|
11
11
|
s.homepage = 'http://github.com/digital-fabric/impression'
|
12
12
|
s.metadata = {
|
13
|
-
"source_code_uri" => "https://github.com/digital-fabric/impression"
|
13
|
+
"source_code_uri" => "https://github.com/digital-fabric/impression",
|
14
|
+
"documentation_uri" => "https://www.rubydoc.info/gems/impression",
|
15
|
+
"homepage_uri" => "https://github.com/digital-fabric/impression",
|
16
|
+
"changelog_uri" => "https://github.com/digital-fabric/impression/blob/master/CHANGELOG.md"
|
14
17
|
}
|
15
18
|
s.rdoc_options = ["--title", "impression", "--main", "README.md"]
|
16
19
|
s.extra_rdoc_files = ["README.md"]
|
@@ -21,14 +24,10 @@ Gem::Specification.new do |s|
|
|
21
24
|
|
22
25
|
s.add_runtime_dependency 'polyphony', '~>0.73.1'
|
23
26
|
s.add_runtime_dependency 'tipi', '~>0.45'
|
24
|
-
s.add_runtime_dependency 'qeweney', '~>0.
|
27
|
+
s.add_runtime_dependency 'qeweney', '~>0.16'
|
25
28
|
|
26
|
-
s.add_runtime_dependency 'kramdown', '~>2.3.0'
|
27
|
-
s.add_runtime_dependency 'rouge', '~>3.26.0'
|
28
|
-
s.add_runtime_dependency 'kramdown-parser-gfm', '~>1.1.0'
|
29
|
-
|
30
29
|
# s.add_runtime_dependency 'rb-inotify', '~>0.10.1'
|
31
|
-
s.add_runtime_dependency 'papercraft', '~>0.
|
30
|
+
s.add_runtime_dependency 'papercraft', '~>0.17'
|
32
31
|
s.add_runtime_dependency 'modulation', '~>1.1'
|
33
32
|
|
34
33
|
|
data/lib/impression/file_tree.rb
CHANGED
@@ -27,8 +27,6 @@ module Impression
|
|
27
27
|
render_from_path_info(req, path_info)
|
28
28
|
end
|
29
29
|
|
30
|
-
private
|
31
|
-
|
32
30
|
# Renders a response from the given response kind and path.
|
33
31
|
#
|
34
32
|
# @param req [Qeweney::Request] request
|
@@ -47,6 +45,9 @@ module Impression
|
|
47
45
|
|
48
46
|
private
|
49
47
|
|
48
|
+
PAGE_EXT_REGEXP = /^(.+)\.html$/.freeze
|
49
|
+
INDEX_PAGE_REGEXP = /^(.+)?\/index$/.freeze
|
50
|
+
|
50
51
|
# Renders a file response for the given request and the given path info.
|
51
52
|
#
|
52
53
|
# @param req [Qeweney::Request] request
|
@@ -89,8 +90,37 @@ module Impression
|
|
89
90
|
elsif stat.directory?
|
90
91
|
return directory_path_info(path)
|
91
92
|
else
|
92
|
-
|
93
|
+
file_info(path)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns the path info for the given file path.
|
98
|
+
#
|
99
|
+
# @param path [String] file path
|
100
|
+
# @return [Hash] path info
|
101
|
+
def file_info(path)
|
102
|
+
relative_path = path.gsub(/^#{@directory}/, '')
|
103
|
+
{
|
104
|
+
kind: :file,
|
105
|
+
path: path,
|
106
|
+
ext: File.extname(path),
|
107
|
+
url: pretty_url(relative_path)
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns the pretty URL for the given relative path. For pages, the
|
112
|
+
# extension is removed. For index pages, the index suffix is removed.
|
113
|
+
#
|
114
|
+
# @param relative_path [String] relative path
|
115
|
+
# @return [String] pretty URL
|
116
|
+
def pretty_url(relative_path)
|
117
|
+
if (m = relative_path.match(PAGE_EXT_REGEXP))
|
118
|
+
relative_path = m[1]
|
119
|
+
end
|
120
|
+
if (m = relative_path.match(INDEX_PAGE_REGEXP))
|
121
|
+
relative_path = m[1] || '/'
|
93
122
|
end
|
123
|
+
relative_path == '/' ? absolute_path : File.join(absolute_path, relative_path)
|
94
124
|
end
|
95
125
|
|
96
126
|
# Calculates the path info for a directory. If an index file exists, its
|
data/lib/impression/jamstack.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'fileutils'
|
4
|
+
require 'date'
|
4
5
|
require 'yaml'
|
5
6
|
require 'modulation'
|
6
7
|
require 'papercraft'
|
@@ -17,8 +18,68 @@ module Impression
|
|
17
18
|
@layouts = {}
|
18
19
|
end
|
19
20
|
|
21
|
+
# Returns a list of pages found in the given directory (relative to the base
|
22
|
+
# directory). Each entry containins the absolute file path, the pretty URL,
|
23
|
+
# the possible date parsed from the file name, and any other front matter
|
24
|
+
# attributes (for .md files). This method will detect only pages with the
|
25
|
+
# extensions .html, .md, .rb. The returned entries are sorted by file path.
|
26
|
+
#
|
27
|
+
# @param dir [String] relative directory
|
28
|
+
# @return [Array<Hash>] array of page entries
|
29
|
+
def page_list(dir)
|
30
|
+
base = File.join(@directory, dir)
|
31
|
+
Dir.glob('*.{html,md}', base: base)
|
32
|
+
.map { |fn| get_path_info(File.join(dir, fn)) }# page_entry(fn, dir) }
|
33
|
+
.sort_by { |i| i[:path] }
|
34
|
+
end
|
35
|
+
|
20
36
|
private
|
21
37
|
|
38
|
+
DATE_REGEXP = /(\d{4}\-\d{2}\-\d{2})/.freeze
|
39
|
+
FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m.freeze
|
40
|
+
MD_EXT_REGEXP = /\.md$/.freeze
|
41
|
+
PAGE_EXT_REGEXP = /^(.+)\.(md|html|rb)$/.freeze
|
42
|
+
INDEX_PAGE_REGEXP = /^(.+)?\/index$/.freeze
|
43
|
+
YAML_OPTS = {
|
44
|
+
permitted_classes: [Date]
|
45
|
+
}.freeze
|
46
|
+
|
47
|
+
# Returns the path info for the given file path.
|
48
|
+
#
|
49
|
+
# @param path [String] file path
|
50
|
+
# @return [Hash] path info
|
51
|
+
def file_info(path)
|
52
|
+
info = super
|
53
|
+
case info[:ext]
|
54
|
+
when '.md'
|
55
|
+
atts, content = parse_markdown_file(path)
|
56
|
+
info = info.merge(atts)
|
57
|
+
info[:html_content] = Papercraft.markdown(content)
|
58
|
+
when '.rb'
|
59
|
+
info[:module] = import(path)
|
60
|
+
end
|
61
|
+
if (m = path.match(DATE_REGEXP))
|
62
|
+
info[:date] ||= Date.parse(m[1])
|
63
|
+
end
|
64
|
+
|
65
|
+
info
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns the pretty URL for the given relative path. For pages, the
|
69
|
+
# extension is removed. For index pages, the index suffix is removed.
|
70
|
+
#
|
71
|
+
# @param relative_path [String] relative path
|
72
|
+
# @return [String] pretty URL
|
73
|
+
def pretty_url(relative_path)
|
74
|
+
if (m = relative_path.match(PAGE_EXT_REGEXP))
|
75
|
+
relative_path = m[1]
|
76
|
+
end
|
77
|
+
if (m = relative_path.match(INDEX_PAGE_REGEXP))
|
78
|
+
relative_path = m[1] || '/'
|
79
|
+
end
|
80
|
+
relative_path == '/' ? absolute_path : File.join(absolute_path, relative_path)
|
81
|
+
end
|
82
|
+
|
22
83
|
# Renders a file response for the given request and the given path info.
|
23
84
|
#
|
24
85
|
# @param req [Qeweney::Request] request
|
@@ -27,9 +88,9 @@ module Impression
|
|
27
88
|
def render_file(req, path_info)
|
28
89
|
case path_info[:ext]
|
29
90
|
when '.rb'
|
30
|
-
render_papercraft_module(req, path_info
|
91
|
+
render_papercraft_module(req, path_info)
|
31
92
|
when '.md'
|
32
|
-
render_markdown_file(req, path_info
|
93
|
+
render_markdown_file(req, path_info)
|
33
94
|
else
|
34
95
|
req.serve_file(path_info[:path])
|
35
96
|
end
|
@@ -38,26 +99,25 @@ module Impression
|
|
38
99
|
# Renders a Papercraft module. The module is loaded using Modulation.
|
39
100
|
#
|
40
101
|
# @param req [Qeweney::Request] reqest
|
41
|
-
# @param
|
102
|
+
# @param path_info [Hash] path info
|
42
103
|
# @return [void]
|
43
|
-
def render_papercraft_module(req,
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
req.respond(html, 'Content-Type' => Qeweney::MimeTypes[:html])
|
104
|
+
def render_papercraft_module(req, path_info)
|
105
|
+
template = Papercraft.html(path_info[:module])
|
106
|
+
body = template.render(request: req, resource: self)
|
107
|
+
req.respond(body, 'Content-Type' => template.mime_type)
|
48
108
|
end
|
49
109
|
|
50
110
|
# Renders a markdown file using a layout.
|
51
111
|
#
|
52
112
|
# @param req [Qeweney::Request] reqest
|
53
|
-
# @param
|
113
|
+
# @param path_info [Hash] path info
|
54
114
|
# @return [void]
|
55
|
-
def render_markdown_file(req,
|
56
|
-
|
115
|
+
def render_markdown_file(req, path_info)
|
116
|
+
layout = get_layout(path_info[:layout])
|
57
117
|
|
58
|
-
|
59
|
-
|
60
|
-
|
118
|
+
html = layout.render(request: req, resource: self, **path_info) {
|
119
|
+
emit path_info[:html_content]
|
120
|
+
}
|
61
121
|
req.respond(html, 'Content-Type' => Qeweney::MimeTypes[:html])
|
62
122
|
end
|
63
123
|
|
@@ -73,30 +133,31 @@ module Impression
|
|
73
133
|
|
74
134
|
import path
|
75
135
|
end
|
76
|
-
|
77
|
-
MARKDOWN_PAGE_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m.freeze
|
78
136
|
|
79
137
|
# Parses the markdown file at the given path.
|
80
138
|
#
|
81
139
|
# @param path [String] file path
|
82
140
|
# @return [Array] an tuple containing properties<Hash>, contents<String>
|
83
141
|
def parse_markdown_file(path)
|
84
|
-
|
85
|
-
|
86
|
-
front_matter = m[1]
|
142
|
+
content = IO.read(path) || ''
|
143
|
+
atts = {}
|
87
144
|
|
88
|
-
|
89
|
-
|
90
|
-
[
|
145
|
+
# Parse date from file name
|
146
|
+
if (m = path.match(DATE_REGEXP))
|
147
|
+
atts[:date] ||= Date.parse(m[1])
|
91
148
|
end
|
92
|
-
end
|
93
149
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
150
|
+
if (m = content.match(FRONT_MATTER_REGEXP))
|
151
|
+
front_matter = m[1]
|
152
|
+
content = m.post_match
|
153
|
+
|
154
|
+
yaml = YAML.load(front_matter, **YAML_OPTS)
|
155
|
+
yaml.each_with_object(atts) do |(k, v), h|
|
156
|
+
h[k.to_sym] = v
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
[atts, content]
|
100
161
|
end
|
101
162
|
|
102
163
|
# Returns the supported path extensions used for searching for files based
|
data/lib/impression/version.rb
CHANGED
data/test/jamstack/articles/a.md
CHANGED
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
layout = import('./_layouts/default')
|
4
|
+
|
5
|
+
export_default layout.apply(title: 'Foobar') { |resource:, request:, **props|
|
6
|
+
h1 request.query[:q]
|
7
|
+
resource.page_list('/articles').each do |i|
|
8
|
+
a i[:title], href: i[:url]
|
9
|
+
end
|
10
|
+
}
|
data/test/jamstack/index.md
CHANGED
data/test/test_file_tree.rb
CHANGED
@@ -113,4 +113,42 @@ class FileTreeTest < MiniTest::Test
|
|
113
113
|
@file_tree.route_and_call(req)
|
114
114
|
assert_response static('bar/index.html'), :html, req
|
115
115
|
end
|
116
|
+
|
117
|
+
def path_info(path)
|
118
|
+
@file_tree.send(:get_path_info, path)
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_path_info
|
122
|
+
assert_equal({
|
123
|
+
kind: :file,
|
124
|
+
path: File.join(__dir__, 'static/index.html'),
|
125
|
+
ext: '.html',
|
126
|
+
url: '/'
|
127
|
+
}, path_info('/index.html'))
|
128
|
+
|
129
|
+
assert_equal({
|
130
|
+
kind: :file,
|
131
|
+
path: File.join(__dir__, 'static/index.html'),
|
132
|
+
ext: '.html',
|
133
|
+
url: '/'
|
134
|
+
}, path_info('/index'))
|
135
|
+
|
136
|
+
assert_equal({
|
137
|
+
kind: :file,
|
138
|
+
path: File.join(__dir__, 'static/index.html'),
|
139
|
+
ext: '.html',
|
140
|
+
url: '/'
|
141
|
+
}, path_info('/'))
|
142
|
+
|
143
|
+
assert_equal({
|
144
|
+
kind: :file,
|
145
|
+
path: File.join(__dir__, 'static/js/a.js'),
|
146
|
+
ext: '.js',
|
147
|
+
url: '/js/a.js'
|
148
|
+
}, path_info('/js/a.js'))
|
149
|
+
|
150
|
+
assert_equal({
|
151
|
+
kind: :not_found,
|
152
|
+
}, path_info('/js/b.js'))
|
153
|
+
end
|
116
154
|
end
|
data/test/test_jamstack.rb
CHANGED
@@ -48,7 +48,7 @@ class JamstackTest < MiniTest::Test
|
|
48
48
|
req = mock_req(':method' => 'GET', ':path' => '/foo')
|
49
49
|
@jamstack.route_and_call(req)
|
50
50
|
|
51
|
-
foo =
|
51
|
+
foo = Papercraft.html {
|
52
52
|
html5 {
|
53
53
|
head {
|
54
54
|
title 'Foo title'
|
@@ -63,7 +63,7 @@ class JamstackTest < MiniTest::Test
|
|
63
63
|
req = mock_req(':method' => 'GET', ':path' => '/index')
|
64
64
|
@jamstack.route_and_call(req)
|
65
65
|
|
66
|
-
index =
|
66
|
+
index = Papercraft.html {
|
67
67
|
html5 {
|
68
68
|
head {
|
69
69
|
title 'Hello'
|
@@ -86,7 +86,7 @@ class JamstackTest < MiniTest::Test
|
|
86
86
|
req = mock_req(':method' => 'GET', ':path' => '/baz')
|
87
87
|
@jamstack.route_and_call(req)
|
88
88
|
|
89
|
-
baz_index =
|
89
|
+
baz_index = Papercraft.html {
|
90
90
|
html5 {
|
91
91
|
head {
|
92
92
|
title 'BarBar'
|
@@ -101,14 +101,14 @@ class JamstackTest < MiniTest::Test
|
|
101
101
|
req = mock_req(':method' => 'GET', ':path' => '/articles/a')
|
102
102
|
@jamstack.route_and_call(req)
|
103
103
|
|
104
|
-
a =
|
104
|
+
a = Papercraft.html {
|
105
105
|
html5 {
|
106
106
|
head {
|
107
107
|
title 'AAA'
|
108
108
|
}
|
109
109
|
body {
|
110
110
|
article {
|
111
|
-
h2 '
|
111
|
+
h2 'ZZZ', id: 'zzz'
|
112
112
|
}
|
113
113
|
}
|
114
114
|
}
|
@@ -116,7 +116,7 @@ class JamstackTest < MiniTest::Test
|
|
116
116
|
assert_response a.render, :html, req
|
117
117
|
end
|
118
118
|
|
119
|
-
def
|
119
|
+
def test_non_root_jamstack_response
|
120
120
|
@jamstack = Impression::Jamstack.new(path: '/app', directory: JAMSTACK_PATH)
|
121
121
|
|
122
122
|
req = mock_req(':method' => 'GET', ':path' => '/app/roo')
|
@@ -138,7 +138,7 @@ class JamstackTest < MiniTest::Test
|
|
138
138
|
req = mock_req(':method' => 'GET', ':path' => '/app/foo')
|
139
139
|
@jamstack.route_and_call(req)
|
140
140
|
|
141
|
-
foo =
|
141
|
+
foo = Papercraft.html {
|
142
142
|
html5 {
|
143
143
|
head {
|
144
144
|
title 'Foo title'
|
@@ -153,7 +153,7 @@ class JamstackTest < MiniTest::Test
|
|
153
153
|
req = mock_req(':method' => 'GET', ':path' => '/app/index')
|
154
154
|
@jamstack.route_and_call(req)
|
155
155
|
|
156
|
-
index =
|
156
|
+
index = Papercraft.html {
|
157
157
|
html5 {
|
158
158
|
head {
|
159
159
|
title 'Hello'
|
@@ -176,7 +176,7 @@ class JamstackTest < MiniTest::Test
|
|
176
176
|
req = mock_req(':method' => 'GET', ':path' => '/app/baz')
|
177
177
|
@jamstack.route_and_call(req)
|
178
178
|
|
179
|
-
baz_index =
|
179
|
+
baz_index = Papercraft.html {
|
180
180
|
html5 {
|
181
181
|
head {
|
182
182
|
title 'BarBar'
|
@@ -191,18 +191,121 @@ class JamstackTest < MiniTest::Test
|
|
191
191
|
req = mock_req(':method' => 'GET', ':path' => '/app/articles/a')
|
192
192
|
@jamstack.route_and_call(req)
|
193
193
|
|
194
|
-
a =
|
194
|
+
a = Papercraft.html {
|
195
195
|
html5 {
|
196
196
|
head {
|
197
197
|
title 'AAA'
|
198
198
|
}
|
199
199
|
body {
|
200
200
|
article {
|
201
|
-
h2 '
|
201
|
+
h2 'ZZZ', id: 'zzz'
|
202
202
|
}
|
203
203
|
}
|
204
204
|
}
|
205
205
|
}
|
206
206
|
assert_response a.render, :html, req
|
207
207
|
end
|
208
|
+
|
209
|
+
def test_page_list
|
210
|
+
@jamstack = Impression::Jamstack.new(path: '/app', directory: JAMSTACK_PATH)
|
211
|
+
|
212
|
+
list = @jamstack.page_list('/')
|
213
|
+
assert_equal [
|
214
|
+
{ kind: :file, path: File.join(JAMSTACK_PATH, 'bar.html'), ext: '.html', url: '/app/bar' },
|
215
|
+
{ kind: :file, path: File.join(JAMSTACK_PATH, 'index.md'), ext: '.md', url: '/app',
|
216
|
+
title: 'Hello', foo: 'BarBar', html_content: "<h1>Index</h1>\n" },
|
217
|
+
], list
|
218
|
+
|
219
|
+
|
220
|
+
list = @jamstack.page_list('/articles')
|
221
|
+
|
222
|
+
assert_equal [
|
223
|
+
{
|
224
|
+
kind: :file,
|
225
|
+
path: File.join(JAMSTACK_PATH, 'articles/2008-06-14-manu.md'),
|
226
|
+
url: '/app/articles/2008-06-14-manu',
|
227
|
+
ext: '.md',
|
228
|
+
title: 'MMM',
|
229
|
+
layout: 'article',
|
230
|
+
html_content: "<h2 id=\"bbb\">BBB</h2>\n",
|
231
|
+
date: Date.new(2008, 06, 14)
|
232
|
+
},
|
233
|
+
{
|
234
|
+
kind: :file,
|
235
|
+
path: File.join(JAMSTACK_PATH, 'articles/2009-06-12-noatche.md'),
|
236
|
+
url: '/app/articles/2009-06-12-noatche',
|
237
|
+
ext: '.md',
|
238
|
+
title: 'NNN',
|
239
|
+
layout: 'article',
|
240
|
+
html_content: "<h2 id=\"ccc\">CCC</h2>\n",
|
241
|
+
date: Date.new(2009, 06, 12)
|
242
|
+
},
|
243
|
+
{
|
244
|
+
kind: :file,
|
245
|
+
path: File.join(JAMSTACK_PATH, 'articles/a.md'),
|
246
|
+
url: '/app/articles/a',
|
247
|
+
ext: '.md',
|
248
|
+
title: 'AAA',
|
249
|
+
layout: 'article',
|
250
|
+
html_content: "<h2 id=\"zzz\">ZZZ</h2>\n",
|
251
|
+
},
|
252
|
+
], list
|
253
|
+
end
|
254
|
+
|
255
|
+
def test_template_resource_and_request
|
256
|
+
req = mock_req(':method' => 'GET', ':path' => '/foobar?q=42')
|
257
|
+
@jamstack.route_and_call(req)
|
258
|
+
|
259
|
+
foo = Papercraft.html {
|
260
|
+
html5 {
|
261
|
+
head {
|
262
|
+
title 'Foobar'
|
263
|
+
}
|
264
|
+
body {
|
265
|
+
h1 '42'
|
266
|
+
a 'MMM', href: '/articles/2008-06-14-manu'
|
267
|
+
a 'NNN', href: '/articles/2009-06-12-noatche'
|
268
|
+
a 'AAA', href: '/articles/a'
|
269
|
+
}
|
270
|
+
}
|
271
|
+
}
|
272
|
+
assert_response foo.render, :html, req
|
273
|
+
end
|
274
|
+
|
275
|
+
def path_info(path)
|
276
|
+
@jamstack.send(:get_path_info, path)
|
277
|
+
end
|
278
|
+
|
279
|
+
def test_path_info
|
280
|
+
assert_equal({
|
281
|
+
kind: :file,
|
282
|
+
path: File.join(JAMSTACK_PATH, 'index.md'),
|
283
|
+
ext: '.md',
|
284
|
+
url: '/',
|
285
|
+
title: 'Hello',
|
286
|
+
foo: 'BarBar',
|
287
|
+
html_content: "<h1>Index</h1>\n"
|
288
|
+
}, path_info('/index'))
|
289
|
+
|
290
|
+
assert_equal({
|
291
|
+
kind: :file,
|
292
|
+
path: File.join(JAMSTACK_PATH, 'index.md'),
|
293
|
+
ext: '.md',
|
294
|
+
url: '/',
|
295
|
+
title: 'Hello',
|
296
|
+
foo: 'BarBar',
|
297
|
+
html_content: "<h1>Index</h1>\n"
|
298
|
+
}, path_info('/'))
|
299
|
+
|
300
|
+
assert_equal({
|
301
|
+
kind: :file,
|
302
|
+
path: File.join(JAMSTACK_PATH, 'assets/js/a.js'),
|
303
|
+
ext: '.js',
|
304
|
+
url: '/assets/js/a.js'
|
305
|
+
}, path_info('/assets/js/a.js'))
|
306
|
+
|
307
|
+
assert_equal({
|
308
|
+
kind: :not_found,
|
309
|
+
}, path_info('/js/b.js'))
|
310
|
+
end
|
208
311
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: impression
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.7'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-01-
|
11
|
+
date: 2022-01-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: polyphony
|
@@ -44,70 +44,28 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0.
|
47
|
+
version: '0.16'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0.
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: kramdown
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - "~>"
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 2.3.0
|
62
|
-
type: :runtime
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - "~>"
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 2.3.0
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: rouge
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 3.26.0
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 3.26.0
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: kramdown-parser-gfm
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - "~>"
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: 1.1.0
|
90
|
-
type: :runtime
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - "~>"
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: 1.1.0
|
54
|
+
version: '0.16'
|
97
55
|
- !ruby/object:Gem::Dependency
|
98
56
|
name: papercraft
|
99
57
|
requirement: !ruby/object:Gem::Requirement
|
100
58
|
requirements:
|
101
59
|
- - "~>"
|
102
60
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0.
|
61
|
+
version: '0.17'
|
104
62
|
type: :runtime
|
105
63
|
prerelease: false
|
106
64
|
version_requirements: !ruby/object:Gem::Requirement
|
107
65
|
requirements:
|
108
66
|
- - "~>"
|
109
67
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0.
|
68
|
+
version: '0.17'
|
111
69
|
- !ruby/object:Gem::Dependency
|
112
70
|
name: modulation
|
113
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -181,6 +139,7 @@ files:
|
|
181
139
|
- README.md
|
182
140
|
- Rakefile
|
183
141
|
- TODO.md
|
142
|
+
- examples/hello.rb
|
184
143
|
- examples/markdown/_assets/style.css
|
185
144
|
- examples/markdown/app.rb
|
186
145
|
- examples/markdown/docs/index.md
|
@@ -192,7 +151,6 @@ files:
|
|
192
151
|
- lib/impression/file_tree.rb
|
193
152
|
- lib/impression/file_watcher.rb
|
194
153
|
- lib/impression/jamstack.rb
|
195
|
-
- lib/impression/pages.rb
|
196
154
|
- lib/impression/request_extensions.rb
|
197
155
|
- lib/impression/request_extensions/responses.rb
|
198
156
|
- lib/impression/request_extensions/routing.rb
|
@@ -201,11 +159,14 @@ files:
|
|
201
159
|
- test/helper.rb
|
202
160
|
- test/jamstack/_layouts/article.rb
|
203
161
|
- test/jamstack/_layouts/default.rb
|
162
|
+
- test/jamstack/articles/2008-06-14-manu.md
|
163
|
+
- test/jamstack/articles/2009-06-12-noatche.md
|
204
164
|
- test/jamstack/articles/a.md
|
205
165
|
- test/jamstack/assets/js/a.js
|
206
166
|
- test/jamstack/bar.html
|
207
167
|
- test/jamstack/baz/index.md
|
208
168
|
- test/jamstack/foo.rb
|
169
|
+
- test/jamstack/foobar.rb
|
209
170
|
- test/jamstack/index.md
|
210
171
|
- test/run.rb
|
211
172
|
- test/static/bar/index.html
|
@@ -222,6 +183,9 @@ licenses:
|
|
222
183
|
- MIT
|
223
184
|
metadata:
|
224
185
|
source_code_uri: https://github.com/digital-fabric/impression
|
186
|
+
documentation_uri: https://www.rubydoc.info/gems/impression
|
187
|
+
homepage_uri: https://github.com/digital-fabric/impression
|
188
|
+
changelog_uri: https://github.com/digital-fabric/impression/blob/master/CHANGELOG.md
|
225
189
|
post_install_message:
|
226
190
|
rdoc_options:
|
227
191
|
- "--title"
|
data/lib/impression/pages.rb
DELETED
@@ -1,239 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'kramdown'
|
4
|
-
require 'rouge'
|
5
|
-
require 'kramdown-parser-gfm'
|
6
|
-
require 'yaml'
|
7
|
-
require 'rb-inotify'
|
8
|
-
require 'papercraft'
|
9
|
-
|
10
|
-
require_relative './errors'
|
11
|
-
|
12
|
-
module Impression
|
13
|
-
class Pages
|
14
|
-
module RequestMethods
|
15
|
-
def serve_page(pages)
|
16
|
-
pages.serve(self)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def initialize(base_path, opts = {})
|
21
|
-
@base_path = base_path
|
22
|
-
@opts = opts
|
23
|
-
@opts[:pages] = self
|
24
|
-
|
25
|
-
load
|
26
|
-
|
27
|
-
if opts[:auto_reload]
|
28
|
-
start_automatic_reloader
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def start_automatic_reloader
|
33
|
-
notifier = INotify::Notifier.new
|
34
|
-
watched = {}
|
35
|
-
@map.each_value do |entry|
|
36
|
-
path = entry[:full_path]
|
37
|
-
next if watched[path]
|
38
|
-
|
39
|
-
notifier.watch(path, :modify, :delete_self) { |e| handle_changed_file(path) }
|
40
|
-
watched[path] = true
|
41
|
-
end
|
42
|
-
notifier.watch(@base_path, :moved_to, :create) do |event|
|
43
|
-
path = event.absolute_name
|
44
|
-
if File.file?(path)
|
45
|
-
notifier.watch(path, :modify, :delete_self) { |e| handle_changed_file(path) }
|
46
|
-
end
|
47
|
-
handle_changed_file(path)
|
48
|
-
end
|
49
|
-
@reloader = spin do
|
50
|
-
notify_io = notifier.to_io
|
51
|
-
loop do
|
52
|
-
notify_io.wait_readable
|
53
|
-
notifier.process
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def handle_changed_file(full_path)
|
59
|
-
p handle_changed_file: full_path
|
60
|
-
if !File.file?(full_path)
|
61
|
-
@map.reject! { |k, v| v[:full_path] == full_path }
|
62
|
-
return
|
63
|
-
end
|
64
|
-
|
65
|
-
path = File.basename(full_path)
|
66
|
-
page = Page.new(path, full_path, @opts)
|
67
|
-
permalink = page.permalink
|
68
|
-
@map[permalink] = { page: page, full_path: full_path }
|
69
|
-
@map['/'] = @map[permalink] if permalink == '/index'
|
70
|
-
end
|
71
|
-
|
72
|
-
def load
|
73
|
-
@map = {}
|
74
|
-
Dir['**/*', base: @base_path].each do |path|
|
75
|
-
next if path =~ /\/_.+/
|
76
|
-
|
77
|
-
full_path = File.join(@base_path, path)
|
78
|
-
next unless File.file?(full_path)
|
79
|
-
|
80
|
-
page = Page.new(path, full_path, @opts)
|
81
|
-
@map[page.permalink] = { page: page, full_path: full_path }
|
82
|
-
end
|
83
|
-
@map['/'] = @map['/index']
|
84
|
-
end
|
85
|
-
alias_method :reload, :load
|
86
|
-
|
87
|
-
def prev_page(page)
|
88
|
-
keys = @map.keys
|
89
|
-
case idx = keys.index(page.permalink)
|
90
|
-
when 0, nil
|
91
|
-
nil
|
92
|
-
else
|
93
|
-
@map[keys[idx - 1]][:page]
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def next_page(page)
|
98
|
-
keys = @map.keys
|
99
|
-
case idx = keys.index(page.permalink)
|
100
|
-
when keys.size - 1, nil
|
101
|
-
nil
|
102
|
-
else
|
103
|
-
@map[keys[idx + 1]][:page]
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def load_file(path)
|
108
|
-
content = IO.read(path)
|
109
|
-
end
|
110
|
-
|
111
|
-
def serve(req)
|
112
|
-
entry = @map[req.route_relative_path]
|
113
|
-
raise NotFoundError unless entry
|
114
|
-
|
115
|
-
body = render_page(entry[:page])
|
116
|
-
req.respond(body, 'Content-Type' => 'text/html')
|
117
|
-
rescue NotFoundError => e
|
118
|
-
req.respond('Not found.', ':status' => e.http_status)
|
119
|
-
end
|
120
|
-
alias_method :call, :serve
|
121
|
-
|
122
|
-
def render_page(page)
|
123
|
-
layout_proc(page.layout).().render(pages: self, page: page)
|
124
|
-
end
|
125
|
-
|
126
|
-
def layout_proc(layout)
|
127
|
-
full_path = File.expand_path("../_layouts/#{layout}.rb", @base_path)
|
128
|
-
instance_eval("->(&block) do; #{IO.read(full_path)}; end", full_path)
|
129
|
-
end
|
130
|
-
|
131
|
-
def select(selector)
|
132
|
-
@map.inject([]) do |array, (permalink, entry)|
|
133
|
-
array << entry[:page] if permalink =~ selector
|
134
|
-
array
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
class Page
|
140
|
-
attr_reader :attributes
|
141
|
-
|
142
|
-
def initialize(path, full_path, opts = {})
|
143
|
-
@path = path
|
144
|
-
@full_path = full_path
|
145
|
-
@opts = opts
|
146
|
-
@kind = detect_page_kind(full_path)
|
147
|
-
read_page
|
148
|
-
end
|
149
|
-
|
150
|
-
EXTNAME_REGEXP = /^\.(.+)$/.freeze
|
151
|
-
|
152
|
-
def detect_page_kind(path)
|
153
|
-
File.extname(path).match(EXTNAME_REGEXP)[1].to_sym
|
154
|
-
end
|
155
|
-
|
156
|
-
PAGE_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m.freeze
|
157
|
-
|
158
|
-
def read_page
|
159
|
-
data = IO.read(@full_path) || ''
|
160
|
-
if data =~ PAGE_REGEXP
|
161
|
-
front_matter = Regexp.last_match(1)
|
162
|
-
@content_start_line = front_matter.lines.size + 2
|
163
|
-
@attributes = YAML.load(front_matter)
|
164
|
-
@content = Regexp.last_match.post_match
|
165
|
-
else
|
166
|
-
@attributes = {}
|
167
|
-
@content_start_line = 1
|
168
|
-
@content = data
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
def permalink
|
173
|
-
@permalink = @attributes[:permalink] || path_without_extension
|
174
|
-
end
|
175
|
-
|
176
|
-
def path_without_extension
|
177
|
-
"/#{@path.delete_suffix(File.extname(@path))}"
|
178
|
-
end
|
179
|
-
|
180
|
-
def title
|
181
|
-
@attributes['title'] || title_from_content
|
182
|
-
end
|
183
|
-
|
184
|
-
def prev_page
|
185
|
-
@opts[:pages].prev_page(self)
|
186
|
-
end
|
187
|
-
|
188
|
-
def next_page
|
189
|
-
@opts[:pages].next_page(self)
|
190
|
-
end
|
191
|
-
|
192
|
-
TITLE_REGEXP = /^#\s+([^\n]+)/.freeze
|
193
|
-
|
194
|
-
def title_from_content
|
195
|
-
(@content =~ TITLE_REGEXP) && Regexp.last_match(1)
|
196
|
-
end
|
197
|
-
|
198
|
-
def status
|
199
|
-
@attributes['status'] || Qeweney::Status::OK
|
200
|
-
end
|
201
|
-
|
202
|
-
def layout
|
203
|
-
layout = @attributes['layout'] || 'default'
|
204
|
-
end
|
205
|
-
|
206
|
-
def render
|
207
|
-
case @kind
|
208
|
-
when :md
|
209
|
-
render_markdown
|
210
|
-
when :rb
|
211
|
-
render_papercraft
|
212
|
-
else
|
213
|
-
raise "Invalid page kind #{kind.inspect}"
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
def render_markdown
|
218
|
-
Kramdown::Document.new(@content, **kramdown_options).to_html
|
219
|
-
end
|
220
|
-
|
221
|
-
def kramdown_options
|
222
|
-
{
|
223
|
-
entity_output: :numeric,
|
224
|
-
syntax_highlighter: :rouge,
|
225
|
-
input: 'GFM',
|
226
|
-
hard_wrap: false
|
227
|
-
}
|
228
|
-
end
|
229
|
-
|
230
|
-
def render_papercraft
|
231
|
-
proc = instance_eval("->(&block) do; #{@content}; end", @full_path, @content_start_line)
|
232
|
-
proc.().render(page: self, pages: @opts[:pages])
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
class Qeweney::Request
|
238
|
-
include Impression::Pages::RequestMethods
|
239
|
-
end
|