roda 3.5.0 → 3.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +8 -0
- data/doc/release_notes/3.6.0.txt +21 -0
- data/lib/roda/plugins/assets.rb +12 -1
- data/lib/roda/plugins/early_hints.rb +26 -0
- data/lib/roda/plugins/json_parser.rb +21 -1
- data/lib/roda/version.rb +1 -1
- data/spec/plugin/assets_spec.rb +21 -0
- data/spec/plugin/early_hints_spec.rb +19 -0
- data/spec/plugin/json_parser_spec.rb +45 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f461d51ffcff3abee852c79773a13d87f2ffc5fea2c6a27346bfb2755b318e1e
|
4
|
+
data.tar.gz: c30fafc14125ef2b100bce74762193cb0f09776805732c284b7d7cd0472f07c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 428d1c607d27a5f90b4c5c992e8f8f3ea33cf8dd7c49e2a0920e49e4b97de34532eb9177aae04ad1efa9a229d747a6e5a744334ae803edbaf60e1e51cf154552
|
7
|
+
data.tar.gz: dd85d32aedb8b2c09fc757bd1cbc0f92dce99849841e4ab2e78f47589de8670c87023eb9d6a144efd1c7ff06f08f705589e2510cb7c4383cba13b40020b2d489
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
= 3.6.0 (2018-03-26)
|
2
|
+
|
3
|
+
* Add :wrap option to json_parser plugin, for whether/how to wrap the uploaded JSON object (jeremyevans) (#142)
|
4
|
+
|
5
|
+
* Add :early_hints option to the assets plugin, for supporting sending early hints for calls to assets (jeremyevans)
|
6
|
+
|
7
|
+
* Add early_hints plugin for sending 103 Early Hint responses, currently only working on puma (jeremyevans)
|
8
|
+
|
1
9
|
= 3.5.0 (2018-02-14)
|
2
10
|
|
3
11
|
* Add request_aref plugin for configuring behavior of request [] and []= methods (jeremyevans)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* An early_hints plugin has been added for senting 103 Early Hint
|
4
|
+
responses. This is currently only supported on puma 3.11+, and
|
5
|
+
can allow for improved performance by letting the requestor know
|
6
|
+
which related files will be needed by the request.
|
7
|
+
|
8
|
+
* An :early_hints option has been added to the assets plugin. If
|
9
|
+
given, calling the assets method will also issue an early hint
|
10
|
+
for the related assets.
|
11
|
+
|
12
|
+
* A :wrap option has been added to the json_parser plugin. If set
|
13
|
+
to :always, all uploaded json data will be stored using a hash
|
14
|
+
with a "_json" key. If set to :unless_hash, uploaded json data
|
15
|
+
will only be wrapped in such a matter if it is not already a hash.
|
16
|
+
|
17
|
+
Using the :wrap option can fix problems when using r.params when
|
18
|
+
the uploaded JSON data is an array and not a hash. However, it
|
19
|
+
does change the behavior of r.POST. It is possible to handle
|
20
|
+
uploaded JSON array data without the :wrap option by using r.GET
|
21
|
+
and r.POST directly instead of using r.params.
|
data/lib/roda/plugins/assets.rb
CHANGED
@@ -267,6 +267,7 @@ class Roda
|
|
267
267
|
# :dependencies :: A hash of dependencies for your asset files. Keys should be paths to asset files,
|
268
268
|
# values should be arrays of paths your asset files depends on. This is used to
|
269
269
|
# detect changes in your asset files.
|
270
|
+
# :early_hints :: Automatically send early hints for all assets. Requires the early_hints plugin.
|
270
271
|
# :group_subdirs :: Whether a hash used in :css and :js options requires the assets for the
|
271
272
|
# related group are contained in a subdirectory with the same name (default: true)
|
272
273
|
# :gzip :: Store gzipped compiled assets files, and serve those to clients who accept gzip encoding.
|
@@ -307,6 +308,7 @@ class Roda
|
|
307
308
|
:concat_only => false,
|
308
309
|
:compiled => false,
|
309
310
|
:add_suffix => false,
|
311
|
+
:early_hints => false,
|
310
312
|
:timestamp_paths => false,
|
311
313
|
:group_subdirs => true,
|
312
314
|
:compiled_css_dir => nil,
|
@@ -369,6 +371,10 @@ class Roda
|
|
369
371
|
opts[:compiled] = ::JSON.parse(::File.read(opts[:precompiled]))
|
370
372
|
end
|
371
373
|
|
374
|
+
if opts[:early_hints]
|
375
|
+
app.plugin :early_hints
|
376
|
+
end
|
377
|
+
|
372
378
|
DEFAULTS.each do |k, v|
|
373
379
|
opts[k] = v unless opts.has_key?(k)
|
374
380
|
end
|
@@ -661,7 +667,12 @@ class Roda
|
|
661
667
|
tag_end = "\" />"
|
662
668
|
end
|
663
669
|
|
664
|
-
assets_paths(type)
|
670
|
+
paths = assets_paths(type)
|
671
|
+
if o[:early_hints]
|
672
|
+
early_hint_as = ltype == :js ? 'script' : 'style'
|
673
|
+
send_early_hints('Link'=>paths.map{|p| "<#{p}>; rel=preload; as=#{early_hint_as}"}.join("\n"))
|
674
|
+
end
|
675
|
+
paths.map{|p| "#{tag_start}#{h(p)}#{tag_end}"}.join("\n")
|
665
676
|
end
|
666
677
|
|
667
678
|
# Render the asset with the given filename. When assets are compiled,
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
class Roda
|
5
|
+
module RodaPlugins
|
6
|
+
# The early_hints plugin allows sending 103 Early Hints responses
|
7
|
+
# using the rack.early_hints environment variable. Currently, this
|
8
|
+
# is only supported by puma 3.11+, and on other servers this is a no-op.
|
9
|
+
# Early hints allow clients to preload necessary files before receiving
|
10
|
+
# the response.
|
11
|
+
module EarlyHints
|
12
|
+
module InstanceMethods
|
13
|
+
# Send given hash of Early Hints using the rack.early_hints environment variable,
|
14
|
+
# currenly only supported by puma. hash given should generally have the single
|
15
|
+
# key 'Link', and a string or array of strings for each of the early hints.
|
16
|
+
def send_early_hints(hash)
|
17
|
+
if eh_proc = env['rack.early_hints']
|
18
|
+
eh_proc.call(hash)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
register_plugin(:early_hints, EarlyHints)
|
25
|
+
end
|
26
|
+
end
|
@@ -25,10 +25,24 @@ class Roda
|
|
25
25
|
# :include_request :: If true, the parser will be called with the request
|
26
26
|
# object as the second argument, so the parser needs
|
27
27
|
# to respond to +call(str, request)+.
|
28
|
+
# :wrap :: Whether to wrap uploaded JSON data in a hash with a "_json"
|
29
|
+
# key. Without this, calls to r.params will fail if a non-Hash
|
30
|
+
# (such as an array) is uploaded in JSON format. A value of
|
31
|
+
# :always will wrap all values, and a value of :unless_hash will
|
32
|
+
# only wrap values that are not already hashes.
|
28
33
|
def self.configure(app, opts=OPTS)
|
29
34
|
app.opts[:json_parser_error_handler] = opts[:error_handler] || app.opts[:json_parser_error_handler] || DEFAULT_ERROR_HANDLER
|
30
35
|
app.opts[:json_parser_parser] = opts[:parser] || app.opts[:json_parser_parser] || DEFAULT_PARSER
|
31
36
|
app.opts[:json_parser_include_request] = opts[:include_request] if opts.has_key?(:include_request)
|
37
|
+
|
38
|
+
case opts[:wrap]
|
39
|
+
when :unless_hash, :always
|
40
|
+
app.opts[:json_parser_wrap] = opts[:wrap]
|
41
|
+
when nil
|
42
|
+
# Nothing
|
43
|
+
else
|
44
|
+
raise RodaError, "unsupported option value for json_parser plugin :wrap option: #{opts[:wrap].inspect} (should be :unless_hash or :always)"
|
45
|
+
end
|
32
46
|
end
|
33
47
|
|
34
48
|
module RequestMethods
|
@@ -43,10 +57,16 @@ class Roda
|
|
43
57
|
input.rewind
|
44
58
|
return super if str.empty?
|
45
59
|
begin
|
46
|
-
json_params =
|
60
|
+
json_params = parse_json(str)
|
47
61
|
rescue
|
48
62
|
roda_class.opts[:json_parser_error_handler].call(self)
|
49
63
|
end
|
64
|
+
|
65
|
+
wrap = roda_class.opts[:json_parser_wrap]
|
66
|
+
if wrap == :always || (wrap == :unless_hash && !json_params.is_a?(Hash))
|
67
|
+
json_params = {"_json"=>json_params}
|
68
|
+
end
|
69
|
+
env["roda.json_params"] = json_params
|
50
70
|
env["rack.request.form_input"] = input
|
51
71
|
json_params
|
52
72
|
else
|
data/lib/roda/version.rb
CHANGED
data/spec/plugin/assets_spec.rb
CHANGED
@@ -196,6 +196,27 @@ if run_tests
|
|
196
196
|
js.must_include('console.log')
|
197
197
|
end
|
198
198
|
|
199
|
+
it 'should handle early hints if the :early_hints option is used' do
|
200
|
+
app.plugin :assets, :early_hints=>true
|
201
|
+
eh = []
|
202
|
+
html = body('/test', 'rack.early_hints'=>proc{|h| eh << h})
|
203
|
+
eh.must_equal [
|
204
|
+
{"Link"=>"</assets/css/app.scss>; rel=preload; as=style\n</assets/css/raw.css>; rel=preload; as=style"},
|
205
|
+
{"Link"=>"</assets/js/head/app.js>; rel=preload; as=script"}
|
206
|
+
]
|
207
|
+
html.scan(/<link/).length.must_equal 2
|
208
|
+
html =~ %r{href="(/assets/css/app\.scss)"}
|
209
|
+
css = body($1)
|
210
|
+
html =~ %r{href="(/assets/css/raw\.css)"}
|
211
|
+
css2 = body($1)
|
212
|
+
html.scan(/<script/).length.must_equal 1
|
213
|
+
html =~ %r{src="(/assets/js/head/app\.js)"}
|
214
|
+
js = body($1)
|
215
|
+
css.must_match(/color: red;/)
|
216
|
+
css2.must_match(/color: blue;/)
|
217
|
+
js.must_include('console.log')
|
218
|
+
end
|
219
|
+
|
199
220
|
it 'should handle rendering assets, linking to them, and accepting requests for them when :add_script_name app option is used' do
|
200
221
|
app.opts[:add_script_name] = true
|
201
222
|
app.plugin :assets
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "../spec_helper"
|
2
|
+
|
3
|
+
describe "early_hints plugin" do
|
4
|
+
it "allows sending early hints to rack.early_hints" do
|
5
|
+
queue = []
|
6
|
+
app(:early_hints) do |r|
|
7
|
+
send_early_hints('Link'=>'</foo.js>; rel=preload; as=script')
|
8
|
+
queue << 'OK'
|
9
|
+
'OK'
|
10
|
+
end
|
11
|
+
|
12
|
+
body.must_equal 'OK'
|
13
|
+
queue.must_equal ['OK']
|
14
|
+
|
15
|
+
queue = []
|
16
|
+
body('rack.early_hints'=>proc{|h| queue << h}).must_equal 'OK'
|
17
|
+
queue.must_equal [{'Link'=>'</foo.js>; rel=preload; as=script'}, 'OK']
|
18
|
+
end
|
19
|
+
end
|
@@ -18,6 +18,12 @@ describe "json_parser plugin" do
|
|
18
18
|
it "returns 400 for invalid json" do
|
19
19
|
req('rack.input'=>StringIO.new('{"a":{"b":1}'), 'CONTENT_TYPE'=>'text/json', 'REQUEST_METHOD'=>'POST').must_equal [400, {}, []]
|
20
20
|
end
|
21
|
+
|
22
|
+
it "raises by default if r.params is called and a non-hash is submitted" do
|
23
|
+
proc do
|
24
|
+
req('rack.input'=>StringIO.new('[1]'), 'CONTENT_TYPE'=>'text/json', 'REQUEST_METHOD'=>'POST')
|
25
|
+
end.must_raise
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
29
|
describe "json_parser plugin" do
|
@@ -28,6 +34,45 @@ describe "json_parser plugin" do
|
|
28
34
|
body('rack.input'=>StringIO.new(''), 'CONTENT_TYPE'=>'text/json', 'REQUEST_METHOD'=>'POST').must_equal '0'
|
29
35
|
end
|
30
36
|
|
37
|
+
it "handles arrays and other non-hash values using r.POST" do
|
38
|
+
app(:json_parser) do |r|
|
39
|
+
r.POST.inspect
|
40
|
+
end
|
41
|
+
body('rack.input'=>StringIO.new('[ 1 ]'), 'CONTENT_TYPE'=>'text/json', 'REQUEST_METHOD'=>'POST').must_equal '[1]'
|
42
|
+
end
|
43
|
+
|
44
|
+
it "supports :wrap=>:always option" do
|
45
|
+
app(:bare) do
|
46
|
+
plugin(:json_parser, :wrap=>:always)
|
47
|
+
route do |r|
|
48
|
+
r.post 'a' do r.params['_json']['a']['b'].to_s end
|
49
|
+
r.params['_json'][1].to_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
body('/a', 'rack.input'=>StringIO.new('{"a":{"b":1}}'), 'CONTENT_TYPE'=>'text/json', 'REQUEST_METHOD'=>'POST').must_equal '1'
|
53
|
+
body('rack.input'=>StringIO.new('[true, 2]'), 'CONTENT_TYPE'=>'text/json', 'REQUEST_METHOD'=>'POST').must_equal '2'
|
54
|
+
end
|
55
|
+
|
56
|
+
it "supports :wrap=>:unless_hash option" do
|
57
|
+
app(:bare) do
|
58
|
+
plugin(:json_parser, :wrap=>:unless_hash)
|
59
|
+
route do |r|
|
60
|
+
r.post 'a' do r.params['a']['b'].to_s end
|
61
|
+
r.params['_json'][1].to_s
|
62
|
+
end
|
63
|
+
end
|
64
|
+
body('/a', 'rack.input'=>StringIO.new('{"a":{"b":1}}'), 'CONTENT_TYPE'=>'text/json', 'REQUEST_METHOD'=>'POST').must_equal '1'
|
65
|
+
body('rack.input'=>StringIO.new('[true, 2]'), 'CONTENT_TYPE'=>'text/json', 'REQUEST_METHOD'=>'POST').must_equal '2'
|
66
|
+
end
|
67
|
+
|
68
|
+
it "raises for unsupported :wrap option" do
|
69
|
+
proc do
|
70
|
+
app(:bare) do
|
71
|
+
plugin(:json_parser, :wrap=>:foo)
|
72
|
+
end
|
73
|
+
end.must_raise Roda::RodaError
|
74
|
+
end
|
75
|
+
|
31
76
|
it "supports :error_handler option" do
|
32
77
|
app(:bare) do
|
33
78
|
plugin(:json_parser, :error_handler=>proc{|r| r.halt [401, {}, ['bad']]})
|
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.6.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: 2018-
|
11
|
+
date: 2018-03-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -201,6 +201,7 @@ extra_rdoc_files:
|
|
201
201
|
- doc/release_notes/3.3.0.txt
|
202
202
|
- doc/release_notes/3.4.0.txt
|
203
203
|
- doc/release_notes/3.5.0.txt
|
204
|
+
- doc/release_notes/3.6.0.txt
|
204
205
|
files:
|
205
206
|
- CHANGELOG
|
206
207
|
- MIT-LICENSE
|
@@ -248,6 +249,7 @@ files:
|
|
248
249
|
- doc/release_notes/3.3.0.txt
|
249
250
|
- doc/release_notes/3.4.0.txt
|
250
251
|
- doc/release_notes/3.5.0.txt
|
252
|
+
- doc/release_notes/3.6.0.txt
|
251
253
|
- lib/roda.rb
|
252
254
|
- lib/roda/plugins/_symbol_regexp_matchers.rb
|
253
255
|
- lib/roda/plugins/all_verbs.rb
|
@@ -269,6 +271,7 @@ files:
|
|
269
271
|
- lib/roda/plugins/delete_empty_headers.rb
|
270
272
|
- lib/roda/plugins/disallow_file_uploads.rb
|
271
273
|
- lib/roda/plugins/drop_body.rb
|
274
|
+
- lib/roda/plugins/early_hints.rb
|
272
275
|
- lib/roda/plugins/empty_root.rb
|
273
276
|
- lib/roda/plugins/environments.rb
|
274
277
|
- lib/roda/plugins/error_email.rb
|
@@ -364,6 +367,7 @@ files:
|
|
364
367
|
- spec/plugin/delete_empty_headers_spec.rb
|
365
368
|
- spec/plugin/disallow_file_uploads_spec.rb
|
366
369
|
- spec/plugin/drop_body_spec.rb
|
370
|
+
- spec/plugin/early_hints_spec.rb
|
367
371
|
- spec/plugin/empty_root_spec.rb
|
368
372
|
- spec/plugin/environments_spec.rb
|
369
373
|
- spec/plugin/error_email_spec.rb
|