impression 0.1 → 0.2
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/.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
data/lib/impression/pages.rb
CHANGED
@@ -4,6 +4,8 @@ require 'kramdown'
|
|
4
4
|
require 'rouge'
|
5
5
|
require 'kramdown-parser-gfm'
|
6
6
|
require 'yaml'
|
7
|
+
require 'rb-inotify'
|
8
|
+
require 'papercraft'
|
7
9
|
|
8
10
|
require_relative './errors'
|
9
11
|
|
@@ -18,8 +20,53 @@ module Impression
|
|
18
20
|
def initialize(base_path, opts = {})
|
19
21
|
@base_path = base_path
|
20
22
|
@opts = opts
|
23
|
+
@opts[:pages] = self
|
21
24
|
|
22
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'
|
23
70
|
end
|
24
71
|
|
25
72
|
def load
|
@@ -31,87 +78,159 @@ module Impression
|
|
31
78
|
next unless File.file?(full_path)
|
32
79
|
|
33
80
|
page = Page.new(path, full_path, @opts)
|
34
|
-
@map[page.
|
81
|
+
@map[page.permalink] = { page: page, full_path: full_path }
|
35
82
|
end
|
36
83
|
@map['/'] = @map['/index']
|
37
84
|
end
|
38
85
|
alias_method :reload, :load
|
39
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
|
+
|
40
107
|
def load_file(path)
|
41
108
|
content = IO.read(path)
|
42
109
|
end
|
43
110
|
|
44
111
|
def serve(req)
|
45
|
-
|
46
|
-
raise NotFoundError unless
|
112
|
+
entry = @map[req.route_relative_path]
|
113
|
+
raise NotFoundError unless entry
|
47
114
|
|
48
|
-
|
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
|
49
121
|
|
50
|
-
|
51
|
-
|
122
|
+
def render_page(page)
|
123
|
+
layout_proc(page.layout).().render(pages: self, page: page)
|
52
124
|
end
|
53
125
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
@opts = opts
|
59
|
-
read_page
|
60
|
-
end
|
61
|
-
|
62
|
-
PAGE_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m.freeze
|
63
|
-
|
64
|
-
def read_page
|
65
|
-
data = IO.read(@full_path) || ''
|
66
|
-
if data =~ PAGE_REGEXP
|
67
|
-
front_matter = Regexp.last_match(1)
|
68
|
-
@attributes = YAML.load(front_matter)
|
69
|
-
@content = Regexp.last_match.post_match
|
70
|
-
else
|
71
|
-
@attributes = {}
|
72
|
-
@content = data
|
73
|
-
end
|
74
|
-
end
|
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
|
75
130
|
|
76
|
-
|
77
|
-
|
131
|
+
def select(selector)
|
132
|
+
@map.inject([]) do |array, (permalink, entry)|
|
133
|
+
array << entry[:page] if permalink =~ selector
|
134
|
+
array
|
78
135
|
end
|
136
|
+
end
|
137
|
+
end
|
79
138
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
def title_from_content
|
91
|
-
(@content =~ TITLE_REGEXP) && Regexp.last_match(1)
|
92
|
-
end
|
93
|
-
|
94
|
-
def status
|
95
|
-
@attributes['status'] || Qeweney::Status::OK
|
96
|
-
end
|
97
|
-
|
98
|
-
def to_html
|
99
|
-
@html ||= render_html
|
100
|
-
end
|
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
|
101
149
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
108
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
|
109
201
|
|
110
|
-
|
111
|
-
|
112
|
-
|
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}"
|
113
214
|
end
|
114
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
|
115
234
|
end
|
116
235
|
end
|
117
236
|
|
@@ -2,8 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'qeweney'
|
4
4
|
|
5
|
-
require_relative './pages'
|
5
|
+
# require_relative './pages'
|
6
|
+
require_relative './request_routing'
|
6
7
|
|
8
|
+
# Extensions to `Qeweney::Request`
|
7
9
|
class Qeweney::Request
|
8
|
-
|
10
|
+
|
11
|
+
# include Impression::Pages::RequestMethods
|
12
|
+
include Impression::RequestRouting
|
9
13
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Impression
|
6
|
+
|
7
|
+
# Request routing extensions for `Qeweney::Request`
|
8
|
+
module RequestRouting
|
9
|
+
|
10
|
+
# Matches a route regexp against the relative request path. The relative
|
11
|
+
# request path is a separate string (stored in `@resource_relative_path`)
|
12
|
+
# that is updated as routes are matched against it. The `route_regexp`
|
13
|
+
# should be either `nil` for root routes (`/`) or a Regexp of the form
|
14
|
+
# `/^#{route}(\/.*)?$/`. See also `Resource#initialize`.
|
15
|
+
#
|
16
|
+
# @param route_regexp [Regexp, nil] Route regexp to match against
|
17
|
+
# @return [String, nil] The remainder of the path (relative to the route)
|
18
|
+
def match_resource_path?(route_regexp)
|
19
|
+
@resource_relative_path ||= path.dup
|
20
|
+
|
21
|
+
return @resource_relative_path unless route_regexp
|
22
|
+
|
23
|
+
# Simplified logic: no match returns nil, otherwise we set the relative path for
|
24
|
+
@resource_relative_path = match_resource_relative_path(
|
25
|
+
@resource_relative_path, route_regexp
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns the relative_path for the latest matched resource
|
30
|
+
#
|
31
|
+
# @return [String]
|
32
|
+
def resource_relative_path
|
33
|
+
@resource_relative_path ||= path.dup
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Matches the given path against the given route regexp. If the path matches
|
39
|
+
# the regexp, the relative path for the given route is returned. Otherwise,
|
40
|
+
# this method returns `nil`.
|
41
|
+
#
|
42
|
+
# @param path [String] path to match
|
43
|
+
# @param route_regexp [Regexp] route regexp to match against
|
44
|
+
# @return [String, nil] the relative path for the given route, or nil if no match.
|
45
|
+
def match_resource_relative_path(path, route_regexp)
|
46
|
+
match = path.match(route_regexp)
|
47
|
+
match && (match[1] || '/')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'qeweney'
|
5
|
+
|
6
|
+
module Impression
|
7
|
+
|
8
|
+
# The `Resource` class represents an abstract web resource. Resources are
|
9
|
+
# organized in tree like structure, with the structure normally corresponding
|
10
|
+
# to the URL path hierarchy. Other ways of organising resources, according to
|
11
|
+
# other taxonomies, can be implemented as long as the resources implement the
|
12
|
+
# same interface as the `Resource` class, which includes the following
|
13
|
+
# methods:
|
14
|
+
#
|
15
|
+
# - `Resource#route` - returns the resource which should respond to the
|
16
|
+
# request.
|
17
|
+
# - `Resource#respond` - responds to the request.
|
18
|
+
#
|
19
|
+
class Resource
|
20
|
+
# Reference to the parent resource
|
21
|
+
attr_reader :parent
|
22
|
+
|
23
|
+
# The resource's path relative to its parent
|
24
|
+
attr_reader :path
|
25
|
+
|
26
|
+
# A hash mapping relative paths to child resources
|
27
|
+
attr_reader :children
|
28
|
+
|
29
|
+
# Initalizes a new resource instance.
|
30
|
+
#
|
31
|
+
# @param parent [Impression::Resource, nil] the parent resource (or nil)
|
32
|
+
# @param path [String] the resource's relative path
|
33
|
+
# @return [void]
|
34
|
+
def initialize(parent: nil, path:)
|
35
|
+
@parent = parent
|
36
|
+
@path = normalize_route_path(path)
|
37
|
+
@route_regexp = @path == '/' ? nil : /^#{@path}(\/.*)?$/.freeze
|
38
|
+
@children = {}
|
39
|
+
|
40
|
+
@parent&.add_child(self)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the resource's absolute path, according to its location in the
|
44
|
+
# resource hierarchy.
|
45
|
+
#
|
46
|
+
# @return [String] absolute path
|
47
|
+
def absolute_path
|
48
|
+
@absoulte_path ||= File.join(@parent ? @parent.absolute_path : '/', @path)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Iterates over the resource and any of its sub-resources, passing each to
|
52
|
+
# the given block.
|
53
|
+
#
|
54
|
+
# @return [Impression::Resource] self
|
55
|
+
def each(&block)
|
56
|
+
block.(self)
|
57
|
+
@children.values.each { |c| c.each(&block) }
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
# Adds a child reference to the children map.
|
62
|
+
#
|
63
|
+
# @param child [Impression::Resource] child resource
|
64
|
+
# @return [Impression::Resource] self
|
65
|
+
def add_child(child)
|
66
|
+
@children[child.path] = child
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
# Responds to the given request by rendering a 404 Not found response.
|
71
|
+
#
|
72
|
+
# @param req [Qeweney::Request] request
|
73
|
+
# @return [void]
|
74
|
+
def respond(req)
|
75
|
+
req.respond(nil, ':status' => Qeweney::Status::NOT_FOUND)
|
76
|
+
end
|
77
|
+
|
78
|
+
FIRST_PATH_SEGMENT_REGEXP = /^(\/[^\/]+)\//.freeze
|
79
|
+
|
80
|
+
# Routes the request by matching self and any children against the request
|
81
|
+
# path, returning the target resource, or nil if there's no match.
|
82
|
+
#
|
83
|
+
# @param req [Qeweney::Request] request
|
84
|
+
# @return [Impression::Resource, nil] target resource
|
85
|
+
def route(req)
|
86
|
+
case (relative_path = req.match_resource_path?(@route_regexp))
|
87
|
+
when nil
|
88
|
+
return nil
|
89
|
+
when '/'
|
90
|
+
return self
|
91
|
+
else
|
92
|
+
# naive case
|
93
|
+
child = @children[relative_path]
|
94
|
+
return child.route(req) if child
|
95
|
+
|
96
|
+
if (m = relative_path.match(FIRST_PATH_SEGMENT_REGEXP))
|
97
|
+
child = @children[m[1]]
|
98
|
+
return child.route(req) if child
|
99
|
+
end
|
100
|
+
|
101
|
+
return self
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Renders the resource and all of its sub-resources to static files.
|
106
|
+
#
|
107
|
+
# @param base_path [String] base path of target directory
|
108
|
+
# @return [Impression::Resource] self
|
109
|
+
def render_tree_to_static_files(base_path)
|
110
|
+
each do |r|
|
111
|
+
path = File.join(base_path, r.relative_static_file_path)
|
112
|
+
dir = File.dirname(path)
|
113
|
+
FileUtils.mkdir_p(dir) if !File.directory?(dir)
|
114
|
+
File.open(path, 'w') { |f| r.render_to_file(f) }
|
115
|
+
end
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
# Converts the resource to a Proc, for use as a Qeweney app.
|
120
|
+
#
|
121
|
+
# @return [Proc] web app proc
|
122
|
+
def to_proc
|
123
|
+
->(req) do
|
124
|
+
resource = route(req) || self
|
125
|
+
resource.respond(req)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
SLASH_PREFIXED_PATH_REGEXP = /^\//.freeze
|
132
|
+
|
133
|
+
# Normalizes the given path by ensuring it starts with a slash.
|
134
|
+
#
|
135
|
+
# @param path [String] path to normalize
|
136
|
+
# @return [String] normalized path
|
137
|
+
def normalize_route_path(path)
|
138
|
+
path =~ SLASH_PREFIXED_PATH_REGEXP ? path : "/#{path}"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/lib/impression/version.rb
CHANGED
data/lib/impression.rb
CHANGED
@@ -1,3 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'polyphony'
|
4
|
+
|
3
5
|
require_relative './impression/request_extensions'
|
6
|
+
# require_relative './impression/file_watcher'
|
7
|
+
|
8
|
+
require_relative './impression/resource'
|
9
|
+
require_relative './impression/file_tree'
|
10
|
+
require_relative './impression/app'
|
data/test/helper.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require_relative './coverage' if ENV['COVERAGE']
|
5
|
+
require 'minitest/autorun'
|
6
|
+
require 'impression'
|
7
|
+
require 'qeweney/test_adapter'
|
8
|
+
|
9
|
+
module Kernel
|
10
|
+
def mock_req(**args)
|
11
|
+
Qeweney::TestAdapter.mock(**args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def capture_exception
|
15
|
+
yield
|
16
|
+
rescue Exception => e
|
17
|
+
e
|
18
|
+
end
|
19
|
+
|
20
|
+
def trace(*args)
|
21
|
+
STDOUT.orig_write(format_trace(args))
|
22
|
+
end
|
23
|
+
|
24
|
+
def format_trace(args)
|
25
|
+
if args.first.is_a?(String)
|
26
|
+
if args.size > 1
|
27
|
+
format("%s: %p\n", args.shift, args)
|
28
|
+
else
|
29
|
+
format("%s\n", args.first)
|
30
|
+
end
|
31
|
+
else
|
32
|
+
format("%p\n", args.size == 1 ? args.first : args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
module Minitest::Assertions
|
38
|
+
def assert_in_range exp_range, act
|
39
|
+
msg = message(msg) { "Expected #{mu_pp(act)} to be in range #{mu_pp(exp_range)}" }
|
40
|
+
assert exp_range.include?(act), msg
|
41
|
+
end
|
42
|
+
|
43
|
+
def assert_response exp_body, exp_content_type, req
|
44
|
+
msg = message(msg) { "Expected response body #{mu_pp(act)} to equal #{mu_pp(exp_body)}" }
|
45
|
+
assert_equal exp_body, req.response_body, msg
|
46
|
+
|
47
|
+
return unless exp_content_type
|
48
|
+
|
49
|
+
if Symbol === exp_content_type
|
50
|
+
exp_content_type = Qeweney::MimeTypes[exp_content_type]
|
51
|
+
end
|
52
|
+
msg = message(msg) { "Expected response content type #{mu_pp(act)} to equal #{mu_pp(exp_body)}" }
|
53
|
+
assert_equal exp_content_type, req.response_content_type, msg
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Impression::Resource
|
58
|
+
def route_and_respond(req)
|
59
|
+
route(req).respond(req)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class PathRenderingResource < Impression::Resource
|
64
|
+
def respond(req)
|
65
|
+
req.respond(absolute_path)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class CompletePathInfoRenderingResource < Impression::Resource
|
70
|
+
def respond(req)
|
71
|
+
req.respond("#{absolute_path} #{req.resource_relative_path}")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Extensions to be used in conjunction with `Qeweney::TestAdapter`
|
76
|
+
class Qeweney::Request
|
77
|
+
def response_headers
|
78
|
+
adapter.headers
|
79
|
+
end
|
80
|
+
|
81
|
+
def response_body
|
82
|
+
adapter.body
|
83
|
+
end
|
84
|
+
|
85
|
+
def response_status
|
86
|
+
adapter.status
|
87
|
+
end
|
88
|
+
|
89
|
+
def response_content_type
|
90
|
+
response_headers['Content-Type']
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
puts "Polyphony backend: #{Thread.current.backend.kind}"
|
data/test/run.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
<h1>BarIndex</h1>
|
@@ -0,0 +1 @@
|
|
1
|
+
<h1>Foo</h1>
|
@@ -0,0 +1 @@
|
|
1
|
+
<h1>Index</h1>
|
data/test/static/js/a.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
console.log(42);
|
data/test/test_app.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
class AppTest < MiniTest::Test
|
6
|
+
def test_empty_app
|
7
|
+
app = Impression::App.new(path: '/')
|
8
|
+
req = mock_req(':method' => 'GET', ':path' => '/')
|
9
|
+
|
10
|
+
app.respond(req)
|
11
|
+
assert_equal Qeweney::Status::NOT_FOUND, req.adapter.status
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_app_each
|
15
|
+
app = Impression::App.new(path: '/')
|
16
|
+
|
17
|
+
buffer = []
|
18
|
+
app.each { |r| buffer << r }
|
19
|
+
assert_equal [app], buffer
|
20
|
+
|
21
|
+
foo = PathRenderingResource.new(parent: app, path: 'foo')
|
22
|
+
bar = PathRenderingResource.new(parent: app, path: 'bar')
|
23
|
+
|
24
|
+
buffer = []
|
25
|
+
app.each { |r| buffer << r }
|
26
|
+
assert_equal [app, foo, bar], buffer
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_app_to_proc
|
30
|
+
app = Impression::App.new(path: '/')
|
31
|
+
app_proc = app.to_proc
|
32
|
+
|
33
|
+
foo = PathRenderingResource.new(parent: app, path: 'foo')
|
34
|
+
bar = PathRenderingResource.new(parent: app, path: 'bar')
|
35
|
+
|
36
|
+
# req = mock_req(':method' => 'GET', ':path' => '/')
|
37
|
+
# app_proc.(req)
|
38
|
+
# assert_equal Qeweney::Status::NOT_FOUND, req.adapter.status
|
39
|
+
|
40
|
+
req = mock_req(':method' => 'GET', ':path' => '/foo')
|
41
|
+
app_proc.(req)
|
42
|
+
assert_equal '/foo', req.adapter.body
|
43
|
+
|
44
|
+
req = mock_req(':method' => 'GET', ':path' => '/bar')
|
45
|
+
app_proc.(req)
|
46
|
+
assert_equal '/bar', req.adapter.body
|
47
|
+
|
48
|
+
req = mock_req(':method' => 'GET', ':path' => '/baz')
|
49
|
+
app_proc.(req)
|
50
|
+
assert_equal Qeweney::Status::NOT_FOUND, req.adapter.status
|
51
|
+
end
|
52
|
+
end
|