syntropy 0.25 → 0.26
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.md +8 -0
- data/examples/index.md +1 -1
- data/lib/syntropy/app.rb +21 -10
- data/lib/syntropy/applets/builtin/default_error_handler/style.css +18 -0
- data/lib/syntropy/applets/builtin/default_error_handler.rb +55 -0
- data/lib/syntropy/applets/builtin/json_api.js +1 -1
- data/lib/syntropy/applets/builtin/req.rb +7 -0
- data/lib/syntropy/module.rb +11 -5
- data/lib/syntropy/request_extensions.rb +15 -1
- data/lib/syntropy/version.rb +1 -1
- data/syntropy.gemspec +1 -1
- data/test/app/_lib/callable.rb +1 -1
- data/test/app/_lib/dep.rb +1 -1
- data/test/app/_lib/missing-export.rb +1 -1
- data/test/app/mod/path/a.rb +3 -0
- data/test/app/mod/path/b.rb +8 -0
- data/test/test_module.rb +18 -7
- data/test/test_routing_tree.rb +1 -1
- metadata +8 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 69fb0521a1229556ec52f7d64d9cb94c00a6e4000d947cd1c78b829923938906
         | 
| 4 | 
            +
              data.tar.gz: 50fceb9e5cf0edd0765da05d44950e1d32fd8bd23cbbf5396df0da3cf941b880
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: b0547a29500fed8ffa15d3d81be8cce5af9a9c8b128291edf44c1eb16aab36987db2f2f16226095649609d9fb0a2b1cc18235b496d0d217fb2518e1de84dfd4b
         | 
| 7 | 
            +
              data.tar.gz: 1b08a07b9220a09b199f4c411af861fe58f674a344643908e7fd691f994cfa1aadaa7782794507800d7a6834be91cc8d117b02a77d597da6bd0411f82dae4e0f
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,3 +1,11 @@ | |
| 1 | 
            +
            # 0.26 2025-10-21
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            - Add /.syntropy/req route for testing request headers
         | 
| 4 | 
            +
            - Add default builtin error page / response
         | 
| 5 | 
            +
            - Add `Request#browser?` method for detecting browser
         | 
| 6 | 
            +
            - Change behaviour of import to support relative paths
         | 
| 7 | 
            +
            - Update Papercraft
         | 
| 8 | 
            +
             | 
| 1 9 | 
             
            # 0.25 2025-10-19
         | 
| 2 10 |  | 
| 3 11 | 
             
            - Upgrade Papercraft to version 3.0
         | 
    
        data/examples/index.md
    CHANGED
    
    
    
        data/lib/syntropy/app.rb
    CHANGED
    
    | @@ -168,7 +168,7 @@ module Syntropy | |
| 168 168 | 
             
                end
         | 
| 169 169 |  | 
| 170 170 | 
             
                # Serves a static file from the given target hash with cache validation.
         | 
| 171 | 
            -
                # | 
| 171 | 
            +
                #
         | 
| 172 172 | 
             
                # @param req [Qeweney::Request] request
         | 
| 173 173 | 
             
                # @param target [Hash] route target hash
         | 
| 174 174 | 
             
                # @return [void]
         | 
| @@ -190,7 +190,7 @@ module Syntropy | |
| 190 190 |  | 
| 191 191 | 
             
                # Validates and conditionally updates the file information for the given
         | 
| 192 192 | 
             
                # target.
         | 
| 193 | 
            -
                # | 
| 193 | 
            +
                #
         | 
| 194 194 | 
             
                # @param target [Hash] route target hash
         | 
| 195 195 | 
             
                # @return [void]
         | 
| 196 196 | 
             
                def validate_static_file_info(target)
         | 
| @@ -203,7 +203,7 @@ module Syntropy | |
| 203 203 | 
             
                STATX_MASK = UM::STATX_MTIME | UM::STATX_SIZE
         | 
| 204 204 |  | 
| 205 205 | 
             
                # Updates the static file information for the given target
         | 
| 206 | 
            -
                # | 
| 206 | 
            +
                #
         | 
| 207 207 | 
             
                # @param target [Hash] route target hash
         | 
| 208 208 | 
             
                # @param now [Time] current time
         | 
