roda 3.25.0 → 3.26.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|