roda 3.5.0 → 3.6.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 +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
         |