| 209 209 | 
             
                # @return [void]
         | 
| @@ -373,12 +373,6 @@ module Syntropy | |
| 373 373 | 
             
                  @module_loader.load(ref)
         | 
| 374 374 | 
             
                end
         | 
| 375 375 |  | 
| 376 | 
            -
                DEFAULT_ERROR_HANDLER = ->(req, err) {
         | 
| 377 | 
            -
                  msg = err.message
         | 
| 378 | 
            -
                  msg = nil if msg.empty? || (req.method == 'head')
         | 
| 379 | 
            -
                  req.respond(msg, ':status' => Syntropy::Error.http_status(err)) rescue nil
         | 
| 380 | 
            -
                }
         | 
| 381 | 
            -
             | 
| 382 376 | 
             
                # Returns an error handler for the given route. If route is nil, looks up
         | 
| 383 377 | 
             
                # the error handler for the routing tree root. If no handler is found,
         | 
| 384 378 | 
             
                # returns the default error handler.
         | 
| @@ -386,7 +380,7 @@ module Syntropy | |
| 386 380 | 
             
                # @param route [Hash] route entry
         | 
| 387 381 | 
             
                # @return [Proc] error handler proc
         | 
| 388 382 | 
             
                def get_error_handler(route)
         | 
| 389 | 
            -
                  route_error_handler(route || @routing_tree.root) ||  | 
| 383 | 
            +
                  route_error_handler(route || @routing_tree.root) || default_error_handler
         | 
| 390 384 | 
             
                end
         | 
| 391 385 |  | 
| 392 386 | 
             
                # Returns the given route's error handler, caching the result.
         | 
| @@ -420,6 +414,23 @@ module Syntropy | |
| 420 414 | 
             
                  route[:parent] && find_error_handler(route[:parent])
         | 
| 421 415 | 
             
                end
         | 
| 422 416 |  | 
| 417 | 
            +
                RAW_DEFAULT_ERROR_HANDLER = ->(req, err) {
         | 
| 418 | 
            +
                  msg = err.message
         | 
| 419 | 
            +
                  msg = nil if msg.empty? || (req.method == 'head')
         | 
| 420 | 
            +
                  req.respond(msg, ':status' => Syntropy::Error.http_status(err)) rescue nil
         | 
| 421 | 
            +
                }
         | 
| 422 | 
            +
             | 
| 423 | 
            +
                def default_error_handler
         | 
| 424 | 
            +
             | 
| 425 | 
            +
                  @default_error_handler ||= begin
         | 
| 426 | 
            +
                    if @builtin_applet
         | 
| 427 | 
            +
                      @builtin_applet.module_loader.load('/default_error_handler')
         | 
| 428 | 
            +
                    else
         | 
| 429 | 
            +
                      RAW_DEFAULT_ERROR_HANDLER
         | 
| 430 | 
            +
                    end
         | 
| 431 | 
            +
                  end
         | 
| 432 | 
            +
                end
         | 
| 433 | 
            +
             | 
| 423 434 | 
             
                # Performs app start up, creating a log message and starting the file
         | 
| 424 435 | 
             
                # watcher according to app options.
         | 
| 425 436 | 
             
                #
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            body {
         | 
| 2 | 
            +
              max-width: 800px;
         | 
| 3 | 
            +
              margin: 4em auto;
         | 
| 4 | 
            +
              
         | 
| 5 | 
            +
              font-family: Seravek, 'Gill Sans Nova', Ubuntu, Calibri, 'DejaVu Sans', source-sans-pro, sans-serif;
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              big {
         | 
| 8 | 
            +
                display: block;
         | 
| 9 | 
            +
                font-weight: bold;
         | 
| 10 | 
            +
                font-size: 10em;
         | 
| 11 | 
            +
              }
         | 
| 12 | 
            +
            }
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            @media (max-width: 768px) {
         | 
| 15 | 
            +
              body {
         | 
| 16 | 
            +
                margin-inline: 1em;
         | 
| 17 | 
            +
              }
         | 
| 18 | 
            +
            }
         | 
