roda 3.25.0 → 3.26.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 +6 -0
- data/doc/release_notes/3.26.0.txt +15 -0
- data/lib/roda/plugins/assets.rb +1 -1
- data/lib/roda/plugins/streaming.rb +51 -1
- data/lib/roda/version.rb +1 -1
- data/spec/plugin/assets_spec.rb +15 -0
- data/spec/plugin/streaming_spec.rb +113 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 441b09ae88ce4427866fe7c773ce8476f60e837b7b4477bfc5106c162e8b0a06
|
4
|
+
data.tar.gz: aca50917cc14429a77c16461352c997dff592e52cc9a44d1f4f64688b8b50693
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb56cb6dbcb70fbc182d10bf161db009162d6887e9117823b9fab26554670644c3eec1c2e2977008310826fee799d1213ff8d7a85ed261e5f257ed949b9baa2b
|
7
|
+
data.tar.gz: 8d7b4ee8b669e7a3a5f3f4ac79f7d51b879cf9700e9634bd5b52b9c219c1158d0f07f03ce5aeab983bbddd3038febd996738b45047fa1e8e49c6e1dc7e07687d
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
= 3.26.0 (2019-11-18)
|
2
|
+
|
3
|
+
* Combine multiple asset files with a newline when compiling them, avoiding corner cases with comments (ameuret) (#176)
|
4
|
+
|
5
|
+
* Add asychronous streaming support to the streaming plugin (janko) (#175)
|
6
|
+
|
1
7
|
= 3.25.0 (2019-10-15)
|
2
8
|
|
3
9
|
* Support change in tilt 2.0.10 private API to continue to support compiled templates, with up to 33% performance improvement (jeremyevans)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Asynchronous streaming is now supported in the streaming plugin,
|
4
|
+
using the :async option. When using this option, streaming
|
5
|
+
responses are temporarily buffered in a queue. By default, the
|
6
|
+
queue is a sized queue with a maximum of 10 elements, but the
|
7
|
+
queue can be specified manually via the :queue option, which
|
8
|
+
can be used with async libraries that support non-blocking
|
9
|
+
queues. This option is currently only supported on Ruby 2.3+.
|
10
|
+
|
11
|
+
= Other Improvements
|
12
|
+
|
13
|
+
* When combining multiple compiled assets into a single file, the
|
14
|
+
files are now separated by a newline, fixing issues when a
|
15
|
+
single line comment is used as the last line of a file.
|
data/lib/roda/plugins/assets.rb
CHANGED
@@ -494,7 +494,7 @@ class Roda
|
|
494
494
|
file = "#{dirs.join('/')}/#{file}" if dirs && o[:group_subdirs]
|
495
495
|
file = "#{o[:"#{type}_path"]}#{file}"
|
496
496
|
app.read_asset_file(file, type)
|
497
|
-
end.join
|
497
|
+
end.join("\n")
|
498
498
|
|
499
499
|
unless o[:concat_only]
|
500
500
|
content = compress_asset(content, type)
|
@@ -21,6 +21,8 @@ class Roda
|
|
21
21
|
#
|
22
22
|
# :callback :: A callback proc to call when the connection is closed.
|
23
23
|
# :loop :: Whether to call the stream block continuously until the connection is closed.
|
24
|
+
# :async :: Whether to call the stream block in a separate thread (default: false). Only supported on Ruby 2.3+.
|
25
|
+
# :queue :: A queue object to use for asynchronous streaming (default: `SizedQueue.new(10)`).
|
24
26
|
#
|
25
27
|
# If the :loop option is used, you can override the
|
26
28
|
# handle_stream_error method to change how exceptions
|
@@ -88,6 +90,52 @@ class Roda
|
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
93
|
+
# Class of the response body if you use #stream with :async set to true.
|
94
|
+
# Uses a separate thread that pushes streaming results to a queue, so that
|
95
|
+
# data can be streamed to clients while it is being prepared by the application.
|
96
|
+
class AsyncStream
|
97
|
+
include Enumerable
|
98
|
+
|
99
|
+
# Handle streaming options, see Streaming for details.
|
100
|
+
def initialize(opts=OPTS, &block)
|
101
|
+
@stream = Stream.new(opts, &block)
|
102
|
+
@queue = opts[:queue] || SizedQueue.new(10) # have some default backpressure
|
103
|
+
@thread = Thread.new { enqueue_chunks }
|
104
|
+
end
|
105
|
+
|
106
|
+
# Continue streaming data until the stream is finished.
|
107
|
+
def each(&out)
|
108
|
+
dequeue_chunks(&out)
|
109
|
+
@thread.join
|
110
|
+
end
|
111
|
+
|
112
|
+
# Stop streaming.
|
113
|
+
def close
|
114
|
+
@queue.close # terminate the producer thread
|
115
|
+
@stream.close
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
# Push each streaming chunk onto the queue.
|
121
|
+
def enqueue_chunks
|
122
|
+
@stream.each do |chunk|
|
123
|
+
@queue.push(chunk)
|
124
|
+
end
|
125
|
+
rescue ClosedQueueError
|
126
|
+
# connection was closed
|
127
|
+
ensure
|
128
|
+
@queue.close
|
129
|
+
end
|
130
|
+
|
131
|
+
# Pop each streaming chunk from the queue and yield it.
|
132
|
+
def dequeue_chunks
|
133
|
+
while chunk = @queue.pop
|
134
|
+
yield chunk
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
91
139
|
module InstanceMethods
|
92
140
|
# Immediately return a streaming response using the current response
|
93
141
|
# status and headers, calling the block to get the streaming response.
|
@@ -105,7 +153,9 @@ class Roda
|
|
105
153
|
end
|
106
154
|
end
|
107
155
|
|
108
|
-
|
156
|
+
stream_class = (opts[:async] && RUBY_VERSION >= '2.3') ? AsyncStream : Stream
|
157
|
+
|
158
|
+
throw :halt, @_response.finish_with_body(stream_class.new(opts, &block))
|
109
159
|
end
|
110
160
|
|
111
161
|
# Handle exceptions raised while streaming when using :loop
|
data/lib/roda/version.rb
CHANGED
data/spec/plugin/assets_spec.rb
CHANGED
@@ -333,6 +333,21 @@ if run_tests
|
|
333
333
|
end
|
334
334
|
|
335
335
|
it 'should handle compiling assets, linking to them, and accepting requests for them' do
|
336
|
+
app.plugin :assets, :js=>{:head => %w'comment_1.js comment_2.js'}
|
337
|
+
app.compile_assets
|
338
|
+
html = body('/test')
|
339
|
+
html.scan(/<script/).length.must_equal 1
|
340
|
+
html =~ %r{src="(/assets/app\.head\.[a-f0-9]{64}\.js)"}
|
341
|
+
js = body($1)
|
342
|
+
js.must_equal <<END
|
343
|
+
// test
|
344
|
+
/*
|
345
|
+
a = 1;
|
346
|
+
*/
|
347
|
+
END
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'should separate compiled assets with new lines' do
|
336
351
|
app.compile_assets
|
337
352
|
html = body('/test')
|
338
353
|
html.scan(/<link/).length.must_equal 1
|
@@ -130,4 +130,117 @@ describe "streaming plugin" do
|
|
130
130
|
b.each{|v| a << v}
|
131
131
|
a.must_equal %w'a 1 b 1 c 1 e'
|
132
132
|
end
|
133
|
+
|
134
|
+
describe "with :async" do
|
135
|
+
it "should stream in a thread" do
|
136
|
+
main_thread = Thread.current
|
137
|
+
minitest = self
|
138
|
+
app(:streaming) do |r|
|
139
|
+
stream(:async=>true) do |out|
|
140
|
+
minitest.refute_equal Thread.current, main_thread
|
141
|
+
%w'a b c'.each do |v|
|
142
|
+
out << v
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
s, h, b = req
|
148
|
+
s.must_equal 200
|
149
|
+
h.must_equal('Content-Type'=>'text/html')
|
150
|
+
b.to_a.must_equal %w'a b c'
|
151
|
+
end
|
152
|
+
|
153
|
+
it "should propagate exceptions" do
|
154
|
+
app(:streaming) do |r|
|
155
|
+
stream(:async=>true) do |out|
|
156
|
+
Thread.current.report_on_exception = false if Thread.current.respond_to?(:report_on_exception=)
|
157
|
+
%w'a b'.each{|v| out << v}
|
158
|
+
raise Roda::RodaError, 'foo'
|
159
|
+
out << 'c'
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
s, h, b = req
|
164
|
+
s.must_equal 200
|
165
|
+
h.must_equal('Content-Type'=>'text/html')
|
166
|
+
a = []
|
167
|
+
proc{b.each{|v| a << v}}.must_raise(Roda::RodaError)
|
168
|
+
a.must_equal %w'a b'
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should terminate the thread on close" do
|
172
|
+
q = Queue.new
|
173
|
+
app(:streaming) do |r|
|
174
|
+
stream(:async=>true) do |out|
|
175
|
+
%w'a b c d e f g h i j'.each{|v| out << v}
|
176
|
+
q.deq
|
177
|
+
out << 'k'
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
*, b = req
|
182
|
+
e = b.enum_for(:each)
|
183
|
+
10.times{e.next}
|
184
|
+
b.close
|
185
|
+
q.enq 'x'
|
186
|
+
proc{e.next}.must_raise(StopIteration)
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should still run callbacks on close" do
|
190
|
+
callback = false
|
191
|
+
app(:streaming) do |r|
|
192
|
+
stream(:async=>true, :callback=>proc{callback = true}) do |out|
|
193
|
+
%w'a b c'.each{|v| out << v}
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
*, b = req
|
198
|
+
b.close
|
199
|
+
callback.must_equal true
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should apply backpressure by default" do
|
203
|
+
q = Queue.new
|
204
|
+
a = []
|
205
|
+
app(:streaming) do |r|
|
206
|
+
stream(:async=>true) do |out|
|
207
|
+
%w'a b c d e f g h i j'.each do |v|
|
208
|
+
out << v
|
209
|
+
a << v
|
210
|
+
end
|
211
|
+
|
212
|
+
q.enq 'x'
|
213
|
+
|
214
|
+
out << 'k'
|
215
|
+
a << 'k'
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
*, b = req
|
220
|
+
q.deq
|
221
|
+
a.must_equal %w'a b c d e f g h i j'
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should handle :queue option to override queue" do
|
225
|
+
q = Queue.new
|
226
|
+
a = []
|
227
|
+
app(:streaming) do |r|
|
228
|
+
stream(:async=>true, queue: SizedQueue.new(5)) do |out|
|
229
|
+
%w'a b c d e'.each do |v|
|
230
|
+
out << v
|
231
|
+
a << v
|
232
|
+
end
|
233
|
+
|
234
|
+
q.enq 'x'
|
235
|
+
|
236
|
+
out << 'f'
|
237
|
+
a << 'f'
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
*, b = req
|
242
|
+
q.deq
|
243
|
+
a.must_equal %w'a b c d e'
|
244
|
+
end
|
245
|
+
end if RUBY_VERSION >= '2.3'
|
133
246
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roda
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.26.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -236,6 +236,7 @@ extra_rdoc_files:
|
|
236
236
|
- doc/release_notes/3.23.0.txt
|
237
237
|
- doc/release_notes/3.24.0.txt
|
238
238
|
- doc/release_notes/3.25.0.txt
|
239
|
+
- doc/release_notes/3.26.0.txt
|
239
240
|
files:
|
240
241
|
- CHANGELOG
|
241
242
|
- MIT-LICENSE
|
@@ -297,6 +298,7 @@ files:
|
|
297
298
|
- doc/release_notes/3.23.0.txt
|
298
299
|
- doc/release_notes/3.24.0.txt
|
299
300
|
- doc/release_notes/3.25.0.txt
|
301
|
+
- doc/release_notes/3.26.0.txt
|
300
302
|
- doc/release_notes/3.3.0.txt
|
301
303
|
- doc/release_notes/3.4.0.txt
|
302
304
|
- doc/release_notes/3.5.0.txt
|