impression 0.10 → 0.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +3 -4
- data/CHANGELOG.md +12 -0
- data/Gemfile +0 -5
- data/Gemfile.lock +18 -18
- data/README.md +78 -8
- data/impression.gemspec +4 -4
- data/lib/impression/app.rb +324 -3
- data/lib/impression/file_tree.rb +4 -2
- data/lib/impression/rack_app.rb +27 -0
- data/lib/impression/version.rb +1 -1
- data/lib/impression.rb +23 -11
- data/test/{jamstack → app}/_layouts/article.rb +0 -0
- data/test/{jamstack → app}/_layouts/default.rb +0 -0
- data/test/{jamstack → app}/articles/2008-06-14-manu.md +0 -0
- data/test/{jamstack → app}/articles/2009-06-12-noatche.md +0 -0
- data/test/{jamstack → app}/articles/a.md +0 -0
- data/test/{jamstack → app}/assets/js/a.js +0 -0
- data/test/{jamstack → app}/bar.html +0 -0
- data/test/{jamstack → app}/baz/index.md +0 -0
- data/test/{jamstack → app}/foo.rb +0 -0
- data/test/{jamstack → app}/foobar.rb +0 -0
- data/test/{jamstack → app}/index.md +0 -0
- data/test/{jamstack → app}/resources/greeter.rb +0 -0
- data/test/{jamstack → app}/resources/recurse.rb +1 -1
- data/test/test_app.rb +349 -6
- data/test/test_file_tree.rb +18 -0
- data/test/test_impression.rb +3 -3
- data/test/test_rack_app.rb +48 -0
- metadata +25 -25
- data/lib/impression/jamstack.rb +0 -276
- data/test/test_jamstack.rb +0 -350
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7dff68cd22a426ae305742591ca09a3aa737cf453af1f186a54170671170f250
|
4
|
+
data.tar.gz: 920c5ca0698212f318dcc967cc09405e0b2f25e8d8521580c2fce331a4bfe982
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 98d8b4da9955eb4ac433cb74c45b9edeae759850e3a2b28cb4933f4445d554c25a360f968323f1a7a6db0bd9e748a2570048efc698a88803ac86920d77452757
|
7
|
+
data.tar.gz: e9e91a9fb0e9754fef82946f6a26368e53eb6c131d1e5d2ac0fe6e46274b75787cd2c47ee352a4dd015fd402ee2895fbfef2be1b7b01d84b8e3327ff91d18010
|
data/.github/workflows/test.yml
CHANGED
@@ -16,7 +16,7 @@ jobs:
|
|
16
16
|
runs-on: ${{matrix.os}}
|
17
17
|
|
18
18
|
env:
|
19
|
-
|
19
|
+
POLYPHONY_LIBEV: "1"
|
20
20
|
|
21
21
|
steps:
|
22
22
|
- name: Setup machine
|
@@ -25,8 +25,7 @@ jobs:
|
|
25
25
|
uses: ruby/setup-ruby@v1
|
26
26
|
with:
|
27
27
|
ruby-version: ${{matrix.ruby}}
|
28
|
-
bundler-cache: true
|
29
|
-
cache-version:
|
30
|
-
|
28
|
+
bundler-cache: true
|
29
|
+
cache-version: 4
|
31
30
|
- name: Run tests
|
32
31
|
run: bundle exec rake test
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
## 0.13 2022-04-05
|
2
|
+
|
3
|
+
- Prevent race condiution on loading Ruby modules
|
4
|
+
|
5
|
+
## 0.12 2022-02-16
|
6
|
+
|
7
|
+
- Implement `RackApp` resource class (#16)
|
8
|
+
|
9
|
+
## 0.11 2022-02-10
|
10
|
+
|
11
|
+
- Update Tipi
|
12
|
+
|
1
13
|
## 0.10 2022-02-10
|
2
14
|
|
3
15
|
- Add support for resource modules in Jamstack (#13)
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
impression (0.
|
4
|
+
impression (0.13)
|
5
5
|
modulation (~> 1.1)
|
6
|
-
papercraft (~> 0.
|
7
|
-
polyphony (~> 0.
|
8
|
-
qeweney (~> 0.
|
9
|
-
tipi (~> 0.
|
6
|
+
papercraft (~> 0.23)
|
7
|
+
polyphony (~> 0.93)
|
8
|
+
qeweney (~> 0.18)
|
9
|
+
tipi (~> 0.52)
|
10
10
|
|
11
11
|
GEM
|
12
12
|
remote: https://rubygems.org/
|
@@ -16,8 +16,8 @@ GEM
|
|
16
16
|
docile (1.4.0)
|
17
17
|
escape_utils (1.2.1)
|
18
18
|
ever (0.1)
|
19
|
-
extralite (1.
|
20
|
-
faraday (1.
|
19
|
+
extralite (1.14)
|
20
|
+
faraday (1.10.0)
|
21
21
|
faraday-em_http (~> 1.0)
|
22
22
|
faraday-em_synchrony (~> 1.0)
|
23
23
|
faraday-excon (~> 1.1)
|
@@ -40,25 +40,25 @@ GEM
|
|
40
40
|
faraday-patron (1.0.0)
|
41
41
|
faraday-rack (1.0.0)
|
42
42
|
faraday-retry (1.0.3)
|
43
|
-
h1p (0.
|
43
|
+
h1p (0.5)
|
44
44
|
http-2 (0.11.0)
|
45
45
|
json (2.6.1)
|
46
|
-
kramdown (2.3.
|
46
|
+
kramdown (2.3.2)
|
47
47
|
rexml
|
48
48
|
kramdown-parser-gfm (1.1.0)
|
49
49
|
kramdown (~> 2.0)
|
50
50
|
localhost (1.1.9)
|
51
51
|
minitest (5.11.3)
|
52
52
|
modulation (1.1)
|
53
|
-
msgpack (1.4.
|
53
|
+
msgpack (1.4.5)
|
54
54
|
multipart-post (2.1.1)
|
55
|
-
papercraft (0.
|
55
|
+
papercraft (0.24)
|
56
56
|
escape_utils (~> 1.2.1)
|
57
57
|
kramdown (~> 2.3.1)
|
58
58
|
kramdown-parser-gfm (~> 1.1.0)
|
59
59
|
rouge (~> 3.27.0)
|
60
|
-
polyphony (0.
|
61
|
-
qeweney (0.
|
60
|
+
polyphony (0.93)
|
61
|
+
qeweney (0.18)
|
62
62
|
escape_utils (~> 1.2.1)
|
63
63
|
rack (2.2.3)
|
64
64
|
rake (12.3.3)
|
@@ -70,16 +70,16 @@ GEM
|
|
70
70
|
json (>= 1.8, < 3)
|
71
71
|
simplecov-html (~> 0.10.0)
|
72
72
|
simplecov-html (0.10.2)
|
73
|
-
tipi (0.
|
73
|
+
tipi (0.52)
|
74
74
|
acme-client (~> 2.0.9)
|
75
75
|
ever (~> 0.1)
|
76
|
-
extralite (~> 1.
|
77
|
-
h1p (~> 0.
|
76
|
+
extralite (~> 1.14)
|
77
|
+
h1p (~> 0.4)
|
78
78
|
http-2 (~> 0.11)
|
79
79
|
localhost (~> 1.1.4)
|
80
80
|
msgpack (~> 1.4.2)
|
81
|
-
polyphony (~> 0.
|
82
|
-
qeweney (~> 0.
|
81
|
+
polyphony (~> 0.80)
|
82
|
+
qeweney (~> 0.18)
|
83
83
|
rack (>= 2.0.8, < 2.3.0)
|
84
84
|
websocket (~> 1.2.8)
|
85
85
|
websocket (1.2.9)
|
data/README.md
CHANGED
@@ -19,20 +19,90 @@
|
|
19
19
|
## What is Impression
|
20
20
|
|
21
21
|
> Impression is still in a very early stage of development. Things might not
|
22
|
-
> correctly
|
22
|
+
> work correctly.
|
23
23
|
|
24
24
|
Impression is a modern web framework for Ruby. Unlike other web framework,
|
25
25
|
Impression does not impose any rigid structure or paradigm, but instead provides
|
26
|
-
a set of tools letting you build any kind of web app, by freely
|
27
|
-
kinds of web resources, be they static files, structured
|
28
|
-
sites, or dynamic APIs.
|
26
|
+
a minimalistic set of tools, letting you build any kind of web app, by freely
|
27
|
+
mixing different kinds of web resources, be they static files, structured
|
28
|
+
templates, Jamstack sites, or dynamic APIs.
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
## Resources
|
31
|
+
|
32
|
+
The main abstraction in Impression is the resource - which represents an web
|
33
|
+
endpoint that is mounted at a specific location in the URL namespace, and
|
34
|
+
responds to requests. Resources can be nested in order to create arbitrarily
|
35
|
+
complex routing trees. Impression provides multiple resource types, each
|
36
|
+
customized for a specific use case, be it a JSON API, a set of MVC-style
|
37
|
+
controllers, or a Markdown-based blog with static content.
|
38
|
+
|
39
|
+
Finally, any kind of resource can be used as an Impression app. Routing is
|
40
|
+
performed automatically according to the resource tree, starting from the root
|
41
|
+
resource.
|
42
|
+
|
43
|
+
## The request-response cycle
|
44
|
+
|
45
|
+
The handling of incoming HTTP requests is done in two stages. First the request
|
46
|
+
is routed to the corresponding resource, which then handles the request by
|
47
|
+
generating a response.
|
48
|
+
|
49
|
+
HTTP requests and responses use the
|
50
|
+
[Qeweney](https://github.com/digital-fabric/qeweney) API.
|
51
|
+
|
52
|
+
## Resource types
|
53
|
+
|
54
|
+
Impression provides the following resources:
|
55
|
+
|
56
|
+
- `Resource` - a generic resource.
|
57
|
+
- `FileTree` - a resource serving static files from the given directory.
|
58
|
+
- `App` - a resource serving static files, markdown files with layouts and Ruby
|
59
|
+
modules from the given directory.
|
60
|
+
- `RackApp` - a resource serving the given Rack app.
|
61
|
+
|
62
|
+
## Setting up a basic resource
|
63
|
+
|
64
|
+
To setup a generic resource, call `Impression.resource` and provide a request
|
65
|
+
handler:
|
66
|
+
|
67
|
+
```
|
68
|
+
app = Impression.resource { |req| req.respond('Hello, world!') }
|
69
|
+
```
|
70
|
+
|
71
|
+
## Running your app with Tipi
|
32
72
|
|
33
73
|
Impression is made for running on top of
|
34
|
-
[Tipi](https://github.com/digital-fabric/tipi)
|
35
|
-
|
74
|
+
[Tipi](https://github.com/digital-fabric/tipi). Your Tipi app file would like
|
75
|
+
something like the following:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
# app.rb
|
79
|
+
app = Impression.resource { |req| req.respond('Hello, world!') }
|
80
|
+
Tipi.run(&app)
|
81
|
+
```
|
82
|
+
|
83
|
+
You can then start Tipi by running `tipi run app.rb`.
|
84
|
+
|
85
|
+
## Running your app with a Rack app server
|
86
|
+
|
87
|
+
You can also run your app on any Rack app server, using something like the
|
88
|
+
following:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
app = Impression.resource { |req| req.respond('Hello, world!') }
|
92
|
+
run Qeweney.rack(&app)
|
93
|
+
```
|
94
|
+
|
95
|
+
## Creating a routing map with resources
|
96
|
+
|
97
|
+
A resource can be mounted at any point in the app's URL space. Resources can be
|
98
|
+
nested within other resources by passing a `parent:` argument when creating a
|
99
|
+
resource:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
app = Impression.app { |req| req.respond('Homepage') }
|
103
|
+
greeter = Impression.resource(parent: app, path: 'greeter')
|
104
|
+
static = Impression.file_tree(parent: app, path: 'static', directory: __dir__)
|
105
|
+
```
|
36
106
|
|
37
107
|
## I want to know more
|
38
108
|
|
data/impression.gemspec
CHANGED
@@ -20,11 +20,11 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.require_paths = ["lib"]
|
21
21
|
s.required_ruby_version = '>= 2.6'
|
22
22
|
|
23
|
-
s.add_runtime_dependency 'polyphony', '~>0.
|
24
|
-
s.add_runtime_dependency 'tipi', '~>0.
|
25
|
-
s.add_runtime_dependency 'qeweney', '~>0.
|
23
|
+
s.add_runtime_dependency 'polyphony', '~>0.93'
|
24
|
+
s.add_runtime_dependency 'tipi', '~>0.52'
|
25
|
+
s.add_runtime_dependency 'qeweney', '~>0.18'
|
26
26
|
|
27
|
-
s.add_runtime_dependency 'papercraft', '~>0.
|
27
|
+
s.add_runtime_dependency 'papercraft', '~>0.23'
|
28
28
|
s.add_runtime_dependency 'modulation', '~>1.1'
|
29
29
|
|
30
30
|
|
data/lib/impression/app.rb
CHANGED
@@ -1,9 +1,330 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'fileutils'
|
4
|
+
require 'date'
|
5
|
+
require 'yaml'
|
6
|
+
require 'modulation'
|
7
|
+
require 'papercraft'
|
8
|
+
|
9
|
+
require_relative './resource'
|
10
|
+
require_relative './file_tree'
|
11
|
+
|
3
12
|
module Impression
|
4
|
-
|
5
|
-
|
6
|
-
|
13
|
+
|
14
|
+
# `App` implements a resource that maps to a generic app directory.
|
15
|
+
class App < FileTree
|
16
|
+
def initialize(**props)
|
17
|
+
super
|
18
|
+
@layouts = {}
|
19
|
+
@file_info_loader = spin { run_file_info_loader }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns a list of pages found in the given directory (relative to the base
|
23
|
+
# directory). Each entry containins the absolute file path, the pretty URL,
|
24
|
+
# the possible date parsed from the file name, and any other front matter
|
25
|
+
# attributes (for .md files). This method will detect only pages with the
|
26
|
+
# extensions .html, .md, .rb. The returned entries are sorted by file path.
|
27
|
+
#
|
28
|
+
# @param dir [String] relative directory
|
29
|
+
# @return [Array<Hash>] array of page entries
|
30
|
+
def page_list(dir)
|
31
|
+
base = File.join(@directory, dir)
|
32
|
+
Dir.glob('*.{html,md}', base: base)
|
33
|
+
.map { |fn| get_path_info(File.join(dir, fn)) }# page_entry(fn, dir) }
|
34
|
+
.sort_by { |i| i[:path] }
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
DATE_REGEXP = /(\d{4}\-\d{2}\-\d{2})/.freeze
|
40
|
+
FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m.freeze
|
41
|
+
MD_EXT_REGEXP = /\.md$/.freeze
|
42
|
+
PAGE_EXT_REGEXP = /^(.+)\.(md|html|rb)$/.freeze
|
43
|
+
INDEX_PAGE_REGEXP = /^(.+)?\/index$/.freeze
|
44
|
+
|
45
|
+
YAML_OPTS = {
|
46
|
+
permitted_classes: [Date],
|
47
|
+
symbolize_names: true
|
48
|
+
}.freeze
|
49
|
+
|
50
|
+
# Runs a file info loader handling incoming requests for file info. This
|
51
|
+
# method is run in a fiber setup in #initialize.
|
52
|
+
#
|
53
|
+
# @return [void]
|
54
|
+
def run_file_info_loader
|
55
|
+
loop do
|
56
|
+
peer, path = receive
|
57
|
+
begin
|
58
|
+
info = calculate_path_info(path)
|
59
|
+
peer << info
|
60
|
+
rescue Polyphony::BaseException
|
61
|
+
raise
|
62
|
+
rescue => e
|
63
|
+
peer.raise(e)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def safe_calculate_path_info(path)
|
69
|
+
@file_info_loader << [Fiber.current, path]
|
70
|
+
receive
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the path info for the given relative path.
|
74
|
+
#
|
75
|
+
# @param path [String] relative path
|
76
|
+
# @return [Hash] path info
|
77
|
+
def get_path_info(path)
|
78
|
+
@path_info_cache[path] ||= safe_calculate_path_info(path)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns complete file info for Markdown files
|
82
|
+
#
|
83
|
+
# @param info [Hash] file info
|
84
|
+
# @param path [String] file path
|
85
|
+
# @return [Hash] file info
|
86
|
+
def file_info_md(info, path)
|
87
|
+
atts, content = parse_markdown_file(path)
|
88
|
+
info = info.merge(atts)
|
89
|
+
info[:html_content] = Papercraft.markdown(content)
|
90
|
+
info[:kind] = :markdown
|
91
|
+
if !info[:date] && (m = path.match(DATE_REGEXP))
|
92
|
+
info[:date] = Date.parse(m[1])
|
93
|
+
end
|
94
|
+
info
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns complete file info for Ruby files
|
98
|
+
#
|
99
|
+
# @param info [Hash] file info
|
100
|
+
# @param path [String] file path
|
101
|
+
# @return [Hash] file info
|
102
|
+
def file_info_rb(info, path)
|
103
|
+
info.merge(
|
104
|
+
kind: :module,
|
105
|
+
module: import(path)
|
106
|
+
)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the path info for the given file path.
|
110
|
+
#
|
111
|
+
# @param path [String] file path
|
112
|
+
# @return [Hash] path info
|
113
|
+
def file_info(path)
|
114
|
+
info = super
|
115
|
+
case info[:ext]
|
116
|
+
when '.md'
|
117
|
+
file_info_md(info, path)
|
118
|
+
when '.rb'
|
119
|
+
file_info_rb(info, path)
|
120
|
+
else
|
121
|
+
info
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns the pretty URL for the given relative path. For pages, the
|
126
|
+
# extension is removed. For index pages, the index suffix is removed.
|
127
|
+
#
|
128
|
+
# @param relative_path [String] relative path
|
129
|
+
# @return [String] pretty URL
|
130
|
+
def pretty_url(relative_path)
|
131
|
+
if (m = relative_path.match(PAGE_EXT_REGEXP))
|
132
|
+
relative_path = m[1]
|
133
|
+
end
|
134
|
+
if (m = relative_path.match(INDEX_PAGE_REGEXP))
|
135
|
+
relative_path = m[1] || '/'
|
136
|
+
end
|
137
|
+
relative_path == '/' ? absolute_path : File.join(absolute_path, relative_path)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Renders a response according to the given path info.
|
141
|
+
#
|
142
|
+
# @param req [Qeweney::Request] request
|
143
|
+
# @param path_info [Hash] path info
|
144
|
+
# @return [void]
|
145
|
+
def render_from_path_info(req, path_info)
|
146
|
+
case (kind = path_info[:kind])
|
147
|
+
when :not_found
|
148
|
+
mod_path_info = up_tree_resource_module_path_info(req, path_info)
|
149
|
+
if mod_path_info
|
150
|
+
render_module(req, mod_path_info)
|
151
|
+
else
|
152
|
+
req.respond(nil, ':status' => Qeweney::Status::NOT_FOUND)
|
153
|
+
end
|
154
|
+
when :module
|
155
|
+
render_module(req, path_info)
|
156
|
+
when :markdown
|
157
|
+
render_markdown_file(req, path_info)
|
158
|
+
when :file
|
159
|
+
render_file(req, path_info)
|
160
|
+
else
|
161
|
+
raise "Invalid path info kind #{kind.inspect}"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns the path info for an up-tree resource module, or false if not
|
166
|
+
# found. the :up_tree_resource_module_path_info KV can be either:
|
167
|
+
# - nil (default): up tree module search has not been performed.
|
168
|
+
# - false: no up tree module was found.
|
169
|
+
# - module path info: up tree module info (subsequent requests will be
|
170
|
+
# directly routed to the module).
|
171
|
+
#
|
172
|
+
# @param req [Qeweney::Request] request
|
173
|
+
# @param path_info [Hash] path info
|
174
|
+
# @return [Hash, false] up-tree resource module path info
|
175
|
+
def up_tree_resource_module_path_info(req, path_info)
|
176
|
+
if path_info[:up_tree_resource_module_path_info].nil?
|
177
|
+
if (mod_path_info = find_up_tree_resource_module(req, path_info))
|
178
|
+
path_info[:up_tree_resource_module_path_info] = mod_path_info
|
179
|
+
return mod_path_info;
|
180
|
+
else
|
181
|
+
path_info[:up_tree_resource_module_path_info] = false
|
182
|
+
return false
|
183
|
+
end
|
184
|
+
end
|
185
|
+
path_info[:up_tree_resource_module_path_info]
|
186
|
+
end
|
187
|
+
|
188
|
+
# Performs a recursive search for an up-tree resource module from the given
|
189
|
+
# path info. If a resource module is found up the tree, its path_info is
|
190
|
+
# returned, otherwise returns nil.
|
191
|
+
#
|
192
|
+
# @param req [Qeweney::Request] request
|
193
|
+
# @param path_info [Hash] path info
|
194
|
+
# @return [Hash, nil] up-tree resource module path info
|
195
|
+
def find_up_tree_resource_module(req, path_info)
|
196
|
+
relative_path = req.resource_relative_path
|
197
|
+
|
198
|
+
while relative_path != path
|
199
|
+
up_tree_path = File.expand_path('..', relative_path)
|
200
|
+
return nil if up_tree_path == relative_path
|
201
|
+
|
202
|
+
up_tree_path_info = get_path_info(up_tree_path)
|
203
|
+
case up_tree_path_info[:kind]
|
204
|
+
when :not_found
|
205
|
+
relative_path = up_tree_path
|
206
|
+
next
|
207
|
+
when :module
|
208
|
+
return up_tree_path_info
|
209
|
+
else
|
210
|
+
return nil
|
211
|
+
end
|
212
|
+
end
|
213
|
+
nil
|
214
|
+
end
|
215
|
+
|
216
|
+
# Renders a file response for the given request and the given path info,
|
217
|
+
# according to the file type.
|
218
|
+
#
|
219
|
+
# @param req [Qeweney::Request] request
|
220
|
+
# @param path_info [Hash] path info
|
221
|
+
# @return [void]
|
222
|
+
# def render_file(req, path_info)
|
223
|
+
# case path_info[:kind]
|
224
|
+
# else
|
225
|
+
# req.serve_file(path_info[:path])
|
226
|
+
# end
|
227
|
+
# end
|
228
|
+
|
229
|
+
# Renders a module. If the module is a Resource, it is mounted, and then the
|
230
|
+
# request is rerouted from the new resource and rendered. If the module is a
|
231
|
+
# Proc or a Papercraft::Template, it is rendered as such. Otherwise, an
|
232
|
+
# error is raised.
|
233
|
+
#
|
234
|
+
# @param req [Qeweney::Request] request
|
235
|
+
# @param path_info [Hash] path info
|
236
|
+
# @return [void]
|
237
|
+
def render_module(req, path_info)
|
238
|
+
# p render_module: path_info
|
239
|
+
case (mod = path_info[:module])
|
240
|
+
when Module
|
241
|
+
resource = mod.resource
|
242
|
+
resource.remount(self, path_info[:url])
|
243
|
+
# p path_info_url: path_info[:url], relative_path: req.resource_relative_path
|
244
|
+
relative_url = path_info[:url].gsub(/^#{path}/, '')
|
245
|
+
# p relative_url: relative_url
|
246
|
+
req.recalc_resource_relative_path(relative_url)
|
247
|
+
# p resource_relative_path: req.resource_relative_path
|
248
|
+
resource.route(req).call(req)
|
249
|
+
when Impression::Resource
|
250
|
+
mod.remount(self, path_info[:url])
|
251
|
+
req.recalc_resource_relative_path(path_info[:url])
|
252
|
+
mod.route(req).call(req)
|
253
|
+
when Proc, Papercraft::Template
|
254
|
+
render_papercraft_module(req, mod)
|
255
|
+
else
|
256
|
+
raise "Unsupported module type #{mod.class}"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Renders a Papercraft module.
|
261
|
+
#
|
262
|
+
# @param mod [Module] Papercraft module
|
263
|
+
# @param path_info [Hash] path info
|
264
|
+
# @return [void]
|
265
|
+
def render_papercraft_module(req, mod)
|
266
|
+
template = Papercraft.html(mod)
|
267
|
+
body = template.render(request: req, resource: self)
|
268
|
+
req.respond(body, 'Content-Type' => template.mime_type)
|
269
|
+
end
|
270
|
+
|
271
|
+
# Renders a markdown file using a layout.
|
272
|
+
#
|
273
|
+
# @param req [Qeweney::Request] reqest
|
274
|
+
# @param path_info [Hash] path info
|
275
|
+
# @return [void]
|
276
|
+
def render_markdown_file(req, path_info)
|
277
|
+
layout = get_layout(path_info[:layout])
|
278
|
+
|
279
|
+
html = layout.render(request: req, resource: self, **path_info) {
|
280
|
+
emit path_info[:html_content]
|
281
|
+
}
|
282
|
+
req.respond(html, 'Content-Type' => layout.mime_type)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Returns a layout component based on the given name. The given name
|
286
|
+
# defaults to 'default' if nil.
|
287
|
+
#
|
288
|
+
# @param layout [String, nil] layout name
|
289
|
+
# @return [Papercraft::Template] layout component
|
290
|
+
def get_layout(layout)
|
291
|
+
layout ||= 'default'
|
292
|
+
path = File.join(@directory, "_layouts/#{layout}.rb")
|
293
|
+
raise "Layout not found #{path}" unless File.file?(path)
|
294
|
+
|
295
|
+
import path
|
296
|
+
end
|
297
|
+
|
298
|
+
# Parses the markdown file at the given path.
|
299
|
+
#
|
300
|
+
# @param path [String] file path
|
301
|
+
# @return [Array] an tuple containing properties<Hash>, contents<String>
|
302
|
+
def parse_markdown_file(path)
|
303
|
+
content = IO.read(path) || ''
|
304
|
+
atts = {}
|
305
|
+
|
306
|
+
# Parse date from file name
|
307
|
+
if (m = path.match(DATE_REGEXP))
|
308
|
+
atts[:date] ||= Date.parse(m[1])
|
309
|
+
end
|
310
|
+
|
311
|
+
if (m = content.match(FRONT_MATTER_REGEXP))
|
312
|
+
front_matter = m[1]
|
313
|
+
content = m.post_match
|
314
|
+
|
315
|
+
yaml = YAML.safe_load(front_matter, **YAML_OPTS)
|
316
|
+
atts = atts.merge(yaml)
|
317
|
+
end
|
318
|
+
|
319
|
+
[atts, content]
|
320
|
+
end
|
321
|
+
|
322
|
+
# Returns the supported path extensions used for searching for files based
|
323
|
+
# on pretty URLs.
|
324
|
+
#
|
325
|
+
# @return [Array] list of supported path extensions
|
326
|
+
def supported_path_extensions
|
327
|
+
[:html, :rb, :md]
|
7
328
|
end
|
8
329
|
end
|
9
330
|
end
|
data/lib/impression/file_tree.rb
CHANGED
@@ -14,8 +14,8 @@ module Impression
|
|
14
14
|
#
|
15
15
|
# @param directory [String] static directory path
|
16
16
|
# @return [void]
|
17
|
-
def initialize(directory
|
18
|
-
super(**props)
|
17
|
+
def initialize(directory: nil, **props, &block)
|
18
|
+
super(**props, &block)
|
19
19
|
@directory = directory
|
20
20
|
@path_info_cache = {}
|
21
21
|
end
|
@@ -25,6 +25,8 @@ module Impression
|
|
25
25
|
# @param req [Qeweney::Request] request
|
26
26
|
# @return [void]
|
27
27
|
def call(req)
|
28
|
+
return super if @directory.nil?
|
29
|
+
|
28
30
|
path_info = get_path_info(req.resource_relative_path)
|
29
31
|
render_from_path_info(req, path_info)
|
30
32
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'tipi'
|
5
|
+
require_relative './resource'
|
6
|
+
|
7
|
+
module Impression
|
8
|
+
|
9
|
+
# The `RackApp` class represents Rack apps as resources.
|
10
|
+
class RackApp < Resource
|
11
|
+
def initialize(app: nil, **props, &block)
|
12
|
+
raise "No Rack app given" unless app || block
|
13
|
+
|
14
|
+
# We pass nil as the block, otherwise the block will pass to
|
15
|
+
# Resource#initialize, which will cause #call to be overidden.
|
16
|
+
super(**props, &nil)
|
17
|
+
@handler = Tipi::RackAdapter.run(app || block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(req)
|
21
|
+
if @path != '/'
|
22
|
+
req.rewrite!(@path, '/')
|
23
|
+
end
|
24
|
+
@handler.(req)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/impression/version.rb
CHANGED