| @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            ErrorPage = ->(error:, status:, backtrace:) {
         | 
| 4 | 
            +
              html {
         | 
| 5 | 
            +
                head {
         | 
| 6 | 
            +
                  title "Syntropy error: #{error.message}"
         | 
| 7 | 
            +
                  meta charset: 'utf-8'
         | 
| 8 | 
            +
                  meta name: 'viewport', content: 'width=device-width, initial-scale=1.0'
         | 
| 9 | 
            +
                  link rel: 'stylesheet', type: 'text/css', href: '/.syntropy/default_error_handler/style.css'
         | 
| 10 | 
            +
                }
         | 
| 11 | 
            +
                body {
         | 
| 12 | 
            +
                  div {
         | 
| 13 | 
            +
                    big status
         | 
| 14 | 
            +
                    h2 error.message
         | 
| 15 | 
            +
                    if backtrace
         | 
| 16 | 
            +
                      p "Backtrace:"
         | 
| 17 | 
            +
                      ul {
         | 
| 18 | 
            +
                        backtrace.each {
         | 
| 19 | 
            +
                          li {
         | 
| 20 | 
            +
                            a(it[:entry], href: it[:url])
         | 
| 21 | 
            +
                          }
         | 
| 22 | 
            +
                        }
         | 
| 23 | 
            +
                      }
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  }
         | 
| 26 | 
            +
                  auto_refresh_watch!
         | 
| 27 | 
            +
                }
         | 
| 28 | 
            +
              }
         | 
| 29 | 
            +
            }
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            def transform_backtrace(backtrace)
         | 
| 32 | 
            +
              backtrace.map do
         | 
| 33 | 
            +
                location = it.match(/^(.+:\d+):/)[1]
         | 
| 34 | 
            +
                { entry: it, url: "vscode://file/#{location}" }
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            def error_response_browser(req, error)
         | 
| 39 | 
            +
              status = Syntropy::Error.http_status(error)
         | 
| 40 | 
            +
              backtrace = transform_backtrace(error.backtrace)
         | 
| 41 | 
            +
              html = Papercraft.html(ErrorPage, error:, status:, backtrace:)
         | 
| 42 | 
            +
              req.respond(html, ':status' => status, 'Content-Type' => 'text/html')
         | 
| 43 | 
            +
            end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            def error_response_raw(req, error)
         | 
| 46 | 
            +
              status = error_status_code(error)
         | 
| 47 | 
            +
              msg = err.message
         | 
| 48 | 
            +
              msg = nil if msg.empty? || (req.method == 'head')
         | 
| 49 | 
            +
              req.respond(msg, ':status' => status)
         | 
| 50 | 
            +
            end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            export ->(req, error) {
         | 
| 53 | 
            +
              req.browser? ?
         | 
| 54 | 
            +
                error_response_browser(req, error) : error_response_raw(req, error)
         | 
| 55 | 
            +
            }
         | 
    
        data/lib/syntropy/module.rb
    CHANGED
    
    | @@ -38,6 +38,8 @@ module Syntropy | |
| 38 38 | 
             
                # @param ref [String] module reference
         | 
| 39 39 | 
             
                # @return [any] export value
         | 
| 40 40 | 
             
                def load(ref)
         | 
| 41 | 
            +
                  ref = "/#{ref}" if ref !~ /^\//
         | 
| 42 | 
            +
             | 
| 41 43 | 
             
                  entry = (@modules[ref] ||= load_module(ref))
         | 
| 42 44 | 
             
                  entry[:export_value]
         | 
| 43 45 | 
             
                end
         | 
| @@ -93,6 +95,7 @@ module Syntropy | |
| 93 95 | 
             
                # @param ref [String] module reference
         | 
| 94 96 | 
             
                # @return [Hash] module entry
         | 
| 95 97 | 
             
                def load_module(ref)
         | 
| 98 | 
            +
                  ref = "/#{ref}" if ref !~ /^\//
         | 
| 96 99 | 
             
                  fn = File.expand_path(File.join(@root_dir, "#{ref}.rb"))
         | 
| 97 100 | 
             
                  raise Syntropy::Error, "File not found #{fn}" if !File.file?(fn)
         | 
