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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f0422791db9ddee07b535f5429fd17ae5f312c7e5c7519dfbe0288e1d285919
4
- data.tar.gz: 905f19053a3997fc7c3c4e4b942b73cd2400cb59df641cb4a86b23191cab873c
3
+ metadata.gz: f461d51ffcff3abee852c79773a13d87f2ffc5fea2c6a27346bfb2755b318e1e
4
+ data.tar.gz: c30fafc14125ef2b100bce74762193cb0f09776805732c284b7d7cd0472f07c5
5
5
  SHA512:
6
- metadata.gz: 10aff2c1df661f7cb2699cb3f49bf521587813017ca5cc5c1117c629f04805075e185170305cc012cbbdc2c4ecae59902f73efab1ff39806d3f9548acc918d8b
7
- data.tar.gz: d6b70c570f4e198b743ecb4f216f600ad09b8010d6e1df1c202873b843846d1fac3c817dfd848fc7509efc4ba8f41d5907c4273187871ab8293810dc307018e5
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.
@@ -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).map{|p| "#{tag_start}#{h(p)}#{tag_end}"}.join("\n")
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 = env["roda.json_params"] = parse_json(str)
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
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 3
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 5
7
+ RodaMinorVersion = 6
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -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.5.0
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-02-14 00:00:00.000000000 Z
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