impression 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/test.yml +32 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +59 -27
- data/Rakefile +10 -0
- data/examples/markdown/_assets/style.css +98 -0
- data/examples/markdown/app.rb +11 -5
- data/examples/markdown/docs/tutorial.md +15 -0
- data/impression.gemspec +7 -3
- data/lib/impression/app.rb +9 -0
- data/lib/impression/file_tree.rb +89 -0
- data/lib/impression/file_watcher.rb +50 -0
- data/lib/impression/pages.rb +178 -59
- data/lib/impression/request_extensions.rb +6 -2
- data/lib/impression/request_routing.rb +50 -0
- data/lib/impression/resource.rb +141 -0
- data/lib/impression/version.rb +1 -1
- data/lib/impression.rb +7 -0
- data/test/helper.rb +94 -0
- data/test/run.rb +5 -0
- data/test/static/bar/index.html +1 -0
- data/test/static/foo.html +1 -0
- data/test/static/index.html +1 -0
- data/test/static/js/a.js +1 -0
- data/test/test_app.rb +52 -0
- data/test/test_file_tree.rb +116 -0
- data/test/test_file_watcher.rb +57 -0
- data/test/test_resource.rb +129 -0
- metadata +55 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aaea11fcf3fcaab1902290a620d9cdb0fa5dfb11ae38c90335f80471629d652f
|
4
|
+
data.tar.gz: 9eaebd5c2a28856a568e583399c3ea6db8332e6c2aae6d570639505788203dc1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 145722a3828e039ed1a021c002cc0a551e66f78a300d869203f15d8c3df8c2d532d71d37674a7c11b1d995c39cb138a06040665a785e22581473843d28581a49
|
7
|
+
data.tar.gz: 9d179f895f2d819f1c63762099137dda5511614176c9e25df4aef7d735975fbda25167b5f3c3a94788f96c8b37ece7c2f42acc7b54dcaa38054863976f039ccf
|
data/.github/FUNDING.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
github: ciconia
|
@@ -0,0 +1,32 @@
|
|
1
|
+
name: Tests
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
strategy:
|
8
|
+
fail-fast: false
|
9
|
+
matrix:
|
10
|
+
os: [ubuntu-latest, macos-latest]
|
11
|
+
ruby: ['2.7', '3.0', '3.1', 'head']
|
12
|
+
|
13
|
+
name: >-
|
14
|
+
${{matrix.os}}, ${{matrix.ruby}}
|
15
|
+
|
16
|
+
runs-on: ${{matrix.os}}
|
17
|
+
|
18
|
+
env:
|
19
|
+
POLYPHONY_USE_LIBEV: "1"
|
20
|
+
|
21
|
+
steps:
|
22
|
+
- name: Setup machine
|
23
|
+
uses: actions/checkout@v1
|
24
|
+
- name: Setup Ruby and install deps
|
25
|
+
uses: ruby/setup-ruby@v1
|
26
|
+
with:
|
27
|
+
ruby-version: ${{matrix.ruby}}
|
28
|
+
bundler-cache: true # 'bundle install' and cache
|
29
|
+
cache-version: 1
|
30
|
+
|
31
|
+
- name: Run tests
|
32
|
+
run: bundle exec rake test
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,53 +1,86 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
impression (0.
|
4
|
+
impression (0.2)
|
5
5
|
kramdown (~> 2.3.0)
|
6
6
|
kramdown-parser-gfm (~> 1.1.0)
|
7
|
-
|
7
|
+
papercraft (~> 0.12)
|
8
|
+
polyphony (~> 0.73.1)
|
9
|
+
qeweney (~> 0.15)
|
8
10
|
rouge (~> 3.26.0)
|
9
|
-
tipi (~> 0.
|
11
|
+
tipi (~> 0.45)
|
10
12
|
|
11
13
|
GEM
|
12
14
|
remote: https://rubygems.org/
|
13
15
|
specs:
|
14
|
-
|
15
|
-
|
16
|
-
docile (1.
|
16
|
+
acme-client (2.0.9)
|
17
|
+
faraday (>= 0.17, < 2.0.0)
|
18
|
+
docile (1.4.0)
|
17
19
|
escape_utils (1.2.1)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
ever (0.1)
|
21
|
+
extralite (1.11)
|
22
|
+
faraday (1.9.3)
|
23
|
+
faraday-em_http (~> 1.0)
|
24
|
+
faraday-em_synchrony (~> 1.0)
|
25
|
+
faraday-excon (~> 1.1)
|
26
|
+
faraday-httpclient (~> 1.0)
|
27
|
+
faraday-multipart (~> 1.0)
|
28
|
+
faraday-net_http (~> 1.0)
|
29
|
+
faraday-net_http_persistent (~> 1.0)
|
30
|
+
faraday-patron (~> 1.0)
|
31
|
+
faraday-rack (~> 1.0)
|
32
|
+
faraday-retry (~> 1.0)
|
33
|
+
ruby2_keywords (>= 0.0.4)
|
34
|
+
faraday-em_http (1.0.0)
|
35
|
+
faraday-em_synchrony (1.0.0)
|
36
|
+
faraday-excon (1.1.0)
|
37
|
+
faraday-httpclient (1.0.1)
|
38
|
+
faraday-multipart (1.0.3)
|
39
|
+
multipart-post (>= 1.2, < 3)
|
40
|
+
faraday-net_http (1.0.1)
|
41
|
+
faraday-net_http_persistent (1.2.0)
|
42
|
+
faraday-patron (1.0.0)
|
43
|
+
faraday-rack (1.0.0)
|
44
|
+
faraday-retry (1.0.3)
|
45
|
+
h1p (0.2)
|
46
|
+
http-2 (0.11.0)
|
47
|
+
json (2.6.1)
|
48
|
+
kramdown (2.3.1)
|
22
49
|
rexml
|
23
50
|
kramdown-parser-gfm (1.1.0)
|
24
51
|
kramdown (~> 2.0)
|
52
|
+
localhost (1.1.9)
|
25
53
|
minitest (5.11.3)
|
26
|
-
minitest-reporters (1.4.3)
|
27
|
-
ansi
|
28
|
-
builder
|
29
|
-
minitest (>= 5.0)
|
30
|
-
ruby-progressbar
|
31
54
|
msgpack (1.4.2)
|
32
|
-
|
33
|
-
|
55
|
+
multipart-post (2.1.1)
|
56
|
+
papercraft (0.12)
|
57
|
+
escape_utils (= 1.2.1)
|
58
|
+
kramdown (~> 2.3.0)
|
59
|
+
kramdown-parser-gfm (~> 1.1.0)
|
60
|
+
rouge (~> 3.26.0)
|
61
|
+
polyphony (0.73.1)
|
62
|
+
qeweney (0.15)
|
34
63
|
escape_utils (~> 1.2.1)
|
35
64
|
rack (2.2.3)
|
36
65
|
rake (12.3.3)
|
37
|
-
rexml (3.2.
|
38
|
-
rouge (3.26.
|
39
|
-
|
66
|
+
rexml (3.2.5)
|
67
|
+
rouge (3.26.1)
|
68
|
+
ruby2_keywords (0.0.5)
|
40
69
|
simplecov (0.17.1)
|
41
70
|
docile (~> 1.1)
|
42
71
|
json (>= 1.8, < 3)
|
43
72
|
simplecov-html (~> 0.10.0)
|
44
73
|
simplecov-html (0.10.2)
|
45
|
-
tipi (0.
|
46
|
-
|
47
|
-
|
74
|
+
tipi (0.45)
|
75
|
+
acme-client (~> 2.0.8)
|
76
|
+
ever (~> 0.1)
|
77
|
+
extralite (~> 1.2)
|
78
|
+
h1p (~> 0.2)
|
79
|
+
http-2 (~> 0.11)
|
80
|
+
localhost (~> 1.1.4)
|
48
81
|
msgpack (~> 1.4.2)
|
49
|
-
polyphony (~> 0.
|
50
|
-
qeweney (~> 0.
|
82
|
+
polyphony (~> 0.71)
|
83
|
+
qeweney (~> 0.14)
|
51
84
|
rack (>= 2.0.8, < 2.3.0)
|
52
85
|
websocket (~> 1.2.8)
|
53
86
|
websocket (1.2.9)
|
@@ -58,9 +91,8 @@ PLATFORMS
|
|
58
91
|
DEPENDENCIES
|
59
92
|
impression!
|
60
93
|
minitest (~> 5.11.3)
|
61
|
-
minitest-reporters (~> 1.4.2)
|
62
94
|
rake (~> 12.3.3)
|
63
95
|
simplecov (~> 0.17.1)
|
64
96
|
|
65
97
|
BUNDLED WITH
|
66
|
-
2.
|
98
|
+
2.3.3
|
data/Rakefile
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
* {
|
2
|
+
border: 0;
|
3
|
+
font: inherit;
|
4
|
+
font-size: 100%;
|
5
|
+
vertical-align: baseline;
|
6
|
+
margin: 0;
|
7
|
+
padding: 0;
|
8
|
+
color: black;
|
9
|
+
}
|
10
|
+
|
11
|
+
body {
|
12
|
+
font: normal 14px monospace;
|
13
|
+
}
|
14
|
+
|
15
|
+
h1, h2 {
|
16
|
+
font-weight: bold;
|
17
|
+
}
|
18
|
+
|
19
|
+
header {
|
20
|
+
margin-bottom: 20px;
|
21
|
+
}
|
22
|
+
|
23
|
+
a {
|
24
|
+
color: -webkit-link;
|
25
|
+
}
|
26
|
+
|
27
|
+
li {
|
28
|
+
margin-left: 40px;
|
29
|
+
}
|
30
|
+
|
31
|
+
em {
|
32
|
+
font-style: italic;
|
33
|
+
}
|
34
|
+
|
35
|
+
strong {
|
36
|
+
font-weight: bold;
|
37
|
+
}
|
38
|
+
|
39
|
+
p, ul, ol {
|
40
|
+
margin: 20px;
|
41
|
+
}
|
42
|
+
|
43
|
+
pre {
|
44
|
+
margin-bottom: 20px;
|
45
|
+
padding: 20px 5px;
|
46
|
+
overflow-x: visible;
|
47
|
+
}
|
48
|
+
|
49
|
+
pre code {
|
50
|
+
word-wrap: no;
|
51
|
+
}
|
52
|
+
|
53
|
+
hr {
|
54
|
+
display: block;
|
55
|
+
margin-top: 0.5em;
|
56
|
+
margin-bottom: 0.5em;
|
57
|
+
margin-left: auto;
|
58
|
+
margin-right: auto;
|
59
|
+
border-style: inset;
|
60
|
+
border-width: 1px;
|
61
|
+
}
|
62
|
+
|
63
|
+
#nav {
|
64
|
+
padding: 20px 5px;
|
65
|
+
}
|
66
|
+
|
67
|
+
#nav, #content, footer {
|
68
|
+
margin: 0 20px;
|
69
|
+
max-width: 580px;
|
70
|
+
}
|
71
|
+
|
72
|
+
#nav h1 {
|
73
|
+
margin-bottom: 20px;
|
74
|
+
}
|
75
|
+
|
76
|
+
iframe {
|
77
|
+
margin: 20px;
|
78
|
+
max-width: 530px;
|
79
|
+
}
|
80
|
+
|
81
|
+
#content img {
|
82
|
+
max-width: 100%;
|
83
|
+
}
|
84
|
+
|
85
|
+
#title {
|
86
|
+
margin-top: 40px;
|
87
|
+
font-weight: bold;
|
88
|
+
text-align: center;
|
89
|
+
}
|
90
|
+
|
91
|
+
#date {
|
92
|
+
display: block;
|
93
|
+
text-align: right;
|
94
|
+
}
|
95
|
+
|
96
|
+
#webring {
|
97
|
+
padding-bottom: 10px;
|
98
|
+
}
|
data/examples/markdown/app.rb
CHANGED
@@ -11,13 +11,19 @@ opts = {
|
|
11
11
|
}
|
12
12
|
|
13
13
|
puts "pid: #{Process.pid}"
|
14
|
-
puts 'Listening on port 4411...'
|
15
14
|
|
16
|
-
Tipi.
|
17
|
-
|
15
|
+
Tipi.full_service do |req|
|
16
|
+
req.route do
|
17
|
+
if req.host != 'noteflakes.com'
|
18
|
+
req.respond(nil, ':status' => Qeweney::Status::SERVICE_UNAVAILABLE)
|
19
|
+
stop_routing
|
20
|
+
end
|
21
|
+
req.on('assets') { req.serve_file(req.route_relative_path, base_path: File.join(__dir__, '_assets')) }
|
22
|
+
req.default { app.call(req) }
|
23
|
+
end
|
18
24
|
rescue Exception => e
|
19
|
-
p e
|
20
|
-
puts e.backtrace.join("\n")
|
25
|
+
p [req.path, e]
|
26
|
+
# puts e.backtrace.join("\n")
|
21
27
|
status = e.respond_to?(:http_status) ? e.http_status : 500
|
22
28
|
req.respond(e.inspect, ':status' => status)
|
23
29
|
end
|
data/impression.gemspec
CHANGED
@@ -19,15 +19,19 @@ Gem::Specification.new do |s|
|
|
19
19
|
|
20
20
|
# s.executables = ['impression']
|
21
21
|
|
22
|
-
s.add_runtime_dependency 'polyphony', '~>0.
|
23
|
-
s.add_runtime_dependency 'tipi', '~>0.
|
22
|
+
s.add_runtime_dependency 'polyphony', '~>0.73.1'
|
23
|
+
s.add_runtime_dependency 'tipi', '~>0.45'
|
24
|
+
s.add_runtime_dependency 'qeweney', '~>0.15'
|
25
|
+
|
24
26
|
s.add_runtime_dependency 'kramdown', '~>2.3.0'
|
25
27
|
s.add_runtime_dependency 'rouge', '~>3.26.0'
|
26
28
|
s.add_runtime_dependency 'kramdown-parser-gfm', '~>1.1.0'
|
27
29
|
|
30
|
+
# s.add_runtime_dependency 'rb-inotify', '~>0.10.1'
|
31
|
+
s.add_runtime_dependency 'papercraft', '~>0.12'
|
32
|
+
|
28
33
|
|
29
34
|
s.add_development_dependency 'rake', '~>12.3.3'
|
30
35
|
s.add_development_dependency 'minitest', '~>5.11.3'
|
31
|
-
s.add_development_dependency 'minitest-reporters', '~>1.4.2'
|
32
36
|
s.add_development_dependency 'simplecov', '~>0.17.1'
|
33
37
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require_relative './resource'
|
5
|
+
|
6
|
+
module Impression
|
7
|
+
|
8
|
+
# `FileTree` implements a resource that maps to a static file hierarchy.
|
9
|
+
class FileTree < Resource
|
10
|
+
|
11
|
+
# Initializes a `FileTree` resource.
|
12
|
+
#
|
13
|
+
# @param directory [String] static directory path
|
14
|
+
# @return [void]
|
15
|
+
def initialize(directory:, **props)
|
16
|
+
super(**props)
|
17
|
+
@directory = directory
|
18
|
+
@path_info_cache = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
# Responds to a request.
|
22
|
+
#
|
23
|
+
# @param req [Qeweney::Request] request
|
24
|
+
# @return [void]
|
25
|
+
def respond(req)
|
26
|
+
path_info = get_path_info(req.resource_relative_path)
|
27
|
+
render_from_path_info(req, *path_info)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Renders a response from the given response kind and path.
|
33
|
+
#
|
34
|
+
# @param req [Qeweney::Request] request
|
35
|
+
# @param kind [Symbol] path kind (`:not_found` or `:file`)
|
36
|
+
# @param path [String, nil] file path
|
37
|
+
def render_from_path_info(req, kind, path = nil)
|
38
|
+
case kind
|
39
|
+
when :not_found
|
40
|
+
req.respond(nil, ':status' => Qeweney::Status::NOT_FOUND)
|
41
|
+
when :file
|
42
|
+
req.serve_file(path)
|
43
|
+
else
|
44
|
+
raise "Invalid path info kind #{kind.inspect}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the path info for the given relative path.
|
49
|
+
#
|
50
|
+
# @param path [String] relative path
|
51
|
+
# @return [Array] path info (a tuple comprising kind and file path)
|
52
|
+
def get_path_info(path)
|
53
|
+
@path_info_cache[path] || calculate_path_info(path)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Calculates the path info for the given relative path.
|
57
|
+
#
|
58
|
+
# @param path [String] relative path
|
59
|
+
# @param add_html_ext [bool] whether to add .html extension if not found
|
60
|
+
# @return [Array] path info
|
61
|
+
def calculate_path_info(path, add_html_ext = true)
|
62
|
+
full_path = File.join(@directory, path)
|
63
|
+
|
64
|
+
stat = File.stat(full_path) rescue nil
|
65
|
+
if !stat
|
66
|
+
return add_html_ext ?
|
67
|
+
calculate_path_info("#{path}.html", false) : [:not_found]
|
68
|
+
elsif stat.directory?
|
69
|
+
return calculate_directory_path_info(full_path)
|
70
|
+
else
|
71
|
+
return [:file, full_path]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Calculates the path info for a directory. If an `index.html` file exists,
|
76
|
+
# its path info is returned, otherwise a `:not_found` path info is returned.
|
77
|
+
#
|
78
|
+
# @param path [String] directory path
|
79
|
+
# @return [Array] path info
|
80
|
+
def calculate_directory_path_info(path)
|
81
|
+
index_path = File.join(path, 'index.html')
|
82
|
+
if File.file?(index_path)
|
83
|
+
[:file, index_path]
|
84
|
+
else
|
85
|
+
[:not_found]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rb-inotify'
|
4
|
+
|
5
|
+
module Impression
|
6
|
+
class FileWatcher
|
7
|
+
def initialize(spec)
|
8
|
+
@notifier = INotify::Notifier.new
|
9
|
+
@buffer = []
|
10
|
+
setup(spec)
|
11
|
+
end
|
12
|
+
|
13
|
+
def setup(spec)
|
14
|
+
if File.file?(spec)
|
15
|
+
setup_single_file(spec)
|
16
|
+
elsif File.directory?(spec)
|
17
|
+
setup_directory(spec)
|
18
|
+
else
|
19
|
+
dir = File.dir_name(spec)
|
20
|
+
filename = File.basename(spec)
|
21
|
+
if dir =~ /\/\*\*$/
|
22
|
+
dir = File.dir_name(dir)
|
23
|
+
filename = "**/#{filename}"
|
24
|
+
end
|
25
|
+
setup_directory(dir, filename)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def setup_directory(dir)
|
30
|
+
@notifier.watch(dir, :moved_to, :create, :move, :attrib, :modify, :delete) do |event|
|
31
|
+
kind = event.flags.first
|
32
|
+
|
33
|
+
@buffer << [kind, event.absolute_name]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def each(&block)
|
38
|
+
@receiver = Fiber.current
|
39
|
+
io = @notifier.to_io
|
40
|
+
loop do
|
41
|
+
io.wait_readable
|
42
|
+
@notifier.process
|
43
|
+
next if @buffer.empty?
|
44
|
+
|
45
|
+
@buffer.each(&block)
|
46
|
+
@buffer.clear
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|