| 98 101 |  | 
| @@ -112,7 +115,7 @@ module Syntropy | |
| 112 115 |  | 
| 113 116 | 
             
                def clean_ref(ref)
         | 
| 114 117 | 
             
                  return '/' if ref =~ /^index(\+)?$/
         | 
| 115 | 
            -
             | 
| 118 | 
            +
             | 
| 116 119 | 
             
                  ref.gsub(/\/index(?:\+)?$/, '')
         | 
| 117 120 | 
             
                end
         | 
| 118 121 |  | 
| @@ -205,8 +208,9 @@ module Syntropy | |
| 205 208 | 
             
                # @param ref [String] module reference
         | 
| 206 209 | 
             
                # @return [any] loaded dependency's export value
         | 
| 207 210 | 
             
                def import(ref)
         | 
| 208 | 
            -
                   | 
| 209 | 
            -
             | 
| 211 | 
            +
                  path = ref =~ /^\// ? ref : File.expand_path(File.join(File.dirname(@ref), ref))
         | 
| 212 | 
            +
                  @module_loader.load(path).tap {
         | 
| 213 | 
            +
                    __dependencies__ << path
         | 
| 210 214 | 
             
                  }
         | 
| 211 215 | 
             
                end
         | 
| 212 216 |  | 
| @@ -246,11 +250,13 @@ module Syntropy | |
| 246 250 | 
             
                  Syntropy.page_list(@env, ref)
         | 
| 247 251 | 
             
                end
         | 
| 248 252 |  | 
| 249 | 
            -
                # Creates and returns a Syntropy app for the given environment.
         | 
| 253 | 
            +
                # Creates and returns a Syntropy app for the given environment. The app's
         | 
| 254 | 
            +
                # environment is based on the module's env merged with the given parameters.
         | 
| 250 255 | 
             
                #
         | 
| 251 256 | 
             
                # @param env [Hash] environment
         | 
| 252 257 | 
             
                def app(**env)
         | 
| 253 | 
            -
                   | 
| 258 | 
            +
                  env = @env.merge(env)
         | 
| 259 | 
            +
                  Syntropy::App.new(**env)
         | 
| 254 260 | 
             
                end
         | 
| 255 261 | 
             
              end
         | 
| 256 262 | 
             
            end
         | 
| @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            require 'qeweney'
         | 
| 4 | 
            +
            require 'json'
         | 
| 4 5 |  | 
| 5 6 | 
             
            module Syntropy
         | 
| 6 7 | 
             
              # Extensions for the Qeweney::Request class
         | 
| @@ -144,7 +145,7 @@ module Syntropy | |
| 144 145 | 
             
                # matches the given etag or last_modified values, responds with a 304 Not
         | 
| 145 146 | 
             
                # Modified status. Otherwise, yields to the given block for a normal
         | 
| 146 147 | 
             
                # response, and sets cache control headers according to the given arguments.
         | 
| 147 | 
            -
                # | 
| 148 | 
            +
                #
         | 
| 148 149 | 
             
                # @param cache_control [String] value for Cache-Control header
         | 
| 149 150 | 
             
                # @param etag [String, nil] Etag header value
         | 
| 150 151 | 
             
                # @param last_modified [String, nil] Last-Modified header value
         | 
| @@ -184,6 +185,19 @@ module Syntropy | |
| 184 185 | 
             
                  raise Syntropy::Error.new(Qeweney::Status::BAD_REQUEST, 'Invalid form data')
         | 
| 185 186 | 
             
                end
         | 
| 186 187 |  | 
| 188 | 
            +
                def html_repsonse(html)
         | 
| 189 | 
            +
                  respond(html, 'Content-Type' => 'text/html; charset=utf-8')
         | 
| 190 | 
            +
                end
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                def json_response(obj)
         | 
| 193 | 
            +
                  respond(JSON.dump(obj), 'Content-Type' => 'application/json; charset=utf-8')
         | 
| 194 | 
            +
                end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                def browser?
         | 
| 197 | 
            +
                  user_agent = headers['user-agent']
         | 
| 198 | 
            +
                  user_agent && user_agent =~ /^Mozilla\//
         | 
| 199 | 
            +
                end
         | 
| 200 | 
            +
             | 
| 187 201 | 
             
                private
         | 
| 188 202 |  | 
| 189 203 | 
             
                BOOL_REGEXP = /^(t|f|true|false|on|off|1|0|yes|no)$/
         | 
    
        data/lib/syntropy/version.rb
    CHANGED
    
    
    
        data/syntropy.gemspec
    CHANGED
    
    | @@ -23,7 +23,7 @@ Gem::Specification.new do |s| | |
| 23 23 |  | 
| 24 24 | 
             
              s.add_dependency 'extralite',     '2.13'
         | 
| 25 25 | 
             
              s.add_dependency 'json',          '2.13.2'
         | 
| 26 | 
            -
              s.add_dependency 'papercraft',    '3.0'
         | 
| 26 | 
            +
              s.add_dependency 'papercraft',    '3.0.1'
         | 
| 27 27 | 
             
              s.add_dependency 'qeweney',       '0.24'
         | 
| 28 28 | 
             
              s.add_dependency 'tp2',           '0.19'
         | 
| 29 29 | 
             
              s.add_dependency 'uringmachine',  '0.18'
         | 
    
        data/test/app/_lib/callable.rb
    CHANGED
    
    
    
        data/test/app/_lib/dep.rb
    CHANGED
    
    
    
        data/test/test_module.rb
    CHANGED
    
    | @@ -29,6 +29,17 @@ class ModuleTest < Minitest::Test | |
| 29 29 | 
             
                assert_equal 43, mod.bar
         | 
| 30 30 | 
             
              end
         | 
| 31 31 |  | 
| 32 | 
            +
              def test_import_paths
         | 
| 33 | 
            +
                mod = @loader.load('/mod/path/b')
         | 
| 34 | 
            +
                assert_kind_of Hash, mod
         | 
| 35 | 
            +
                assert_equal [:a1, :a2, :foo, :callable], mod.keys
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                assert_equal :foo, mod[:a1]
         | 
| 38 | 
            +
                assert_equal :foo, mod[:a2]
         | 
| 39 | 
            +
                assert_kind_of Syntropy::Module, mod[:foo]
         | 
| 40 | 
            +
                assert_equal 'barbarbar', mod[:callable].(3)
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 32 43 | 
             
              def test_export_self
         | 
| 33 44 | 
             
                mod = @loader.load('_lib/self')
         | 
| 34 45 | 
             
                assert_kind_of Syntropy::Module, mod
         | 
| @@ -39,23 +50,23 @@ class ModuleTest < Minitest::Test | |
| 39 50 | 
             
                mod = @loader.load('_lib/env')
         | 
| 40 51 |  | 
| 41 52 | 
             
                assert_equal mod, mod.module_const
         | 
| 42 | 
            -
                assert_equal @env.merge(module_loader: @loader, ref: '_lib/env'), mod.env
         | 
| 53 | 
            +
                assert_equal @env.merge(module_loader: @loader, ref: '/_lib/env'), mod.env
         | 
| 43 54 | 
             
                assert_equal @machine, mod.machine
         | 
| 44 55 | 
             
                assert_equal @loader, mod.module_loader
         | 
| 45 56 | 
             
                assert_equal 42, mod.app
         | 
| 46 57 |  | 
| 47 58 | 
             
                assert_equal mod, mod.module_const
         | 
| 48 | 
            -
                assert_equal @env.merge(module_loader: @loader, ref: '_lib/env'), mod.env
         | 
| 59 | 
            +
                assert_equal @env.merge(module_loader: @loader, ref: '/_lib/env'), mod.env
         | 
| 49 60 | 
             
                assert_equal @machine, mod.machine
         | 
| 50 61 | 
             
                assert_equal @loader, mod.module_loader
         | 
| 51 62 | 
             
                assert_equal 42, mod.app
         | 
| 52 63 | 
             
              end
         | 
| 53 64 |  | 
| 54 65 | 
             
              def test_dependency_invalidation
         | 
| 55 | 
            -
                 | 
| 56 | 
            -
                assert_equal ['_lib/self', '_lib/dep'], @loader.modules.keys
         | 
| 66 | 
            +
                _mod = @loader.load('_lib/dep')
         | 
| 67 | 
            +
                assert_equal ['/_lib/self', '/_lib/dep'], @loader.modules.keys
         | 
| 57 68 |  | 
| 58 | 
            -
                self_fn = @loader.modules['_lib/self'][:fn]
         | 
| 69 | 
            +
                self_fn = @loader.modules['/_lib/self'][:fn]
         | 
| 59 70 | 
             
                @loader.invalidate_fn(self_fn)
         | 
| 60 71 |  | 
| 61 72 | 
             
                assert_equal [], @loader.modules.keys
         | 
| @@ -63,9 +74,9 @@ class ModuleTest < Minitest::Test | |
| 63 74 |  | 
| 64 75 | 
             
              def test_index_module_env
         | 
| 65 76 | 
             
                mod = @loader.load('mod/bar/index+')
         | 
| 66 | 
            -
                assert_equal 'mod/bar', mod.env[:ref]
         | 
| 77 | 
            +
                assert_equal '/mod/bar', mod.env[:ref]
         | 
| 67 78 |  | 
| 68 79 | 
             
                mod = @loader.load('mod/foo/index')
         | 
| 69 | 
            -
                assert_equal 'mod/foo', mod.env[:ref]
         | 
| 80 | 
            +
                assert_equal '/mod/foo', mod.env[:ref]
         | 
| 70 81 | 
             
              end
         | 
| 71 82 | 
             
            end
         | 
    
        data/test/test_routing_tree.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: syntropy
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: '0. | 
| 4 | 
            +
              version: '0.26'
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Sharon Rosner
         | 
| @@ -43,14 +43,14 @@ dependencies: | |
| 43 43 | 
             
                requirements:
         | 
| 44 44 | 
             
                - - '='
         | 
| 45 45 | 
             
                  - !ruby/object:Gem::Version
         | 
| 46 | 
            -
                    version:  | 
| 46 | 
            +
                    version: 3.0.1
         | 
| 47 47 | 
             
              type: :runtime
         | 
| 48 48 | 
             
              prerelease: false
         | 
| 49 49 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 50 50 | 
             
                requirements:
         | 
| 51 51 | 
             
                - - '='
         | 
| 52 52 | 
             
                  - !ruby/object:Gem::Version
         | 
| 53 | 
            -
                    version:  | 
| 53 | 
            +
                    version: 3.0.1
         | 
| 54 54 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 55 55 | 
             
              name: qeweney
         | 
| 56 56 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -203,8 +203,11 @@ files: | |
| 203 203 | 
             
            - lib/syntropy/applets/builtin/auto_refresh/watch.sse.rb
         | 
| 204 204 | 
             
            - lib/syntropy/applets/builtin/debug/debug.css
         | 
| 205 205 | 
             
            - lib/syntropy/applets/builtin/debug/debug.js
         | 
| 206 | 
            +
            - lib/syntropy/applets/builtin/default_error_handler.rb
         | 
| 207 | 
            +
            - lib/syntropy/applets/builtin/default_error_handler/style.css
         | 
| 206 208 | 
             
            - lib/syntropy/applets/builtin/json_api.js
         | 
| 207 209 | 
             
            - lib/syntropy/applets/builtin/ping.rb
         | 
| 210 | 
            +
            - lib/syntropy/applets/builtin/req.rb
         | 
| 208 211 | 
             
            - lib/syntropy/connection_pool.rb
         | 
| 209 212 | 
             
            - lib/syntropy/dev_mode.rb
         | 
| 210 213 | 
             
            - lib/syntropy/errors.rb
         | 
| @@ -238,6 +241,8 @@ files: | |
| 238 241 | 
             
            - test/app/index.html
         | 
| 239 242 | 
             
            - test/app/mod/bar/index+.rb
         | 
| 240 243 | 
             
            - test/app/mod/foo/index.rb
         | 
| 244 | 
            +
            - test/app/mod/path/a.rb
         | 
| 245 | 
            +
            - test/app/mod/path/b.rb
         | 
| 241 246 | 
             
            - test/app/params/[foo].rb
         | 
| 242 247 | 
             
            - test/app/rss.rb
         | 
| 243 248 | 
             
            - test/app/tmp.rb
         |