├── .ci.gemfile ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG ├── CONTRIBUTING ├── MIT-LICENSE ├── README.rdoc ├── Rakefile ├── doc ├── CHANGELOG.old ├── conventions.rdoc └── release_notes │ ├── 1.0.0.txt │ ├── 1.1.0.txt │ ├── 1.2.0.txt │ ├── 1.3.0.txt │ ├── 2.0.0.txt │ ├── 2.1.0.txt │ ├── 2.10.0.txt │ ├── 2.11.0.txt │ ├── 2.12.0.txt │ ├── 2.13.0.txt │ ├── 2.14.0.txt │ ├── 2.15.0.txt │ ├── 2.16.0.txt │ ├── 2.17.0.txt │ ├── 2.18.0.txt │ ├── 2.19.0.txt │ ├── 2.2.0.txt │ ├── 2.20.0.txt │ ├── 2.21.0.txt │ ├── 2.22.0.txt │ ├── 2.23.0.txt │ ├── 2.24.0.txt │ ├── 2.25.0.txt │ ├── 2.26.0.txt │ ├── 2.27.0.txt │ ├── 2.28.0.txt │ ├── 2.29.0.txt │ ├── 2.3.0.txt │ ├── 2.4.0.txt │ ├── 2.5.0.txt │ ├── 2.5.1.txt │ ├── 2.6.0.txt │ ├── 2.7.0.txt │ ├── 2.8.0.txt │ ├── 2.9.0.txt │ ├── 3.0.0.txt │ ├── 3.1.0.txt │ ├── 3.10.0.txt │ ├── 3.11.0.txt │ ├── 3.12.0.txt │ ├── 3.13.0.txt │ ├── 3.14.0.txt │ ├── 3.14.1.txt │ ├── 3.15.0.txt │ ├── 3.16.0.txt │ ├── 3.17.0.txt │ ├── 3.18.0.txt │ ├── 3.19.0.txt │ ├── 3.2.0.txt │ ├── 3.20.0.txt │ ├── 3.21.0.txt │ ├── 3.22.0.txt │ ├── 3.23.0.txt │ ├── 3.24.0.txt │ ├── 3.25.0.txt │ ├── 3.26.0.txt │ ├── 3.27.0.txt │ ├── 3.28.0.txt │ ├── 3.29.0.txt │ ├── 3.3.0.txt │ ├── 3.30.0.txt │ ├── 3.31.0.txt │ ├── 3.32.0.txt │ ├── 3.33.0.txt │ ├── 3.34.0.txt │ ├── 3.35.0.txt │ ├── 3.36.0.txt │ ├── 3.37.0.txt │ ├── 3.38.0.txt │ ├── 3.39.0.txt │ ├── 3.4.0.txt │ ├── 3.40.0.txt │ ├── 3.41.0.txt │ ├── 3.42.0.txt │ ├── 3.43.0.txt │ ├── 3.44.0.txt │ ├── 3.45.0.txt │ ├── 3.46.0.txt │ ├── 3.47.0.txt │ ├── 3.48.0.txt │ ├── 3.49.0.txt │ ├── 3.5.0.txt │ ├── 3.50.0.txt │ ├── 3.51.0.txt │ ├── 3.52.0.txt │ ├── 3.53.0.txt │ ├── 3.54.0.txt │ ├── 3.55.0.txt │ ├── 3.56.0.txt │ ├── 3.57.0.txt │ ├── 3.58.0.txt │ ├── 3.59.0.txt │ ├── 3.6.0.txt │ ├── 3.60.0.txt │ ├── 3.61.0.txt │ ├── 3.62.0.txt │ ├── 3.63.0.txt │ ├── 3.64.0.txt │ ├── 3.65.0.txt │ ├── 3.66.0.txt │ ├── 3.67.0.txt │ ├── 3.68.0.txt │ ├── 3.69.0.txt │ ├── 3.7.0.txt │ ├── 3.70.0.txt │ ├── 3.71.0.txt │ ├── 3.72.0.txt │ ├── 3.73.0.txt │ ├── 3.74.0.txt │ ├── 3.75.0.txt │ ├── 3.76.0.txt │ ├── 3.77.0.txt │ ├── 3.78.0.txt │ ├── 3.79.0.txt │ ├── 3.8.0.txt │ ├── 3.80.0.txt │ ├── 3.81.0.txt │ ├── 3.82.0.txt │ ├── 3.83.0.txt │ ├── 3.84.0.txt │ ├── 3.85.0.txt │ ├── 3.86.0.txt │ ├── 3.87.0.txt │ ├── 3.88.0.txt │ ├── 3.89.0.txt │ ├── 3.9.0.txt │ ├── 3.90.0.txt │ ├── 3.91.0.txt │ └── 3.92.0.txt ├── lib ├── roda.rb └── roda │ ├── cache.rb │ ├── plugins.rb │ ├── plugins │ ├── Integer_matcher_max.rb │ ├── _after_hook.rb │ ├── _base64.rb │ ├── _before_hook.rb │ ├── _optimized_matching.rb │ ├── _symbol_class_matchers.rb │ ├── _symbol_regexp_matchers.rb │ ├── additional_render_engines.rb │ ├── additional_view_directories.rb │ ├── all_verbs.rb │ ├── assets.rb │ ├── assets_preloading.rb │ ├── assume_ssl.rb │ ├── autoload_hash_branches.rb │ ├── autoload_named_routes.rb │ ├── backtracking_array.rb │ ├── branch_locals.rb │ ├── break.rb │ ├── caching.rb │ ├── capture_erb.rb │ ├── chunked.rb │ ├── class_level_routing.rb │ ├── class_matchers.rb │ ├── common_logger.rb │ ├── conditional_sessions.rb │ ├── content_for.rb │ ├── content_security_policy.rb │ ├── cookie_flags.rb │ ├── cookies.rb │ ├── csrf.rb │ ├── custom_block_results.rb │ ├── custom_matchers.rb │ ├── default_headers.rb │ ├── default_status.rb │ ├── delay_build.rb │ ├── delegate.rb │ ├── delete_empty_headers.rb │ ├── direct_call.rb │ ├── disallow_file_uploads.rb │ ├── drop_body.rb │ ├── each_part.rb │ ├── early_hints.rb │ ├── empty_root.rb │ ├── environments.rb │ ├── erb_h.rb │ ├── error_email.rb │ ├── error_handler.rb │ ├── error_mail.rb │ ├── exception_page.rb │ ├── filter_common_logger.rb │ ├── flash.rb │ ├── h.rb │ ├── halt.rb │ ├── hash_branch_view_subdir.rb │ ├── hash_branches.rb │ ├── hash_matcher.rb │ ├── hash_paths.rb │ ├── hash_routes.rb │ ├── head.rb │ ├── header_matchers.rb │ ├── heartbeat.rb │ ├── hmac_paths.rb │ ├── hooks.rb │ ├── host_authorization.rb │ ├── host_routing.rb │ ├── hsts.rb │ ├── indifferent_params.rb │ ├── inject_erb.rb │ ├── invalid_request_body.rb │ ├── json.rb │ ├── json_parser.rb │ ├── link_to.rb │ ├── mail_processor.rb │ ├── mailer.rb │ ├── match_affix.rb │ ├── match_hook.rb │ ├── match_hook_args.rb │ ├── middleware.rb │ ├── middleware_stack.rb │ ├── module_include.rb │ ├── multi_public.rb │ ├── multi_route.rb │ ├── multi_run.rb │ ├── multi_view.rb │ ├── multibyte_string_matcher.rb │ ├── named_routes.rb │ ├── named_templates.rb │ ├── not_allowed.rb │ ├── not_found.rb │ ├── optimized_segment_matchers.rb │ ├── optimized_string_matchers.rb │ ├── padrino_render.rb │ ├── param_matchers.rb │ ├── params_capturing.rb │ ├── part.rb │ ├── partials.rb │ ├── pass.rb │ ├── path.rb │ ├── path_matchers.rb │ ├── path_rewriter.rb │ ├── permissions_policy.rb │ ├── placeholder_string_matchers.rb │ ├── plain_hash_response_headers.rb │ ├── precompile_templates.rb │ ├── public.rb │ ├── r.rb │ ├── recheck_precompiled_assets.rb │ ├── redirect_http_to_https.rb │ ├── relative_path.rb │ ├── render.rb │ ├── render_coverage.rb │ ├── render_each.rb │ ├── render_locals.rb │ ├── request_aref.rb │ ├── request_headers.rb │ ├── response_request.rb │ ├── route_block_args.rb │ ├── route_csrf.rb │ ├── run_append_slash.rb │ ├── run_handler.rb │ ├── run_require_slash.rb │ ├── sessions.rb │ ├── shared_vars.rb │ ├── sinatra_helpers.rb │ ├── slash_path_empty.rb │ ├── static.rb │ ├── static_routing.rb │ ├── status_303.rb │ ├── status_handler.rb │ ├── streaming.rb │ ├── strip_path_prefix.rb │ ├── symbol_matchers.rb │ ├── symbol_status.rb │ ├── symbol_views.rb │ ├── timestamp_public.rb │ ├── type_routing.rb │ ├── typecast_params.rb │ ├── typecast_params_sized_integers.rb │ ├── unescape_path.rb │ └── view_options.rb │ ├── request.rb │ ├── response.rb │ ├── session_middleware.rb │ └── version.rb ├── roda.gemspec ├── spec ├── all.rb ├── assets │ ├── css │ │ ├── app.html │ │ ├── app.str │ │ ├── import.str │ │ ├── no_access.css │ │ └── raw.css │ └── js │ │ └── head │ │ ├── app.js │ │ ├── comment_1.js │ │ └── comment_2.js ├── autoload_hash_branches │ ├── a.rb │ ├── a │ │ ├── c.rb │ │ ├── d.rb │ │ └── e.rb │ └── b.rb ├── autoload_named_routes │ ├── a.rb │ ├── a │ │ ├── c.rb │ │ ├── d.rb │ │ └── e.rb │ └── b.rb ├── cache_spec.rb ├── composition_spec.rb ├── define_roda_method_spec.rb ├── env_spec.rb ├── freeze_spec.rb ├── integration_spec.rb ├── matchers_spec.rb ├── optimization_spec.rb ├── opts_spec.rb ├── plugin │ ├── Integer_matcher_max_spec.rb │ ├── _after_hook_spec.rb │ ├── additional_render_engines_spec.rb │ ├── additional_view_directories_spec.rb │ ├── all_verbs_spec.rb │ ├── assets_preloading_spec.rb │ ├── assets_spec.rb │ ├── assume_ssl_spec.rb │ ├── autoload_hash_branches_spec.rb │ ├── autoload_named_routes_spec.rb │ ├── backtracking_array_spec.rb │ ├── branch_locals_spec.rb │ ├── break_spec.rb │ ├── caching_spec.rb │ ├── capture_erb_spec.rb │ ├── chunked_spec.rb │ ├── class_level_routing_spec.rb │ ├── class_matchers_spec.rb │ ├── common_logger_spec.rb │ ├── conditional_sessions_spec.rb │ ├── content_for_spec.rb │ ├── content_security_policy_spec.rb │ ├── cookie_flags_spec.rb │ ├── cookies_spec.rb │ ├── csrf_spec.rb │ ├── custom_block_results_spec.rb │ ├── custom_matchers_spec.rb │ ├── default_headers_spec.rb │ ├── default_status_spec.rb │ ├── delay_build_spec.rb │ ├── delegate_spec.rb │ ├── delete_empty_headers_spec.rb │ ├── direct_call_spec.rb │ ├── disallow_file_uploads_spec.rb │ ├── drop_body_spec.rb │ ├── each_part_spec.rb │ ├── early_hints_spec.rb │ ├── empty_root_spec.rb │ ├── environments_spec.rb │ ├── erb_h_spec.rb │ ├── error_email_spec.rb │ ├── error_handler_spec.rb │ ├── error_mail_spec.rb │ ├── exception_page_spec.rb │ ├── filter_common_logger_spec.rb │ ├── flash_spec.rb │ ├── h_spec.rb │ ├── halt_spec.rb │ ├── hash_branch_view_subdir_spec.rb │ ├── hash_branches_spec.rb │ ├── hash_matcher_spec.rb │ ├── hash_paths_spec.rb │ ├── hash_routes_spec.rb │ ├── head_spec.rb │ ├── header_matchers_spec.rb │ ├── heartbeat_spec.rb │ ├── hmac_paths_spec.rb │ ├── hooks_spec.rb │ ├── host_authorization_spec.rb │ ├── host_routing_spec.rb │ ├── hsts_spec.rb │ ├── indifferent_params_spec.rb │ ├── inject_erb_spec.rb │ ├── invalid_request_body_spec.rb │ ├── json_parser_spec.rb │ ├── json_spec.rb │ ├── link_to_spec.rb │ ├── mail_processor_spec.rb │ ├── mailer_spec.rb │ ├── match_affix_spec.rb │ ├── match_hook_args_spec.rb │ ├── match_hook_spec.rb │ ├── middleware_spec.rb │ ├── middleware_stack_spec.rb │ ├── module_include_spec.rb │ ├── multi_public_spec.rb │ ├── multi_route_spec.rb │ ├── multi_run_spec.rb │ ├── multi_view_spec.rb │ ├── multibyte_string_matcher_spec.rb │ ├── named_routes_spec.rb │ ├── named_templates_spec.rb │ ├── not_allowed_spec.rb │ ├── not_found_spec.rb │ ├── optimized_segment_matchers_spec.rb │ ├── optimized_string_matchers_spec.rb │ ├── padrino_render_spec.rb │ ├── param_matchers_spec.rb │ ├── params_capturing_spec.rb │ ├── part_spec.rb │ ├── partials_spec.rb │ ├── pass_spec.rb │ ├── path_matchers_spec.rb │ ├── path_rewriter_spec.rb │ ├── path_spec.rb │ ├── permissions_policy_spec.rb │ ├── placeholder_string_matchers_spec.rb │ ├── plain_hash_response_headers_spec.rb │ ├── precompile_templates_spec.rb │ ├── public_spec.rb │ ├── r_spec.rb │ ├── recheck_precompiled_assets_spec.rb │ ├── redirect_http_to_https_spec.rb │ ├── relative_path_spec.rb │ ├── render_coverage_spec.rb │ ├── render_each_spec.rb │ ├── render_locals_spec.rb │ ├── render_spec.rb │ ├── request_aref_spec.rb │ ├── request_headers_spec.rb │ ├── response_request_spec.rb │ ├── route_block_args_spec.rb │ ├── route_csrf_spec.rb │ ├── run_append_slash_spec.rb │ ├── run_handler_spec.rb │ ├── run_require_slash_spec.rb │ ├── sessions_spec.rb │ ├── shared_vars_spec.rb │ ├── sinatra_helpers_spec.rb │ ├── slash_path_empty_spec.rb │ ├── static_routing_spec.rb │ ├── static_spec.rb │ ├── status_303_spec.rb │ ├── status_handler_spec.rb │ ├── streaming_spec.rb │ ├── strip_path_prefix_spec.rb │ ├── symbol_matchers_spec.rb │ ├── symbol_status_spec.rb │ ├── symbol_views_spec.rb │ ├── timestamp_public_spec.rb │ ├── type_routing_spec.rb │ ├── typecast_params_sized_integers_spec.rb │ ├── typecast_params_spec.rb │ ├── unescape_path_spec.rb │ └── view_options_spec.rb ├── plugin_spec.rb ├── redirect_spec.rb ├── request_spec.rb ├── response_spec.rb ├── route_spec.rb ├── session_middleware_spec.rb ├── session_spec.rb ├── spec_helper.rb ├── version_spec.rb └── views │ ├── _each.foo.str │ ├── _each.str │ ├── _test.erb │ ├── a.erb │ ├── a.rdoc │ ├── a1.str │ ├── a2.html │ ├── a3.erb │ ├── about.erb │ ├── about.str │ ├── about │ ├── _test.css.gz │ ├── _test.erb │ ├── _test.erb.gz │ ├── _test2.css │ ├── _test2.css.br │ ├── _test2.css.gz │ ├── _test2.css.zst │ ├── comp_test.erb │ ├── nested │ │ └── comp_test.erb │ ├── only.erb │ └── only_about.erb │ ├── additional │ ├── only.erb │ └── only_add.erb │ ├── b.erb │ ├── c.erb │ ├── comp_layout.erb │ ├── comp_test.erb │ ├── content-yield.erb │ ├── each.foo.str │ ├── each.str │ ├── fixed │ ├── comp_each_test.erb │ ├── comp_test.erb │ ├── layout.erb │ ├── local_test.erb │ └── opt_local_test.erb │ ├── home.erb │ ├── home.str │ ├── iv.erb │ ├── layout-alternative.erb │ ├── layout-yield.erb │ ├── layout-yield2.erb │ ├── layout.erb │ ├── layout.str │ ├── multiple-layout.erb │ └── multiple.erb └── www ├── config.ru ├── layout.erb ├── make_www.rb ├── pages ├── compare_to_sinatra.erb ├── development.erb ├── documentation.erb └── index.erb └── public ├── css └── roda.css ├── data ├── other │ ├── memory.txt │ └── rps.txt └── popular │ ├── memory.txt │ └── rps.txt ├── images ├── circuits.svg ├── other │ ├── memory.png │ └── rps.png ├── popular │ ├── memory.png │ └── rps.png ├── roda-logo.svg └── sinatra.png └── js └── roda.js /.ci.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | if RUBY_VERSION < '2' 4 | gem 'rake', '<10' 5 | elsif RUBY_VERSION < '2.3' 6 | gem 'rake', '<13' 7 | else 8 | gem 'rake' 9 | end 10 | 11 | if RUBY_VERSION < '2.0.0' 12 | gem 'mime-types', '< 3' 13 | gem "tilt", '<2.0.11' 14 | else 15 | gem "tilt" 16 | end 17 | 18 | if RUBY_VERSION >= '3.4' 19 | gem 'rdoc' 20 | end 21 | 22 | if RUBY_VERSION < '3.1.0' && RUBY_VERSION >= '3.0.0' 23 | gem 'json', '2.5.1' 24 | elsif RUBY_VERSION < '2.0.0' 25 | gem 'json', '<1.8.5' 26 | elsif RUBY_VERSION < '2.3.0' 27 | gem 'json', '<2.6' 28 | else 29 | gem 'json' 30 | end 31 | 32 | case RUBY_VERSION[0, 3] 33 | when '1.9', '2.0' 34 | gem 'rack', '<1.6' 35 | when '2.1', '2.2' 36 | gem 'rack', '<2' 37 | when '2.3' 38 | gem 'rack', '<2.1' 39 | when '2.5' 40 | gem 'rack', '<2.2' 41 | when '2.6' 42 | gem 'rack', '<3' 43 | when '2.7' 44 | gem 'rack', '<3.1' 45 | when '2.4', '3.3' 46 | # Test main branch of Rack for lowest and highest supported 47 | # Ruby version 48 | gem 'rack', :git => 'https://github.com/rack/rack' 49 | else 50 | gem 'rack' 51 | end 52 | 53 | if RUBY_VERSION < '2.4.0' 54 | # Until mintest 5.12.0 is fixed 55 | gem 'minitest', '5.11.3' 56 | else 57 | gem 'minitest' 58 | end 59 | 60 | if RUBY_VERSION >= '3.1.0' 61 | gem 'net-smtp' 62 | end 63 | 64 | gem "minitest-global_expectations" 65 | gem "minitest-hooks" 66 | gem "erubi" 67 | gem "rack_csrf" 68 | gem "mail" 69 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | tests: 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | os: [ubuntu-latest] 18 | ruby: [ "2.0.0", 2.1, 2.3, 2.4, 2.5, 2.6, 2.7, "3.0", 3.1, 3.2, 3.3, 3.4, jruby-9.3, jruby-9.4, jruby-10.0, truffleruby-head ] 19 | include: 20 | - { os: ubuntu-22.04, ruby: "1.9.3" } 21 | - { os: ubuntu-22.04, ruby: jruby-9.1 } 22 | - { os: ubuntu-22.04, ruby: jruby-9.2 } 23 | runs-on: ${{ matrix.os }} 24 | name: ${{ matrix.ruby }} 25 | env: 26 | BUNDLE_GEMFILE: .ci.gemfile 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: ruby/setup-ruby@v1 30 | with: 31 | ruby-version: ${{ matrix.ruby }} 32 | bundler-cache: true 33 | - run: bundle exec rake spec_ci 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /roda-*.gem 2 | /rdoc/ 3 | /coverage/ 4 | /www/public/*.html 5 | /www/public/rdoc/ 6 | /spec/iv-*.erb 7 | /spec/pid-* 8 | /spec/render_coverage-* 9 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2023 Jeremy Evans 2 | Copyright (c) 2010-2014 Michel Martens, Damian Janowski and Cyril David 3 | Copyright (c) 2008-2009 Christian Neukirchen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /doc/release_notes/2.10.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The json plugin now accepts a :content_type option, which will 4 | override the default Content-Type response header used for 5 | responses. 6 | 7 | * Stream#write has been added to the streaming plugin, allowing 8 | the following type of code to work: 9 | 10 | stream do |out| 11 | IO.copy_stream(StringIO.new(content), out) 12 | end 13 | 14 | = Other Improvements 15 | 16 | * Roda now works with ruby 2.3's --enable-frozen-string-literal, 17 | and all of the library files are set to use frozen string 18 | literals by default. 19 | 20 | Most of roda's plugin-specific dependencies were found to have 21 | issues with frozen string literals, and while pull requests have 22 | been sent to fix the issues, it's unlikely that you would 23 | currently be able to use --enable-frozen-string-literal in 24 | production. 25 | 26 | * The json plugin will no longer override a Content-Type header if 27 | one is already set. 28 | -------------------------------------------------------------------------------- /doc/release_notes/2.12.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An optimized_string_matchers plugin has been added, which contains 4 | optimized matchers for single strings. r.on_branch is an optimized 5 | version of r.on and r.is_exactly is optimized version of r.is: 6 | 7 | plugin :optimized_string_matchers 8 | 9 | route do |r| 10 | r.on_branch "x" do 11 | # matches /x and paths starting with /x/ 12 | r.is_exactly "y" do 13 | # matches /x/y 14 | end 15 | end 16 | end 17 | 18 | Both of these methods will work even if the strings have placeholders, 19 | but no captures will be yielded to the blocks. 20 | 21 | * The error_handler plugin now has access to the request's 22 | remaining_path when handling an error. You can then compare the 23 | remaining_path to path_info to see how much of request was already 24 | routed, which can be useful when reporting errors. 25 | 26 | = Other Improvements 27 | 28 | * String matching for strings without placeholders is now 60% faster 29 | as it uses optimized string operations instead of a regexp match. 30 | 31 | * Symbol matching is now 60% faster as it uses optimized string 32 | operations instead of a regexp match. 33 | 34 | = Backwards Compatibility 35 | 36 | * The match methods no longer automatically reset the remaining_path 37 | via ensure. This means that using non-local jumps out of the code 38 | such as begin/rescue and throw/catch will not reset remaining_path 39 | automatically. Users that want to reset remaining path in 40 | such cases should use their own ensure blocks. 41 | -------------------------------------------------------------------------------- /doc/release_notes/2.13.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The render plugin now supports :check_paths and :allowed_paths 4 | options. Setting :check_paths to true will turn on path checking of 5 | template files. By default, template files are required to be in 6 | the :views directory, otherwise an exception will be raised. Using 7 | the :check_paths option can prevent security issues when template 8 | names are derived from user input. The :allowed_paths option 9 | overrides which path prefixes are allowed. In Roda 3, :check_paths 10 | will default to true. 11 | -------------------------------------------------------------------------------- /doc/release_notes/2.14.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A symbol_status plugin has been added for using symbolic status names 4 | in response.status=: 5 | 6 | class App < Roda 7 | plugin :symbol_status 8 | 9 | route do |r| 10 | r.is "needs_authorization" 11 | response.status = :unauthorized 12 | end 13 | end 14 | 15 | = Other Improvements 16 | 17 | * The middleware plugin will now also run the application's middleware 18 | when the application is used as middleware. For example, if you 19 | have the following code in your config.ru file: 20 | 21 | class App < Roda 22 | plugin :csrf 23 | plugin :middleware 24 | route{} 25 | end 26 | 27 | use App 28 | 29 | previously, the csrf protection would not be enforced, as it uses a 30 | middleware instead of being part of the application. Now, csrf 31 | protection will be enforced. This change makes it so the Roda 32 | application operates the same way regardless of whether it is run 33 | as the rack application or used as rack middleware. 34 | 35 | Because of this change, if you are nesting roda applications using 36 | the middleware plugin, you may need to use the middleware plugin's 37 | :env_var option to specify the environment variable used to 38 | indicate to the Roda application that it is being run as middleware. 39 | 40 | = Backwards Compatibility 41 | 42 | * See above changes to the middleware plugin if you are using 43 | middleware inside a Roda application that uses the middleware 44 | plugin. 45 | -------------------------------------------------------------------------------- /doc/release_notes/2.19.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The indifferent_params plugin is now optimized when using Rack 2, 4 | using Rack 2's query_parser API, and it no longer needs to do a 5 | deep copy of the params. 6 | 7 | * The Content-Type and Content-Length headers are no longer added 8 | for 1xx, 204, 205, and 304 responses. 9 | 10 | * The assets_paths method in the assets plugin now works 11 | correctly when subresource integrity is enabled. 12 | 13 | * The asset paths are now escaped in tags by the assets and 14 | assets_preloading plugins. While it's unlikely a developer 15 | would use an asset path that requires escaping, that case is 16 | now handled correctly. 17 | 18 | * The h plugin no longer calls Rack::Utils.escape_html, instead 19 | implementing it's own html escaping. 20 | 21 | * The assets plugin now uses the h plugin, instead of calling 22 | Rack::Utils.escape_html. 23 | 24 | = Backwards Compatibility 25 | 26 | * The h plugin's html escaping no longer escapes "/", which is 27 | a behavior change if you are using any recent version of rack. 28 | The security arguments made to escape "/" could be applied to 29 | many other characters, so if you want to escape "/", you should 30 | probably use a separate method that escapes all \W characters. 31 | -------------------------------------------------------------------------------- /doc/release_notes/2.20.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The render plugin now supports :erubi as an :escape option value, 4 | which will change the plugin to use Erubi instead of Erubis as the 5 | template processor. Erubi is a simplified Erubis fork. 6 | -------------------------------------------------------------------------------- /doc/release_notes/2.21.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The streaming plugin now supports a handle_stream_error method to 4 | handle exceptions when using stream(:loop=>true). This method 5 | takes the exception and the stream object, and can be used to log 6 | errors, output errors into the stream, close the stream, ignore 7 | errors, or any other error handling. 8 | 9 | = Other Improvements 10 | 11 | * A couple of unused variable assignments have been removed, providing 12 | a minor speedup. 13 | 14 | * The specs no longer produce deprecation warnings when using Minitest 15 | 5.10. 16 | 17 | * Some verbose warnings have been removed from the specs. 18 | -------------------------------------------------------------------------------- /doc/release_notes/2.22.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An :unsupported_block_result => :raise option is now supported 4 | for Roda applications. This will raise a RodaError if an 5 | unsupported value is returned from a match block or the 6 | route block. This can make it easier to discover potential 7 | problems in the routing tree. This option may become the 8 | default behavior in Roda 3. 9 | 10 | * An :unsupported_matcher => :raise option is now supported for 11 | Roda applications. This will raise a RodaError if you use an 12 | unsupported value as a matcher. This can make it easier to 13 | discover potential problems in the routing tree. This option 14 | may become the default behavior in Roda 3. 15 | 16 | * A :verbatim_string_matcher option is now supported for Roda 17 | applications. This will make all string matchers only match 18 | the path verbatim, disallowing the use of a colon for 19 | placeholders in the string. It's recommended that users 20 | switch to using separate symbol arguments for placeholders. 21 | 22 | If you enable this option, you need to change code such as: 23 | 24 | r.is "foo/:bar" do |bar| 25 | end 26 | 27 | to: 28 | 29 | r.is "foo", :bar do |bar| 30 | end 31 | 32 | If you are looking to convert an existing routing tree 33 | from using placeholders in strings to separate symbol 34 | arguments, you can scan your routing tree for potential 35 | usage of placeholders in strings: 36 | 37 | grep ' r\..*['\''"].*:.*['\''"]' app.rb 38 | 39 | This option may become the default behavior in Roda 3, with 40 | a plugin added to support the current default behavior of 41 | allowing placeholders in string matchers. 42 | -------------------------------------------------------------------------------- /doc/release_notes/2.23.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An :explicit_cache option has been added to the render plugin. 4 | This is similar to the :cache=>false option, but instead of 5 | disabling caching completely, this disables caching by default but 6 | allows for explicit caching of templates by providing the :cache 7 | option to view/render. 8 | 9 | In development mode, Roda now defaults to :explicit_cache=>true 10 | instead of :cache=>false. 11 | 12 | * An :inherit_cache option has been added to the render plugin, 13 | making subclasses of that class start with a dup of the template 14 | cache, instead of starting with an empty template cache. This can 15 | result in less memory used. 16 | 17 | * Roda#error_email in the error_email plugin now accepts non-Exception 18 | arguments (such as strings). This can be useful in conditions that 19 | are errors you may want to notify about, where an exception hasn't 20 | been raised. 21 | 22 | * Roda#error_email_content has been added to the error_email plugin. 23 | This can be used to create the email message, which can be delivered 24 | via another mechanism, and may make testing easier. 25 | 26 | = Other Improvements 27 | 28 | * Roda.freeze in the static_routing plugin now returns self, fixing 29 | code such as Roda.freeze.app. 30 | -------------------------------------------------------------------------------- /doc/release_notes/2.25.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An error_mail plugin has been added for reporting exceptions raised 4 | via email. This is similar to the existing error_email plugin, but 5 | uses the mail library instead of net/smtp directly. If you are 6 | already using the mail library and the error_email plugin in your 7 | application, it's recommended to switch to the error_mail plugin. 8 | Example: 9 | 10 | plugin :error_mail, :to=>'to@example.com', :from=>'from@example.com' 11 | plugin :error_handler do |e| 12 | error_mail(e) 13 | 'Internal Server Error' 14 | end 15 | -------------------------------------------------------------------------------- /doc/release_notes/2.26.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The csrf plugin now supports a :skip_middleware option, which adds 4 | the methods without adding the middleware. This is designed for 5 | cases where you are using multiple rack apps, where the rack_csrf 6 | middleware is loaded in an earlier rack app, and you want to avoid 7 | the duplicate CSRF checks. 8 | 9 | = Other Improvements 10 | 11 | * The type_routing plugin now supports using multiple extensions 12 | where one extension is a suffix of another extension, such as 13 | using gz and tar.gz. 14 | -------------------------------------------------------------------------------- /doc/release_notes/2.28.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A status_303 plugin has been added, which changes the default 4 | redirect status from 302 to 303 if the HTTP version is 1.1 and 5 | the request is not a GET request. 6 | 7 | = Other Improvements 8 | 9 | * Roda is now optimized for ruby 2.3+ using frozen string literals 10 | instead of constant references. This improves performance on ruby 11 | 2.3+, and decreases performance on ruby <2.3. 12 | 13 | = Backwards Compatibility 14 | 15 | * Many now unused internal constants are now deprecated, and 16 | attempting to access them will result in deprecation warnings 17 | on ruby 2.3+. 18 | -------------------------------------------------------------------------------- /doc/release_notes/2.5.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The assets plugin now supports a :compiled_asset_host option, which 4 | specifies a hostname used to serve compiled assets. 5 | 6 | * The render plugin now supports a :cache_class option, which 7 | specificies a class to use for the thread-safe template cache. 8 | This can be used to setup LRU caching or caching that checks 9 | modify times on the underlying template files. 10 | 11 | * r.multi_run in the multi_run plugin now accepts a block, and calls 12 | the block before dispatching to the related rack application. This 13 | can be used to modify the environment before dispatching. Example: 14 | 15 | r.multi_run do |prefix| 16 | env['authenticated'] = true 17 | end 18 | 19 | = Backwards Compatibility 20 | 21 | * The :by_name option to the path plugin now defaults to true in 22 | development mode. This should only negatively affect applications 23 | that register anonymous classes with the path plugin. 24 | -------------------------------------------------------------------------------- /doc/release_notes/2.5.1.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The multi_route plugin now works correctly if the middleware plugin 4 | is loaded after it. 5 | -------------------------------------------------------------------------------- /doc/release_notes/2.6.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * :params and :params! matchers have been added to the param_matchers 4 | plugin, allowing you to match multiple params at the same time: 5 | 6 | r.on :params=>%w'foo bar' do |foo, bar| end 7 | # instead of 8 | r.on({:param=>'foo'}, :param=>'bar') do |foo, bar| end 9 | 10 | = Other Improvements 11 | 12 | * When loading the csrf plugin multiple times, instead of loading the 13 | middleware multiple times with different settings, merge options 14 | in later plugin calls into a single middleware option hash, and 15 | only load the middleware once. 16 | 17 | This allows plugins to depend on the csrf plugin, while also 18 | allowing the application to use the csrf plugin with options. 19 | 20 | * request.halt now works correctly when used inside a before hook when 21 | using the hooks plugin. 22 | -------------------------------------------------------------------------------- /doc/release_notes/2.8.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A multi_view plugin has been added, for easily setting up routing 4 | for rendering multiple views: 5 | 6 | plugin :multi_view 7 | 8 | route do |r| 9 | r.multi_view(['foo', 'bar', 'baz']) 10 | end 11 | 12 | # or: 13 | 14 | route do |r| 15 | r.multi_view(/(foo|bar|baz)/) 16 | end 17 | 18 | # or: 19 | 20 | regexp = multi_view_compile(['foo', 'bar', 'baz']) 21 | route do |r| 22 | r.multi_view(regexp) 23 | end 24 | 25 | # all are equivalent to: 26 | 27 | route do |r| 28 | r.get 'foo' do 29 | view('foo') 30 | end 31 | 32 | r.get 'bar' do 33 | view('bar') 34 | end 35 | 36 | r.get 'baz' do 37 | view('baz') 38 | end 39 | end 40 | 41 | = Other Improvements 42 | 43 | * The content_for plugin now supports haml templates. Previous only 44 | erb templates were supported. 45 | -------------------------------------------------------------------------------- /doc/release_notes/2.9.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The content_for plugin now supports passing the content as a 4 | string argument instead of a block: 5 | 6 | <% content_for :foo, "Some content" %> 7 | -------------------------------------------------------------------------------- /doc/release_notes/3.1.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A :timestamp_paths option has been added to the assets plugin to 4 | include timestamps in paths in non-compiled mode. This can fix 5 | asset staleness issues when using a caching proxy. This is 6 | not needed in compiled mode, as the asset file names include the 7 | hash of the asset. It is not the default in non-compiled mode, 8 | as few people would use a caching proxy in non-compiled mode. 9 | 10 | = Other Improvements 11 | 12 | * Make set_layout_locals and set_view_locals in branch_locals 13 | plugin work when the other is not called. 14 | 15 | * When testing support for uglifier usability as a JS asset 16 | compressor, handle case where uglifier is installed but there is 17 | no available javascript runtime. 18 | 19 | = Backwards Compatibility 20 | 21 | * The deprecated Roda.thread_safe_cache method has been removed. 22 | 23 | * The deprecated private RodaRequest#placeholder_string_matcher? 24 | method has been removed. 25 | -------------------------------------------------------------------------------- /doc/release_notes/3.12.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A common_logger plugin has been added for common log support. This 4 | offers about 30% better performance than Rack::CommonLogger, with 5 | the following differences: 6 | 7 | * When timing requests, doesn't consider middleware or proxy the 8 | body, so timing information is just the time that Roda takes 9 | to process the request. 10 | * Only looks for "Content-Length" as a header, not different 11 | capitalizations (Roda only uses "Content-Length" internally). 12 | * Logs to $stderr instead of rack.errors in request environment 13 | if a logger object is not explicitly passed. 14 | 15 | = Other Improvements 16 | 17 | * Internal before/after hook methods now use more descriptive names 18 | for easier debugging, with a naming format designed to not 19 | conflict with hook methods in external plugins. 20 | -------------------------------------------------------------------------------- /doc/release_notes/3.14.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The convert! and convert_each! methods in the typecast_params plugin 4 | now support a :raise option for handling missing parameters specified 5 | as arguments to the methods. 6 | 7 | If the :raise option is set to false for convert! and the parameter 8 | argument is missing, then no conversion is done and an empty hash 9 | is returned: 10 | 11 | typecast_params.convert!('missing', raise: false) do |tp| 12 | # ... 13 | end 14 | # => {} 15 | 16 | If the :raise option is set to false for convert_each! and a :keys 17 | option is given, any key not present is ignored and nil will be 18 | returned for the converted value 19 | 20 | typecast_params.convert_each!(:keys=>['present', 'missing'], raise: false) do |tp| 21 | tp.int('b') 22 | end 23 | # => [{'b'=>1}, nil] 24 | 25 | = Other Improvements 26 | 27 | * The :symbolize setting to the convert! and convert_each! methods in 28 | the typecast_params plugin is no longer persisted beyond the call 29 | to the method. This fixes unexpected behavior if you do: 30 | 31 | typecast_params.convert!(:symbolize=>true) do |tp| 32 | # ... 33 | end 34 | typecast_params.convert! do |tp| 35 | # ... 36 | end 37 | -------------------------------------------------------------------------------- /doc/release_notes/3.15.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The render plugin :escape option value can now be a string or an 4 | array of strings, and then the plugin will will only add the 5 | :escape template option for those specific template engines given. 6 | By default, the :escape plugin option adds the :escape template 7 | option for all engines, which breaks the usage with some engines 8 | (such as the rcsv engine). 9 | 10 | * The convert! and convert_each! methods in the typecast_params plugin 11 | now support a :skip_missing option to support not storing missing 12 | parameters: 13 | 14 | typecast_params.convert! do |tp| 15 | tp.int('missing') 16 | end 17 | # => {'missing'=>nil} 18 | typecast_params.convert!(skip_missing: false) do |tp| 19 | tp.int('missing') 20 | end 21 | # => {} 22 | -------------------------------------------------------------------------------- /doc/release_notes/3.2.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A timestamp_public plugin has been added for serving static files 4 | with paths that change based on the modification timestamp of the 5 | file. By using a new path, cached versions of the file will not 6 | be used, fixing staleness issues. Example: 7 | 8 | plugin :timestamp_public 9 | 10 | route do |r| 11 | # serves requests for /static/\d+/.* 12 | r.timestamp_public 13 | 14 | # /static/1234567890/path/to/file 15 | timestamp_path("path/to/file") 16 | end 17 | 18 | = Other Improvements 19 | 20 | * When using the assets plugin :timestamp_paths option, the 21 | timestamps now include microseconds, to make cache poisoning more 22 | difficult. 23 | -------------------------------------------------------------------------------- /doc/release_notes/3.20.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * For empty responses with status code 205, a Content-Length header 4 | is now added with a value of 0, for better conformance to RFC 7232. 5 | 6 | Similarly, when using the drop_body plugin, responses with status 7 | code 205 now have a Content-Length header added with a value of 0. 8 | -------------------------------------------------------------------------------- /doc/release_notes/3.21.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * View rendering speed is significantly improved in development mode 4 | by caching file-based templates until there has been a modification 5 | to the template file. 6 | -------------------------------------------------------------------------------- /doc/release_notes/3.22.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The render/view methods in the render plugin, when called with 4 | a single string/symbol argument (the most common case), are now 5 | up to 2.5x/4x faster by directly calling compiled template methods. 6 | This works by extracting the UnboundMethod objects that Tilt 7 | creates, and defining real methods for them, then calling those 8 | methods using send. This avoids most of the overhead of the render 9 | and view methods. The compiled template methods are defined inside 10 | a module included in the Roda app's class, so this support works 11 | even if the Roda app itself is frozen. 12 | 13 | Some plugins, such as render_locals, do not work with this 14 | optimization, and disable the use of it. The view_options plugin 15 | does work with this optimization if you are using set_view_subdir or 16 | append_view_subdir, but not if using set_view_options or 17 | set_layout_options. 18 | 19 | This optimization depends on Ruby 2.3+ and Tilt 1.2+, and will not 20 | be used on earlier versions, or if an API change in Tilt is 21 | detected. 22 | 23 | * Session deserialization is now slightly faster in the sessions 24 | plugin. 25 | -------------------------------------------------------------------------------- /doc/release_notes/3.23.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The render/view methods in the render plugin, when called with 4 | a single string/symbol argument (the most common case), are now 5 | up to 2x faster in cache: false mode by directly calling compiled 6 | template methods. This takes the performance increase in 3.22.0 7 | and applies it to cache: false mode in addition to cache: true 8 | mode. If the template file has changed, the compiled method is 9 | removed, and a new compiled method replaces it. 10 | 11 | * Template modification detection in the render plugin now uses a 12 | faster check for modification, which also avoids a race condition. 13 | 14 | * The type_routing plugin now handles requests with nothing but the 15 | extension in the request path. This fixes cases when you have 16 | one app partially route a request, and send the request to another 17 | app, and that app uses the type_routing plugin and has an r.is 18 | call at the root level. 19 | 20 | * The roda/session_middleware middleware now works correctly if the 21 | type_routing plugin is loaded into Roda itself (as opposed to a 22 | Roda subclass). 23 | 24 | * The exception_page plugin now always shows the line number for 25 | each line. Previously, it only showed the line number if it was 26 | showing the content of the line, which complicated debugging in 27 | cases where the content of the line was no longer retrievable 28 | due to file system permissions or restrictions (e.g. chroot). 29 | -------------------------------------------------------------------------------- /doc/release_notes/3.24.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The performance of the render_each plugin has been dramatically 4 | improved by calling compiled template methods directly. For a simple 5 | template, render_each performance with a single object can be about 6 | 2x faster, and render_each performance for 100 objects can be 3x 7 | (cache: false) to 9x (cache: true) faster. 8 | 9 | This optimization can be used if no options are provided to 10 | render_each, or if :local and/or :locals options are provided. Use 11 | of other options will disable this optimization. 12 | 13 | * The module_include plugin no longer calls Proc.new without a 14 | block, fixing a warning on Ruby 2.7. 15 | -------------------------------------------------------------------------------- /doc/release_notes/3.25.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The new tilt 2.0.10 private API is now supported when using 4 | compiled template methods, with up to a 33% performance increase. 5 | The older tilt private API (back to tilt 1.2) is still supported. 6 | 7 | * The performance of the render and view methods in the render plugin 8 | when called with only the :locals option are now about 75% faster 9 | by calling compiled template methods directly. 10 | 11 | * Keyword argument separation issues are now handled on Ruby 2.7+ 12 | when defining methods with blocks that accept keyword arguments. 13 | -------------------------------------------------------------------------------- /doc/release_notes/3.26.0.txt: -------------------------------------------------------------------------------- 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. 16 | -------------------------------------------------------------------------------- /doc/release_notes/3.27.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A multibyte_string_matcher plugin has been added that supports 4 | multibyte characters in strings used as matchers. It uses a slower 5 | string matching implementation that supports multibyte characters. 6 | As multibyte strings in paths must be escaped, this also loads the 7 | unescape_path plugin. 8 | 9 | = Other Improvements 10 | 11 | * The json_parser plugin now returns expected results for invalid JSON 12 | if the params_capturing plugin is used. 13 | 14 | * lib/roda.rb has been split into multiple files for easier code 15 | navigation. 16 | -------------------------------------------------------------------------------- /doc/release_notes/3.28.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The sessions plugin now supports RodaRequest#session_created_at 4 | and RodaRequest#session_updated_at for the times of session 5 | creation and last update. 6 | 7 | = Other Improvements 8 | 9 | * The json_parser plugin now correctly parses the request body even 10 | if the request body has already been read. 11 | 12 | * The sessions plugin now correctly handles upgrading rack cookie 13 | sessions when using rack 2.0.8+. 14 | -------------------------------------------------------------------------------- /doc/release_notes/3.29.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The common_logger plugin now includes the SCRIPT_NAME when 4 | logging, for greater compatibility with typical web server 5 | logs. 6 | 7 | * The exception_page plugin now handles invalid POST data. 8 | Previously, invalid POST data would cause the exception page 9 | display to raise an exception. 10 | 11 | * An error is now raised if trying to load a plugin that is not a 12 | module or a recognized plugin symbol. 13 | 14 | * Specs and older release notes are no longer shipped in the 15 | gem, reducing gem size by over 35%. 16 | -------------------------------------------------------------------------------- /doc/release_notes/3.30.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A :relative_paths plugin option has been added to the assets 4 | plugin. This option makes the paths to the asset files in the 5 | link and script tags relative paths instead of absolute paths. 6 | 7 | = Other Improvements 8 | 9 | * The :header matcher in the header_matchers plugin now works 10 | correctly for the Content-Type and Content-Length headers, which 11 | are not prefixed with HTTP_ in the rack environment. 12 | 13 | * The run_append_slash and run_handler plugins now work correctly 14 | when used together. 15 | -------------------------------------------------------------------------------- /doc/release_notes/3.31.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A relative_path plugin has been added, adding a relative_path 4 | method that will take an absolute path and make it relative to the 5 | current request by prepending an appropriate prefix. This is 6 | helpful when using Roda as a static site generator to generate a 7 | site that can be hosted at any subpath or directly from the 8 | filesystem. 9 | 10 | * In the path plugin, the path method now accepts a :relative 11 | option for generating relative paths instead of absolute paths. 12 | -------------------------------------------------------------------------------- /doc/release_notes/3.32.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * render_each in the render_each plugin now automatically handles 4 | template names with subdirectories and extensions. Previously, these 5 | caused issues unless the :local option was provided. So now you 6 | can use: 7 | 8 | render_each(foos, "items/foo") 9 | 10 | instead of: 11 | 12 | render_each(foos, "items/foo", :local=>:foo) 13 | 14 | * each_partial has been added to the partials plugin. It operates 15 | similarly to render_each, but uses the convention for partial 16 | template naming. So this: 17 | 18 | each_partial(foos, "items/foo") 19 | 20 | is the same as: 21 | 22 | render_each(foos, "items/_foo", :local=>:foo) 23 | 24 | = Other Improvements 25 | 26 | * The :dependencies option in the assets plugin now works correctly 27 | with compiled templates in the render plugin in uncached mode 28 | (the default in development). Previously, modifying a dependency 29 | file would not result in recompiling the asset template when 30 | requesting the main file. 31 | 32 | * Method visibility issues in the following plugins have been fixed: 33 | 34 | * content_security_policy 35 | * default_headers 36 | * indifferent_params 37 | * placeholder_string_matchers 38 | * symbol_matchers 39 | 40 | Previously, these plugins made private methods public by mistake 41 | when overriding them. Additionally, Roda.freeze no longer changes 42 | the visibility of the set_default_headers private method. 43 | -------------------------------------------------------------------------------- /doc/release_notes/3.33.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The path plugin now supports a url method, allowing for returning 4 | the entire URL instead of just the path for class-based paths. 5 | 6 | * The public plugin now supports a :brotli option that will directly 7 | serve brotli-compressed files (with .br extension) similar to how the 8 | :gzip option directly serves gzipped files (with the .gz extension). 9 | -------------------------------------------------------------------------------- /doc/release_notes/3.34.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * Multiple unneeded conditionals have been removed. 4 | 5 | * pre_content and post_context sections in backtraces are no longer 6 | included in the exception_page plugin output if they would be 7 | empty. 8 | 9 | * The match_affix plugin can be loaded again with a single argument. 10 | It was originally designed to accept a single argument, but a bug 11 | introduced in 2.29.0 made it require two arguments. 12 | 13 | * Core Roda and all plugins that ship with Roda now have 100% branch 14 | coverage. 15 | 16 | * The sinatra_helpers plugin no longer emits statement not reached 17 | warnings in verbose mode. 18 | -------------------------------------------------------------------------------- /doc/release_notes/3.35.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An r plugin has been added. This plugin adds an r method for the 4 | request, useful for allowing the use of r.halt and r.redirect even 5 | in methods where the r local variable is not in scope. 6 | 7 | = Other Improvements 8 | 9 | * Attempting to load a plugin with an argument or block when the plugin 10 | does not accept arguments or a block now warns. This is because a 11 | future update to support a block or an optional argument could break 12 | the call. 13 | -------------------------------------------------------------------------------- /doc/release_notes/3.36.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A multi_public plugin has been added, which allows serving static 4 | files from multiple separate directories. This is especially 5 | useful when there are different access control requirements per 6 | directory. 7 | 8 | * The content_security_policy now supports a 9 | content_security_policy.report_to method to set the 10 | report-to directive. 11 | 12 | = Other Improvements 13 | 14 | * When using the type_routing plugin and performing type routing 15 | using the Accept request header, the Vary response header will be 16 | added or updated so that http caches do not cache a response for one 17 | type and serve it for a different type. 18 | -------------------------------------------------------------------------------- /doc/release_notes/3.37.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A custom_matchers plugin has been added, which allows using 4 | arbitrary objects as matchers, as long as the matcher has been 5 | registered. You can register matchers using the custom_matcher 6 | class method, which takes the class of the matcher, and a block 7 | which is yielded the matcher object. The block should return 8 | nil or false if the matcher doesn't match, and any other value 9 | if the matcher does match. Example: 10 | 11 | plugin :custom_matchers 12 | method_segment = Struct.new(:request_method, :next_segment) 13 | custom_matcher(method_segment) do |matcher| 14 | # self is the request instance ("r" yielded in the route block below) 15 | if matcher.request_method == self.request_method 16 | match(matcher.next_segment) 17 | end 18 | end 19 | 20 | get_foo = method_segment.new('GET', 'foo') 21 | post_any = method_segment.new('POST', String) 22 | route do |r| 23 | r.on('baz') do 24 | r.on(get_foo) do 25 | # GET method, /baz/foo prefix 26 | end 27 | 28 | r.is(post_any) do |seg| 29 | # for POST /baz/bar, seg is "bar" 30 | end 31 | end 32 | 33 | r.on('quux') do 34 | r.is(get_foo) do 35 | # GET method, /quux/foo route 36 | end 37 | 38 | r.on(post_any) do |seg| 39 | # for POST /quux/xyz, seg is "xyz" 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /doc/release_notes/3.38.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The error_email and error_mail plugins now rescue invalid parameter 4 | errors when preparing the email body, because you generally don't 5 | want your error handler to raise an exception. 6 | -------------------------------------------------------------------------------- /doc/release_notes/3.39.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The relative_path plugin is now faster if you are calling 4 | relative_path or relative_prefix more than once when handling a 5 | request. 6 | 7 | * The typecast_params.convert! method in the typecast_params plugin 8 | now handles explicit nil values the same as missing values. 9 | Explicit nil values do not generally occur in normal Rack parameter 10 | parsing, but they can occur when using the json_parser plugin to 11 | parse JSON requests. 12 | 13 | * Roda now avoids method redefinition warnings in verbose mode by 14 | using a self alias. As Ruby 3 is dropping uninitialized instance 15 | variable warnings, Roda will be verbose warning free if you are 16 | using Ruby 3. 17 | -------------------------------------------------------------------------------- /doc/release_notes/3.4.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A middleware_stack plugin has been added for more detailed control 4 | over middleware, allowing for the removal of middleware and the 5 | insertion of middleware before existing middleware. Example: 6 | 7 | plugin :middleware_stack 8 | 9 | # Remove csrf middleware 10 | middleware_stack.remove{|m, *args| m == Rack::Csrf} 11 | 12 | # Insert csrf middleware before logger middleware 13 | middleware_stack.before{|m, *args| m == Rack::CommonLogger}. 14 | use(Rack::Csrf, raise: true) 15 | 16 | # Insert csrf middleware after logger middleware 17 | middleware_stack.after{|m, *args| m == Rack::CommonLogger}. 18 | use(Rack::Csrf, raise: true) 19 | 20 | = Other Improvements 21 | 22 | * The head plugin now calls close on the response body if the body 23 | responds to close. Previously an existing response body was 24 | just ignored. 25 | -------------------------------------------------------------------------------- /doc/release_notes/3.40.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A precompile_views method has been added to the 4 | precompile_templates plugin. This method works with Roda's 5 | optimized compiled view methods, allowing additional memory 6 | sharing between parent and child processes. 7 | 8 | * A freeze_template_caches! method has been added to the 9 | precompile_templates plugin. This freezes the template caches, 10 | preventing the compilation of additional templates, useful for 11 | enforcing that only precompiled templates are used. Additionally, 12 | this speeds up access to the template caches. 13 | 14 | * RodaCache#freeze now returns the frozen internal hash, which can 15 | then be accessed without a mutex. Previously, freeze only froze 16 | the receiver and not the internal hash, so it didn't have the 17 | expected effect. 18 | 19 | = Other Improvements 20 | 21 | * The view method in the render plugin is now faster in most cases 22 | when a single argument is used. When freezing the application, 23 | an additional optimization is performed to increase the 24 | performance of the view method even further. 25 | -------------------------------------------------------------------------------- /doc/release_notes/3.41.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The performance of the render plugin's view method when passed the 4 | :content option and no other options or arguments has been improved 5 | by about 3x, by calling compiled template methods directly. 6 | 7 | * The compiled template method for the layout is cleared when the 8 | render plugin is loaded again, which can fix issues when it is 9 | loaded with different options that affect the layout. 10 | -------------------------------------------------------------------------------- /doc/release_notes/3.42.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A recheck_precompiled_assets plugin has been added, which allows 4 | for checking for updates to the precompiled asset metadata file, 5 | and automatically using the updated data. 6 | 7 | * The common_logger plugin now supports a :method plugin option to 8 | specify the method to call on the logger. 9 | 10 | = Other Improvements 11 | 12 | * Plugins and middleware that use keyword arguments are now supported 13 | in Ruby 3. 14 | 15 | * The compile_assets class method in the assets plugin now uses an 16 | atomic approach to writing the precompiled asset metadata file. 17 | 18 | * Minor method visibility issues have been fixed. The custom_matchers 19 | plugin no longer makes the unsupported_matcher request method 20 | public, and the render plugin no longer makes the _layout_method 21 | public when the application is frozen. 22 | -------------------------------------------------------------------------------- /doc/release_notes/3.43.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A host_authorization plugin has been added to verify the requested 4 | Host header is authorized. Using it can prevent DNS rebinding 5 | attacks in cases where the application can receive requests for 6 | arbitrary hosts. 7 | 8 | To check for authorized hosts in your routing tree, you call the 9 | check_host_authorization! method. For example, if you want to 10 | check for authorized hosts after serving requests for public 11 | files, you could do: 12 | 13 | plugin :public 14 | plugin :host_authorization, 'my-domain-name.example.com' 15 | 16 | route do |r| 17 | r.public 18 | check_host_authorized! 19 | 20 | # ... rest of routing tree 21 | end 22 | 23 | In addition to handling single domain names via a string, you can 24 | provide an array of domain names, a regexp to match again, or a 25 | proc. 26 | 27 | By default, requests using unauthorized hosts receive an empty 403 28 | response. If you would like to customize the response, you can 29 | pass a block when loading the plugin: 30 | 31 | plugin :host_authorization, 'my-domain-name.example.com' do |r| 32 | response.status = 403 33 | "Response Body Here" 34 | end 35 | -------------------------------------------------------------------------------- /doc/release_notes/3.44.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An optimized_segment_matchers plugin has been added that offers 4 | very fast matchers for arbitrary segments (the same segments 5 | that would be matched by the String class matcher). The 6 | on_segment method it offers accepts no arguments and yields 7 | the next segment if there is a segment. The is_segment method 8 | is similar, but only yields if the next segment is the final 9 | segment. 10 | 11 | = Other Improvements 12 | 13 | * The send_file and attachment methods in the sinatra_helpers plugin 14 | now support RFC 5987 UTF-8 and ISO-8859-1 encoded filenames, 15 | allowing modern browsers to save files with encoded chracters. For 16 | older browsers that do not support RFC 5987, unsupported characters 17 | in filenames are replaced with dashes. This is considered to be an 18 | improvement over the previous behavior of using Ruby's inspect 19 | output for the filename, which could contain backslashes (backslash 20 | is not an allowed chracter in Windows filenames). 21 | 22 | * The performance of the String class matcher has been slightly 23 | improved. 24 | -------------------------------------------------------------------------------- /doc/release_notes/3.45.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The typecast_params plugin checks now checks for null bytes by 4 | default before typecasting. If null bytes are present, it raises 5 | an error. Most applications do not require null bytes in 6 | parameters, and in some cases allowing them can lead to security 7 | issues, especially when parameters are passed to C extensions. 8 | In general, the benefit of forbidding null bytes in parameters is 9 | greater than the cost. 10 | 11 | If you would like to continue allowing null bytes, use the 12 | :allow_null_bytes option when loading the plugin. 13 | 14 | Note that this change does not affect uploaded files, since those 15 | are expected to contain null bytes. 16 | 17 | = Backwards Compatibility 18 | 19 | * The change to the typecast_params plugin to raise an error for 20 | null bytes can break applications that are expecting null bytes 21 | to be passed in parameters. Such applications should use the 22 | :allow_null_bytes option when loading the plugin. 23 | -------------------------------------------------------------------------------- /doc/release_notes/3.46.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The r.on, r.is, r.get and r.post methods (and other verb methods 4 | if using the all_verbs plugin) have now been optimized when using 5 | a single string or regexp matcher, or the String or Integer class 6 | matcher. Since those four matchers are the most common types of 7 | matchers passed to the methods, this can significantly improve 8 | routing performance (about 50% in the r10k benchmark). 9 | 10 | This optimization is automatically applied when freezing 11 | applications, if the related methods have not been modified by 12 | plugins. 13 | 14 | This optimization does come at the expense of a small decrease 15 | in routing performance (3-4%) for unoptimized cases, but the 16 | majority of applications will see a overall performance benefit 17 | from this change. 18 | 19 | * Other minor performance improvements have been made. 20 | -------------------------------------------------------------------------------- /doc/release_notes/3.47.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The r.on optimization added in 3.46.0 has been extended to optimize 4 | all single argument calls. This results in the following speedups 5 | based on argument type: 6 | 7 | * Hash matching: 10% 8 | * Array/Symbol/Class matching: 15% 9 | * Proc matching: 25% 10 | * true matching: 45% 11 | * false/nil matching: 65% 12 | 13 | * Other minor performance improvements have been made. 14 | -------------------------------------------------------------------------------- /doc/release_notes/3.48.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A named_routes plugin has been added, for defining named route 4 | blocks that you can dispatch to with r.route. This feature was 5 | previously available as part of the multi_route plugin, but there 6 | are cases where the r.route method and support for named routes is 7 | helpful even when the multi_route plugin is not used (such as when 8 | the hash_routes plugin is used instead of the multi_route plugin). 9 | The multi_route plugin now depends on the named_routes plugin, so 10 | this change should not cause any backwards compatibility issues. 11 | -------------------------------------------------------------------------------- /doc/release_notes/3.49.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The r.is optimization added in 3.46.0 has been extended to optimize 4 | all single argument calls. This results in the following speedups 5 | based on argument type: 6 | 7 | * Hash/Class matching: 20% 8 | * Symbol matching: 25% 9 | * Array matching: 35% 10 | * Proc matching: 50% 11 | * false/nil matching: 65% 12 | 13 | * Roda now uses defined?(yield) instead of block_given? internally 14 | for better performance on CRuby. defined?(yield) is faster as it is 15 | built into the VM, while block_given? is a regular method and has 16 | the overhead of calling a regular method. Note that defined?(yield) 17 | is not implemented correctly on JRuby before 9.0.0.0, so this 18 | release of Roda drops support for JRuby versions before 9.0.0.0. 19 | -------------------------------------------------------------------------------- /doc/release_notes/3.5.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A request_aref plugin has been added for configuring the behavior 4 | of the [] and []= request methods. These methods are deprecated 5 | in the current version of Rack, but Rack will only print a 6 | deprecation warning in verbose mode. With this plugin, you can 7 | choose to never warn, always warn, or raise an exception: 8 | 9 | # Don't emit a warning, allowing for the historical Rack 10 | # behavior 11 | plugin :request_aref, :allow 12 | 13 | # Always emit a warning when the method is called 14 | plugin :request_aref, :warn 15 | 16 | # Raise an exception if the method is called 17 | plugin :request_aref, :raise 18 | 19 | = Other Improvements 20 | 21 | * When using the content_for plugin and calling content_for with a 22 | block, convert the result of the block to a string before passing 23 | the result to Tilt. This can fix issues when the template class 24 | that Tilt uses does not handle non-String input. 25 | 26 | * When using the public plugin with the :gzip option, do not add the 27 | Content-Type or Content-Encoding headers if a 304 response is 28 | returned. 29 | 30 | * Add the spec/views/about directory to the gem, allowing the specs 31 | to run correctly using just the files in the gem. 32 | -------------------------------------------------------------------------------- /doc/release_notes/3.50.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An inject_erb plugin has been added, adding an inject_erb method 4 | that allows for injecting content directly into the template output 5 | for the template currently being rendered. This allows you to more 6 | easily wrap blocks in templates, by calling methods that accept 7 | template blocks and injecting content before and after the block. 8 | 9 | * A capture_erb plugin has been added, adding a capture_erb method 10 | for capturing a template block in an erb template and returning 11 | the content appended during the block as a string, instead of 12 | having the content of the template block be included directly into 13 | the template output. This can be combined with the inject_erb 14 | plugin to inject modified versions of captured blocks into template 15 | output. 16 | 17 | * The hash_routes plugin now allows calling hash_branch and hash_path 18 | without a block in order to remove the existing route handler. This 19 | is designed to be used with code reloading libraries, so that if a 20 | route file is deleted, the related hash branches/paths are also 21 | removed, without having to reload all route files. 22 | -------------------------------------------------------------------------------- /doc/release_notes/3.51.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The named_routes plugin now allows calling route without a block 4 | to remove the existing route handler. The multi_run plugin 5 | now allows calling run without an app to remove an existing handler. 6 | These changes are designed to better support code reloading 7 | libraries, so that if the related file is deleted, the related 8 | handlers are also removed, without having to reload the entire 9 | application. 10 | 11 | = Other Improvements 12 | 13 | * The error_handler plugin now avoids a method redefinition warning 14 | in verbose warning mode. 15 | 16 | = Other 17 | 18 | * Roda's primary discussion forum is now GitHub Discussions. The 19 | ruby-roda Google Group is still available for users who would 20 | prefer to use that instead. 21 | -------------------------------------------------------------------------------- /doc/release_notes/3.52.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The typecast_params plugin now supports a :date_parse_input_handler 4 | option that will be called with all input that will be passed to 5 | the date parsing methods. You can use this option to automatically 6 | truncate input, if that is perferable to raising an error (which is 7 | how recent versions of Ruby handle too-long input). 8 | 9 | = Other Improvements 10 | 11 | * The path helper methods added by the path plugin now support 12 | blocks that use keyword arguments on Ruby 3+. 13 | 14 | * The assets plugin now uses OpenSSL::Digest instead of Digest (if 15 | available) for calculating SRI digests. This is faster on Ruby 3+, 16 | where Digest no longer uses the faster OpenSSL::Digest automatically 17 | if available. 18 | 19 | * Roda.freeze now returns self when the multi_route plugin is used. 20 | This was broken (not returning self) starting in 3.48.0. 21 | -------------------------------------------------------------------------------- /doc/release_notes/3.53.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An additional_view_directories plugin has been added, which allows 4 | you to specify additional directories to look in for templates. 5 | If the template path does not exist when using the default view 6 | directory, then each additional view directory will be checked, 7 | returning the first path that exists: 8 | 9 | plugin :additional_view_directories, ['admin_views', 'public_views'] 10 | 11 | = Other Improvements 12 | 13 | * The indifferent_params plugin now avoids a deprecation warning when 14 | using the rack main branch, which will become Rack 3. 15 | -------------------------------------------------------------------------------- /doc/release_notes/3.55.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A :forward_response_headers option has been added to the middleware 4 | plugin, which uses the response headers added by the middleware 5 | as default response headers even if the middleware does not handle 6 | the response. Response headers set by the underlying application 7 | take precedence over response headers set by the middleware. 8 | 9 | * The render plugin view method now accepts a block and will pass the 10 | block to the underlying render method call. This is useful for 11 | rendering a template that yields inside of an existing layout. 12 | Previously, you had to nest render calls to do that. 13 | -------------------------------------------------------------------------------- /doc/release_notes/3.56.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * RodaRequest#http_version has been added for determining the HTTP 4 | version the request was submitted with. This will be a string 5 | such as "HTTP/1.0", "HTTP/1.1", "HTTP/2", etc. This will use the 6 | SERVER_PROTOCOL and HTTP_VERSION entries from the environment to 7 | determine which HTTP version is in use. 8 | 9 | * The status_handler method in the status_handler plugin now supports 10 | a :keep_headers option. The value for this option should be an 11 | array of header names to keep. All other headers are removed. The 12 | default behavior without the option is still to remove all headers. 13 | 14 | * A run_require_slash plugin has been added, which will skip 15 | dispatching to another rack application if the remaining path is not 16 | empty and does not start with a slash. 17 | 18 | = Other Improvements 19 | 20 | * The status_303 plugin will use 303 as the default redirect status 21 | for non-GET requests for HTTP/2 and higher HTTP versions. Previously, 22 | it only used 303 for HTTP/1.1. 23 | 24 | * The not_allowed plugin now overrides the r.root method to return 25 | 405 responses to non-GET requests to the root. 26 | 27 | * The not_allowed plugin no longer sets the body when returning 405 28 | responses using methods such as r.get and r.post. Previously, the 29 | body was unintentionally set to the same value as the Allow header. 30 | 31 | * When using the Rack master branch (what will become Rack 3), Roda 32 | only requires the parts of rack that it uses, instead of requiring 33 | rack and relying on autoload to load the parts of rack in use. 34 | -------------------------------------------------------------------------------- /doc/release_notes/3.57.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * hash_branches and hash_paths plugins have been split off from the 4 | hash_routes plugin, allowing you to use only those parts instead 5 | of all of hash_routes. 6 | 7 | The hash_branches plugin supports the hash_branch class method 8 | and r.hash_branches routing method. 9 | 10 | The hash_paths plugin supports the hash_path class method and 11 | r.hash_paths routing method. 12 | 13 | The hash_routes plugin functions as it did previously by 14 | requiring the hash_branches and hash_paths plugins. It adds 15 | the hash_routes DSL and r.hash_routes routing method. 16 | 17 | * A hash_branch_view_subdir has been added. It builds on the 18 | view_options plugin and new hash_branches plugin, automatically 19 | appending a view subdirectory for each successful hash branch. 20 | This can DRY up code that uses a separate view subdirectory for 21 | each branch. 22 | 23 | = Other Improvements 24 | 25 | * Unprintable characters are now hex escaped in the output of the 26 | common_logger plugin. This can protect users who use software 27 | that respects shell escape sequences to view the logs. 28 | 29 | = Backwards Compatibility 30 | 31 | * The static_routing plugin now depends on the hash_paths plugin 32 | instead of the hash_routes plugin, so you will need to update 33 | your application to explicitly load the hash_routes plugin if 34 | you were relying on static_routing to implicitly load it. 35 | -------------------------------------------------------------------------------- /doc/release_notes/3.58.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A filter_common_logger plugin has been added, allowing you to skip 4 | logging of certain requests in the common_logger plugin. This 5 | allows you to only log requests for certain paths, or only log 6 | requests for certain types of responses. 7 | 8 | = Other Improvements 9 | 10 | * The heartbeat plugin is now compatible with recent changes in the 11 | rack master branch (what will be rack 3). 12 | 13 | * The exception_page plugin will now use Exception#detailed_message 14 | on Ruby 3.2+, preserving the did_you_mean and error_highlight 15 | information. Additionally, the display of exception messages 16 | has been improved. 17 | -------------------------------------------------------------------------------- /doc/release_notes/3.59.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An additional_render_engines plugin has been added, for considering 4 | multiple render engines for templates. If the template path does not 5 | exist for the default render engine, then each additional render 6 | engine will be checked, returning the first path that exists: 7 | 8 | plugin :additional_render_engines, ['haml', 'str'] 9 | 10 | This is similar to the additional_view_directories plugin added in 11 | 3.53.0. Both plugins can be used if you want to consider multiple 12 | view directories and multiple render engines. 13 | 14 | = Other Improvements 15 | 16 | * A typo in a private method name in the delete_empty_headers plugin 17 | has been fixed. 18 | -------------------------------------------------------------------------------- /doc/release_notes/3.6.0.txt: -------------------------------------------------------------------------------- 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. 22 | -------------------------------------------------------------------------------- /doc/release_notes/3.61.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The typecast_params plugin now limits input bytesize for integer, 4 | float, and date/time typecasts. If the input is over the allowed 5 | bytesize, typecasting will fail. This prevents issues with trying 6 | to typecast arbitrarily large input. 7 | 8 | * The default Integer class matcher now limits integer segments to 9 | 100 characters by default, also to prevent issues with typecasting 10 | arbitrarily large input. Segments larger than 100 characters will 11 | no longer be matched by the Integer class matcher. 12 | 13 | = Backwards Compatibility 14 | 15 | * If the input bytesize limits in the typecast_params plugin cause 16 | issues in your application, you can use the :skip_bytesize_checking 17 | option when loading the plugin to disable the checks. 18 | 19 | * If the default Integer class matcher limit causes problems in your 20 | application, you can use the class_matchers plugin to override the 21 | matcher to not use a limit: 22 | 23 | plugin :class_matchers 24 | class_matcher(Integer, /(\d+)/){|a| [a.to_i]} 25 | -------------------------------------------------------------------------------- /doc/release_notes/3.63.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An autoload_hash_branches plugin has been added for autoloading 4 | route files for each hash branch, instead of requiring the route 5 | files be loaded up front. For example, to automatically load a 6 | route file for a hash branch on the first request to that branch: 7 | 8 | plugin :autoload_hash_branches 9 | autoload_hash_branch('branch_name', '/path/to/file') 10 | autoload_hash_branch('namespace', 'branch_name', '/path/to/file') 11 | 12 | The route file loaded should define the expected hash branch. 13 | 14 | It is common to have route files stored in a directory, with the 15 | file name matching the branch name. In that case, you can set 16 | autoloading for all route files in a given directory: 17 | 18 | plugin :autoload_hash_branches 19 | autoload_hash_branch_dir('/path/to/dir') 20 | autoload_hash_branch_dir('namespace', '/path/to/dir') 21 | 22 | Note that autoloading hash branches does not work if the application 23 | is frozen. This plugin should only be used in development mode for 24 | faster startup, or when running tests on a subset of the application 25 | in order to avoid loading parts of the application unrelated to what 26 | is being tested. 27 | 28 | * The mailer plugin now supports a :terminal plugin option to make 29 | the r.mail method force a terminal match, similar to how r.get 30 | and other HTTP verb methods work in standard Roda. This behavior 31 | will become the default in Roda 4. 32 | 33 | = Other Improvements 34 | 35 | * The mailer plugin now correctly sets the content_type of the body 36 | for emails with attachments when using mail 2.8.0+. 37 | -------------------------------------------------------------------------------- /doc/release_notes/3.64.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An erb_h plugin has been added for faster HTML escaping using 4 | erb/escape. erb 4 added erb/escape and it is included in Ruby 3.2. 5 | 6 | The erb_h plugin is added as a separate plugin because it changes 7 | the behavior of the h method. The h method added by the h plugin 8 | will always return a new string, but the h method added by the 9 | erb_h plugin will return the argument if the argument is a 10 | string that does not need escaping. By avoiding unnecessary 11 | string allocations, use of the erb_h plugin can speed up HTML 12 | escaping. 13 | 14 | = Other Improvements 15 | 16 | * The autoload_hash_branches plugin added in Roda 3.63.0 will now 17 | eagerly load the hash branches when freezing the application, 18 | allowing the application to continue to work after being frozen. 19 | Additionally, file paths for the hash branches will now be 20 | automatically expanded, allowing the use of relative file paths. 21 | 22 | = Backwards Compatibility 23 | 24 | * The expanding of file paths in the autoload_hash_branches plugin 25 | can break applications that were providing relative paths and 26 | expecting them to be looked up using the Ruby load path. 27 | -------------------------------------------------------------------------------- /doc/release_notes/3.65.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An autoload_named_routes plugin has been added for autoloading files 4 | for a named route setup by the named_routes plugin when there is a 5 | request for that route. 6 | 7 | = Other Improvements 8 | 9 | * The path method in the path plugin now supports a :class_name option. 10 | You can set this option to true and use a class name String/Symbol 11 | to register paths for classes without referencing the related class, 12 | useful when autoloading the class. 13 | -------------------------------------------------------------------------------- /doc/release_notes/3.66.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A render_coverage plugin has been added, which will cause compiled 4 | template code to be saved to a folder and loaded using load instead 5 | of eval. This allows for coverage to work for the compiled template 6 | code in Ruby versions before 3.2. It can also allow for verbose 7 | syntax warnings in compiled template code (ignored by eval), and 8 | can also be useful for static analysis of compiled template code. 9 | This plugin requires tilt 2.1+. 10 | 11 | * The exception_page plugin now supports exception_page_{css,js} 12 | instance methods for overriding the CSS and JavaScript on the 13 | generated exception page. 14 | 15 | = Other Improvements 16 | 17 | * Using inline templates (render/view :inline option) no longer keeps 18 | a reference to the Roda instance that caches the template. 19 | 20 | = Backwards Compatibility 21 | 22 | * The Render::TemplateMtimeWrapper API has changed. Any external 23 | use of this class needs to be updated. 24 | -------------------------------------------------------------------------------- /doc/release_notes/3.67.0.txt: -------------------------------------------------------------------------------- 1 | = New Feature 2 | 3 | * A custom_block_results plugin has been added for custom handling 4 | of block results. This allows routing blocks to return 5 | arbitrary objects instead of just String, nil, and false, and 6 | to have custom handling for them. For example, if you want to 7 | be able to have your routing blocks return the status code to use, 8 | you could do: 9 | 10 | plugin :custom_block_results 11 | 12 | handle_block_result Integer do |result| 13 | response.status_code = result 14 | end 15 | 16 | route do |r| 17 | 200 18 | end 19 | 20 | While the expected use of the handle_block_result method is with 21 | class arguments, you can use any argument that implements an 22 | appropriate === method. 23 | 24 | The symbol_views and json plugins, which support additional block 25 | results, now use the custom_block_results plugin internally. 26 | -------------------------------------------------------------------------------- /doc/release_notes/3.68.0.txt: -------------------------------------------------------------------------------- 1 | = New Feature 2 | 3 | * Roda.run in the multi_run plugin now accepts blocks, to allow 4 | autoloading of apps to dispatch to: 5 | 6 | class App < Roda 7 | plugin :multi_run 8 | 9 | run("other_app"){OtherApp} 10 | 11 | route do |r| 12 | r.multi_run 13 | end 14 | end 15 | 16 | With the above example, the block is not evaluated until a 17 | request for the /other_app branch is received. If OtherApp is 18 | autoloaded, this can speed up application startup and partial 19 | testing. When freezing the application (for production use), 20 | the block is eagerly loaded, so that requests to the 21 | /other_app branch do not call the block on every request. 22 | -------------------------------------------------------------------------------- /doc/release_notes/3.69.0.txt: -------------------------------------------------------------------------------- 1 | = New Feature 2 | 3 | * The symbol_matcher method in the symbol_matchers plugin now 4 | supports a block to allow for type conversion of matched 5 | segments: 6 | 7 | symbol_matcher(:date, /(\d\d\d\d)-(\d\d)-(\d\d)/) do |y, m, d| 8 | [Date.new(y.to_i, m.to_i, d.to_i)] 9 | end 10 | 11 | route do |r| 12 | r.on :date do |date| 13 | # date is an instance of Date 14 | end 15 | end 16 | 17 | As shown above, the block should return an array of objects to yield 18 | to the match block. 19 | 20 | If you have a segment match the passed regexp, but decide during block 21 | processing that you do not want to treat it as a match, you can have the 22 | block return nil or false. This is useful if you want to make sure you 23 | are using valid data: 24 | 25 | symbol_matcher(:date, /(\d\d\d\d)-(\d\d)-(\d\d)/) do |y, m, d| 26 | y = y.to_i 27 | m = m.to_i 28 | d = d.to_i 29 | [Date.new(y, m, d)] if Date.valid_date?(y, m, d) 30 | end 31 | 32 | When providing a block when using the symbol_matchers method, that 33 | symbol may not work with the params_capturing plugin. 34 | -------------------------------------------------------------------------------- /doc/release_notes/3.70.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A plain_hash_response_headers plugin has been added. On Rack 3, 4 | this changes Roda to use a plain hash for response headers (as it 5 | does on Rack 2), instead of using Rack::Headers (the default on 6 | Rack 3). For a minimal app, using this plugin can almost double 7 | the performance on Rack 3. Before using this plugin, you should 8 | make sure that all response headers set explictly in your 9 | application are already lower-case. 10 | 11 | = Improvements 12 | 13 | * Roda now natively uses lower-case for all response headers set 14 | implicitly when using Rack 3. Previously, Roda used mixed-case 15 | response headers and had Rack::Headers handle the conversion to 16 | lower-case (Rack 3 requires lower-case response headers). Note 17 | that Rack::Headers is still used for response headers by default 18 | on Rack 3, as applications may not have converted to using 19 | lower-case response headers. 20 | -------------------------------------------------------------------------------- /doc/release_notes/3.71.0.txt: -------------------------------------------------------------------------------- 1 | = New Feature 2 | 3 | * A match_hook_args plugin has been added. This is similar to the 4 | existing match_hook plugin, but passes through the matchers and 5 | block arguments (values yielded to the match block). Example: 6 | 7 | plugin :match_hook_args 8 | 9 | add_match_hook do |matchers, block_args| 10 | logger.debug("matchers: #{matchers.inspect}. #{block_args.inspect} yielded.") 11 | end 12 | 13 | # Term is an implicit matcher used for terminating matches, and 14 | # will be included in the array of matchers yielded to the match hook 15 | # if a terminating match is used. 16 | term = self.class::RodaRequest::TERM 17 | 18 | route do |r| 19 | r.root do 20 | # for a request for / 21 | # matchers: nil, block_args: nil 22 | end 23 | 24 | r.on 'a', ['b', 'c'], Integer do |segment, id| 25 | # for a request for /a/b/1 26 | # matchers: ["a", ["b", "c"], Integer], block_args: ["b", 1] 27 | end 28 | 29 | r.get 'd' do 30 | # for a request for /d 31 | # matchers: ["d", term], block_args: [] 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /doc/release_notes/3.73.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The middleware plugin now accepts a :next_if_not_found option. 4 | This allows the middleware plugin to pass the request to the next 5 | application if the current application handles the request but 6 | ends up calling the not_found handler. With the following 7 | middleware: 8 | 9 | class Mid < Roda 10 | plugin :middleware 11 | 12 | route do |r| 13 | r.on "foo" do 14 | r.get "bar" do 15 | 'bar' 16 | end 17 | end 18 | end 19 | end 20 | 21 | Requests for /x would be forwarded to the next application, since 22 | the application doesn't handle the request, but requests for /foo/x 23 | would not be, because the middleware is partially handling the 24 | request in the r.on "foo" block. With the :next_if_not_found 25 | option, only requests for /foo/bar would be handled by the 26 | middleware, and all other requests would be forwarded to the next 27 | application. 28 | 29 | = Other Improvements 30 | 31 | * The sessions and route_csrf plugins no longer depend on the base64 32 | library. base64 will be removed from Ruby's standard library 33 | starting in Ruby 3.4. 34 | -------------------------------------------------------------------------------- /doc/release_notes/3.74.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A redirect_http_to_https plugin has been added, redirecting HTTP 4 | requests to the same path on an HTTPS site. Using the routing tree, 5 | you can control where to do the redirection, which allows you to 6 | easily have part of your site accessible via HTTP, with sensitive 7 | sections requiring HTTPS: 8 | 9 | plugin :redirect_http_to_https 10 | 11 | route do |r| 12 | # routes available via both HTTP and HTTPS 13 | r.redirect_http_to_https 14 | # routes available only via HTTPS 15 | end 16 | 17 | If you want to redirect to HTTPS for all routes in the routing tree, you 18 | can have r.redirect_http_to_https as the very first method call in the 19 | routing tree. Note that in Roda it is possible to handle routing before 20 | the normal routing tree using before hooks. The static_routing and 21 | heartbeat plugins use this feature. If you would like to handle routes 22 | before the normal routing tree, you can setup a before hook: 23 | 24 | plugin :hooks 25 | 26 | before do 27 | request.redirect_http_to_https 28 | end 29 | -------------------------------------------------------------------------------- /doc/release_notes/3.75.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A cookie_flags plugin has been added, for overriding, warning, or 4 | raising for incorrect cookie flags. The plugin by default checks 5 | whether the secure, httponly, and samesite=strict flags are set. 6 | The default behavior is to add the appropriate flags if they are 7 | not set, and change the samesite flag to strict if it is set to 8 | something else. You can configure the flag checking behavior 9 | via the :httponly, :same_site, and :secure options. 10 | 11 | You can configure the action the plugin takes via the :action option. 12 | The default action is to modify the flags, but the :action option can 13 | be set to :raise, :warn, or :warn_and_modify to override the behavior. 14 | 15 | The recommended way to use the plugin is to use it during testing, 16 | and specify action: :raise, so you can catch places where cookies 17 | are set with the wrong flags. Then you can fix those places to 18 | use the correct flags, which is better than relying on the plugin 19 | at runtime in production to fix incorrect flags. 20 | -------------------------------------------------------------------------------- /doc/release_notes/3.76.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A break plugin has been added, allowing you to use break from 4 | inside a routing block and continue routing after the block. This 5 | offers the same feature as the pass plugin, but using the standard 6 | break keyword instead of the r.pass method. 7 | 8 | * The error_mail and error_email features now both accept a :filter 9 | plugin option. The value should respond to call with two arguments. 10 | The first arguments is the key, and the second is the value, and 11 | should return a truthy value if the value should be filtered. This 12 | will be used for filtering parameter values, ENV values, and session 13 | values in the generated emails. 14 | 15 | = Other Improvements 16 | 17 | * On Ruby 3.3+, the middleware plugin sets a temporary class name for 18 | the created middleware, based on the class name of the Roda app. 19 | -------------------------------------------------------------------------------- /doc/release_notes/3.77.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The route_csrf plugin now supports formaction/formmethod attributes 4 | in forms. A csrf_formaction_tag method has been added for creating 5 | a hidden input for a particular path and method. When a form is 6 | submitted, the check_csrf! method will fix check for a path-specific 7 | csrf token (set by the hidden tag added by the csrf_formaction_tag 8 | method), before checking for the default csrf token. 9 | -------------------------------------------------------------------------------- /doc/release_notes/3.8.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The convert_each! method in the typecast_params plugin now 4 | accepts a Proc or Method value for the :keys option. The proc 5 | or method is called with the current array or hash that 6 | typecast params is operating on, and should return an 7 | array of keys to use for the conversion. 8 | 9 | * The convert_each! method in the typecast_params plugin will 10 | now automatically handle hashes with keys from '0'..'N', 11 | without a :keys option being provided. 12 | 13 | This makes it possible to handle parameter names such as 14 | foo[0][bar], foo[0][baz], foo[1][bar], and foo[1][baz], if you 15 | want to avoid the issues related to rack's issues when parsing 16 | array parameters. 17 | 18 | = Other Improvements 19 | 20 | * The Roda::RodaVersionNumber constant has been added for easier 21 | version comparisons. It is 30080 for version 3.8.0. 22 | 23 | = Backwards Compatibility 24 | 25 | * When an unsupported type is given as value of the :keys option 26 | to the convert_each! method in the typecast_params plugin, a 27 | ProgrammerError exception is now raised. 28 | -------------------------------------------------------------------------------- /doc/release_notes/3.80.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The hmac_paths plugin now supports a :namespace option for both hmac_path and 4 | r.hmac_path. The :namespace option makes the generated HMAC values unique 5 | per namespace, allowing easy use of per user/group HMAC paths. This can 6 | be useful if the same path will show different information to different 7 | users/groups, and you want to prevent path enumeration for each user/group 8 | (not allow paths enumerated by one user/group to be valid for a different 9 | user/group). Example: 10 | 11 | hmac_path('/widget/1', namespace: '1') 12 | # => "/3793ac2a72ea399c40cbd63f154d19f0fe34cdf8d347772134c506a0b756d590/n/widget/1" 13 | 14 | hmac_path('/widget/1', namespace: '2') 15 | # => "/0e1e748860d4fd17fe9b7c8259b1e26996502c38e465f802c2c9a0a13000087c/n/widget/1" 16 | 17 | The HMAC path created with namespace: '1' will only be valid when calling 18 | r.hmac_path with namespace: '1' (similar for namespace: '2'). 19 | 20 | It is expected that the most common use of the :namespace option is to 21 | reference session values, so the value of each path depends on the logged in 22 | user. You can use the :namespace_session_key plugin option to set the 23 | default namespace for both hmac_path and r.hmac_path: 24 | 25 | plugin :hmac_paths, secret: 'some-secret-value-with-at-least-32-bytes', 26 | namespace_session_key: 'account_id' 27 | 28 | This will use session['account_id'] (converted to a string) as the namespace 29 | for both hmac_path and r.hmac_path, unless a specific :namespace option is 30 | given, making it simple to implement per user/group HMAC paths across an 31 | application. 32 | -------------------------------------------------------------------------------- /doc/release_notes/3.81.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The hmac_paths plugin now supports :until and :seconds options for 4 | hmac_path, to create a path that is only valid for a specific amount of 5 | time. :until sets a specific time that the path will be valid until, 6 | and :seconds makes the path only valid for the given number of seconds. 7 | 8 | hmac_path('/widget/1', until: Time.utc(2100)) 9 | # => "/dc8b6e56e4cbe7815df7880d42f0e02956b2e4c49881b6060ceb0e49745a540d/t/4102444800/widget/1" 10 | 11 | Requests for the path after the given time will not be matched by 12 | r.hmac_path. 13 | 14 | = Other Improvements 15 | 16 | * The early_hints plugin now correctly follows the Rack 3 SPEC when 17 | using Rack 3. This was not caught previously because Rack only 18 | added official support for early_hints in the last month. 19 | 20 | * Ruby 3.4 backtraces are now parsed correctly in the exception_page 21 | plugin. 22 | 23 | * Some plugins that accept a block no longer issue an unused block 24 | warning on Ruby 3.4. 25 | -------------------------------------------------------------------------------- /doc/release_notes/3.83.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An assume_ssl plugin has been added. This plugin is designed for 4 | cases where the application is being fronted by an SSL-terminating 5 | reverse proxy that does not set the X-Forwarded-Proto or similar 6 | header to indicate it is forwarding an SSL request. 7 | -------------------------------------------------------------------------------- /doc/release_notes/3.84.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An hsts plugin has been added to easily add an appropriate 4 | Strict-Transport-Security header: 5 | 6 | plugin :hsts 7 | # Strict-Transport-Security: max-age=63072000; includeSubDomains 8 | 9 | plugin :hsts, preload: true 10 | # Strict-Transport-Security: max-age=63072000; includeSubDomains; preload 11 | 12 | = Other Improvements 13 | 14 | * The gem size has been reduced 25% by removing documentation. 15 | -------------------------------------------------------------------------------- /doc/release_notes/3.86.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * A conditional_sessions plugin has been added. This allows you to 4 | only support sessions for a subset of the application's requests. 5 | You pass a block when loading the plugin, and sessions are only 6 | supported if the block returns truthy. The block is evaluated 7 | in request scope. 8 | 9 | As an example, if you do not want to support sessions for request 10 | paths starting with /static, you could use: 11 | 12 | plugin :conditional_sessions, secret: ENV["SECRET"] do 13 | !path_info.start_with?('/static') 14 | end 15 | 16 | With this example, if the request path starts with /static: 17 | 18 | * The request methods +session+, +session_created_at+, and 19 | +session_updated_at+ all raise an exception. 20 | * The request +persist_session+ and route scope +clear_session+ 21 | methods do nothing and return nil. 22 | 23 | Options passed when loading the plugin are passed to the sessions 24 | plugin. 25 | 26 | * In the content_security_policy plugin, you can now call 27 | response.skip_content_security_policy! to skip the setting of the 28 | response header. 29 | 30 | * In the permissions_policy plugin, you can now call 31 | response.skip_permissions_policy! to skip the setting of the 32 | response header. 33 | 34 | = Other Improvements 35 | 36 | * When using the autoload_hash_branches and/or autoload_named_routes 37 | plugins, Roda.freeze now works correctly if the Roda class is 38 | already frozen. 39 | -------------------------------------------------------------------------------- /doc/release_notes/3.89.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The render plugin now supports an :assume_fixed_locals option, 4 | which allows for better caching when all templates use fixed 5 | locals, by using a simplified cache key, and avoiding duplicate 6 | cache entries for templates rendered both with and without locals. 7 | 8 | Additionally, when this plugin option is set, calling template 9 | methods is now faster if the following are true: 10 | 11 | * The application is frozen 12 | * Template caching is enabled 13 | * Ruby version is 3+ 14 | 15 | * A part plugin has been added, which simplifies rendering a template 16 | with locals: 17 | 18 | # render plugin 19 | render(:template, locals: {foo: 'bar'}) 20 | 21 | # part plugin 22 | part(:template, foo: 'bar') 23 | 24 | In addition to offering a nicer API if you only need to provide 25 | locals, the part method can also be faster if all of the 26 | following are true: 27 | 28 | * The application is frozen 29 | * The :assume_fixed_locals render plugin option is set 30 | * Template caching is enabled 31 | * Ruby version is 3+ (even faster on Ruby 3.4+) 32 | 33 | = Other Improvements 34 | 35 | * The mailer plugin's mail and sendmail class methods now support 36 | keyword arguments and pass them as keywords to the r.mail blocks 37 | in the routing tree. 38 | -------------------------------------------------------------------------------- /doc/release_notes/3.90.0.txt: -------------------------------------------------------------------------------- 1 | = Improvements 2 | 3 | * The send_file method in the sinatra_helpers plugin now returns 4 | a response body that implements to_path. 5 | 6 | * Roda now sets a temporary name for the remaining anonymous 7 | modules and classes on Ruby 3.3+. 8 | 9 | * The common_logger plugin now escapes embedded newlines. These 10 | should only be present if the server is broken and including 11 | newlines in things it shouldn't. 12 | -------------------------------------------------------------------------------- /doc/release_notes/3.91.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * The render_each method in the render_each plugin now accepts a 4 | block. If passed a block, instead of returning a concatenation 5 | of the rendered template output, it yields each rendered template 6 | output, and returns nil. This allows for use in the case where you 7 | want to wrap the template output: 8 | 9 | <% render_each([1,2,3], :foo) do |text| %> 10 |
<%= text %>
11 | <% end %> 12 | 13 | If can also be used to reduce memory usage even in the case where 14 | you are not wrapping template output. Instead of: 15 | 16 | <%= render_each([1,2,3], :foo) %> 17 | 18 | You can do: 19 | 20 | <% render_each([1,2,3], :foo) %><%= body %><% end %> 21 | 22 | This will avoid building a potentially large unnecessary intermediate 23 | string. 24 | 25 | * The capture_erb plugin now supports a returns: :buffer method and 26 | plugin option. When this option is provided, the capture_erb method 27 | returns the buffer instead of the return value of the block passed 28 | to it. This better handles cases where the template ends in a 29 | conditional: 30 | 31 | <% value = capture_erb do %> 32 | Some content here. 33 | <% if something %> 34 | Some more content here. 35 | <% end %> 36 | <% end %> 37 | -------------------------------------------------------------------------------- /doc/release_notes/3.92.0.txt: -------------------------------------------------------------------------------- 1 | = New Features 2 | 3 | * An each_part plugin has been added, offering a simpler method for 4 | using render_each with locals: 5 | 6 | # With render_each: 7 | render_each(array_of_foos, :foo, locals: {bar: 1}) 8 | 9 | # With each_part: 10 | each_part(array_of_foos, :foo, bar: 1) 11 | 12 | The each_part provides similar benefits to the render_each plugin 13 | that the part plugin provides to the render plugin. The each_part 14 | method has been optimized to work with the render plugin's 15 | :assume_fixed_locals option. 16 | 17 | = Other Improvements 18 | 19 | * The render_each plugin has been optimized to work with the render 20 | plugin's :assume_fixed_locals option. 21 | -------------------------------------------------------------------------------- /lib/roda/cache.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | require "thread" 4 | 5 | class Roda 6 | # A thread safe cache class, offering only #[] and #[]= methods, 7 | # each protected by a mutex. 8 | class RodaCache 9 | # Create a new thread safe cache. 10 | def initialize 11 | @mutex = Mutex.new 12 | @hash = {} 13 | end 14 | 15 | # Make getting value from underlying hash thread safe. 16 | def [](key) 17 | @mutex.synchronize{@hash[key]} 18 | end 19 | 20 | # Make setting value in underlying hash thread safe. 21 | def []=(key, value) 22 | @mutex.synchronize{@hash[key] = value} 23 | end 24 | 25 | # Return the frozen internal hash. The internal hash can then 26 | # be accessed directly since it is frozen and there are no 27 | # thread safety issues. 28 | def freeze 29 | @hash.freeze 30 | end 31 | 32 | private 33 | 34 | # Create a copy of the cache with a separate mutex. 35 | def initialize_copy(other) 36 | @mutex = Mutex.new 37 | other.instance_variable_get(:@mutex).synchronize do 38 | @hash = other.instance_variable_get(:@hash).dup 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/roda/plugins/_after_hook.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | require_relative 'error_handler' 4 | 5 | # 6 | class Roda 7 | module RodaPlugins 8 | # RODA4: Remove 9 | register_plugin(:_after_hook, ErrorHandler) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/roda/plugins/_base64.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # 4 | class Roda 5 | module RodaPlugins 6 | module Base64_ 7 | class << self 8 | if RUBY_VERSION >= '2.4' 9 | def decode64(str) 10 | str.unpack1("m0") 11 | end 12 | # :nocov: 13 | else 14 | def decode64(str) 15 | str.unpack("m0")[0] 16 | end 17 | # :nocov: 18 | end 19 | 20 | def urlsafe_encode64(bin) 21 | str = [bin].pack("m0") 22 | str.tr!("+/", "-_") 23 | str 24 | end 25 | 26 | def urlsafe_decode64(str) 27 | decode64(str.tr("-_", "+/")) 28 | end 29 | end 30 | end 31 | 32 | register_plugin(:_base64, Base64_) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/roda/plugins/_before_hook.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # 4 | class Roda 5 | module RodaPlugins 6 | # Internal before hook module, not for external use. 7 | # Allows for plugins to configure the order in which 8 | # before processing is done by using _roda_before_* 9 | # private instance methods that are called in sorted order. 10 | # Loaded automatically by the base library if any _roda_before_* 11 | # methods are defined. 12 | module BeforeHook # :nodoc: 13 | module InstanceMethods 14 | # Run internal before hooks - Old Dispatch API. 15 | def call(&block) 16 | # RODA4: Remove 17 | super do 18 | _roda_before 19 | instance_exec(@_request, &block) # call Fallback 20 | end 21 | end 22 | 23 | # Run internal before hooks before running the main 24 | # roda route. 25 | def _roda_run_main_route(r) 26 | _roda_before 27 | super 28 | end 29 | 30 | private 31 | 32 | # Default empty implementation of _roda_before, usually 33 | # overridden by Roda.def_roda_before. 34 | def _roda_before 35 | end 36 | end 37 | end 38 | 39 | register_plugin(:_before_hook, BeforeHook) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/roda/plugins/_symbol_regexp_matchers.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # 4 | class Roda 5 | module RodaPlugins 6 | # The _symbol_regexp_matchers plugin is designed for internal use by other plugins, 7 | # for the historical behavior of a symbol matching an arbitrary segment by default 8 | # using a regexp. 9 | module SymbolRegexpMatchers 10 | module RequestMethods 11 | private 12 | 13 | # The regular expression to use for matching symbols. By default, any non-empty 14 | # segment matches. 15 | def _match_symbol_regexp(s) 16 | "([^\\/]+)" 17 | end 18 | end 19 | end 20 | 21 | register_plugin(:_symbol_regexp_matchers, SymbolRegexpMatchers) 22 | end 23 | end 24 | 25 | -------------------------------------------------------------------------------- /lib/roda/plugins/all_verbs.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # 4 | class Roda 5 | module RodaPlugins 6 | # The all_verbs plugin adds methods for http verbs other than 7 | # get and post. The following verbs are added, assuming 8 | # rack handles them: delete, head, options, link, patch, put, 9 | # trace, unlink. 10 | # 11 | # These methods operate just like Roda's default get and post 12 | # methods, so using them without any arguments just checks for 13 | # the request method, while using them with any arguments also 14 | # checks that the arguments match the full path. 15 | # 16 | # Example: 17 | # 18 | # plugin :all_verbs 19 | # 20 | # route do |r| 21 | # r.delete do 22 | # # Handle DELETE 23 | # end 24 | # r.put do 25 | # # Handle PUT 26 | # end 27 | # r.patch do 28 | # # Handle PATCH 29 | # end 30 | # end 31 | # 32 | # The verb methods are defined via metaprogramming, so there 33 | # isn't documentation for the individual methods created. 34 | module AllVerbs 35 | module RequestMethods 36 | %w'delete head options link patch put trace unlink'.each do |verb| 37 | if ::Rack::Request.method_defined?("#{verb}?") 38 | class_eval(<<-END, __FILE__, __LINE__+1) 39 | def #{verb}(*args, &block) 40 | _verb(args, &block) if #{verb}? 41 | end 42 | END 43 | end 44 | end 45 | end 46 | end 47 | 48 | register_plugin(:all_verbs, AllVerbs) 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/roda/plugins/assume_ssl.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # 4 | class Roda 5 | module RodaPlugins 6 | # The assume_ssl plugin makes the request ssl? method always return 7 | # true. This is useful when using an SSL-terminating reverse proxy 8 | # that doesn't set the X-Forwarded-Proto or similar header to notify 9 | # Rack that it is forwarding an SSL request. 10 | # 11 | # The sessions and sinatra_helpers plugins that ship with Roda both 12 | # use the ssl? method internally and can be affected by use of the 13 | # plugin. It's recommended that you use this plugin if you are 14 | # using either plugin and an SSL-terminating proxy as described above. 15 | # 16 | # plugin :assume_ssl 17 | module AssumeSSL 18 | module RequestMethods 19 | # Assume all requests are protected by SSL. 20 | def ssl? 21 | true 22 | end 23 | end 24 | end 25 | 26 | register_plugin(:assume_ssl, AssumeSSL) 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/roda/plugins/break.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # 4 | class Roda 5 | module RodaPlugins 6 | # The break plugin supports calling break inside a match block, to 7 | # return from the block and continue in the routing tree, restoring 8 | # the remaining path so that future matchers operating on the path 9 | # operate as expected. 10 | # 11 | # plugin :break 12 | # 13 | # route do |r| 14 | # r.on "foo", :bar do |bar| 15 | # break if bar == 'baz' 16 | # "/foo/#{bar} (not baz)" 17 | # end 18 | # 19 | # r.on "foo/baz" do 20 | # "/foo/baz" 21 | # end 22 | # end 23 | # 24 | # This provides the same basic feature as the pass plugin, but 25 | # uses Ruby's standard control flow primative instead of a 26 | # separate method. 27 | module Break 28 | module RequestMethods 29 | private 30 | 31 | # Handle break inside match blocks, restoring remaining path. 32 | def if_match(_) 33 | rp = @remaining_path 34 | super 35 | ensure 36 | @remaining_path = rp 37 | end 38 | end 39 | end 40 | 41 | register_plugin(:break, Break) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/roda/plugins/default_status.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # 4 | class Roda 5 | module RodaPlugins 6 | # The default_status plugin accepts a block which should 7 | # return a response status integer. This integer will be used as 8 | # the default response status (usually 200) if the body has been 9 | # written to, and you have not explicitly set a response status. 10 | # 11 | # Example: 12 | # 13 | # # Use 201 default response status for all requests 14 | # plugin :default_status do 15 | # 201 16 | # end 17 | module DefaultStatus 18 | def self.configure(app, &block) 19 | raise RodaError, "default_status plugin requires a block" unless block 20 | if check_arity = app.opts.fetch(:check_arity, true) 21 | unless block.arity == 0 22 | if check_arity == :warn 23 | RodaPlugins.warn "Arity mismatch in block passed to plugin :default_status. Expected Arity 0, but arguments required for #{block.inspect}" 24 | end 25 | b = block 26 | block = lambda{instance_exec(&b)} # Fallback 27 | end 28 | end 29 | app::RodaResponse.send(:define_method, :default_status, &block) 30 | end 31 | end 32 | 33 | register_plugin(:default_status, DefaultStatus) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/roda/plugins/delay_build.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # 4 | class Roda 5 | module RodaPlugins 6 | module DelayBuild 7 | module ClassMethods 8 | # No-op for backwards compatibility 9 | def build! 10 | end 11 | end 12 | end 13 | 14 | # RODA4: Remove plugin 15 | # Only available for backwards compatibility, no longer needed 16 | register_plugin(:delay_build, DelayBuild) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/roda/plugins/delete_empty_headers.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # 4 | class Roda 5 | module RodaPlugins 6 | # The delete_empty_headers plugin deletes any headers whose 7 | # value is set to the empty string. Because of how default headers are 8 | # set in Roda, if you have a default header but don't want 9 | # to set it for a specific request, you need to use this plugin 10 | # and set the header value to the empty string, and Roda will automatically 11 | # delete the header. 12 | module DeleteEmptyHeaders 13 | module ResponseMethods 14 | # Delete any empty headers when calling finish 15 | def finish 16 | delete_empty_headers(super) 17 | end 18 | 19 | # Delete any empty headers when calling finish_with_body 20 | def finish_with_body(_) 21 | delete_empty_headers(super) 22 | end 23 | 24 | private 25 | 26 | # Delete any empty headers from response 27 | def delete_empty_headers(res) 28 | res[1].delete_if{|_, v| v.is_a?(String) && v.empty?} 29 | res 30 | end 31 | end 32 | end 33 | 34 | register_plugin(:delete_empty_headers, DeleteEmptyHeaders) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/roda/plugins/direct_call.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # 4 | class Roda 5 | module RodaPlugins 6 | # The direct_call plugin makes the call class method skip the middleware stack 7 | # (app.call will still call the middleware). 8 | # This can be used as an optimization, as the Roda class itself can be used 9 | # as the callable, which is faster than using a lambda. 10 | module DirectCall 11 | def self.configure(app) 12 | app.send(:build_rack_app) 13 | end 14 | 15 | module ClassMethods 16 | # Call the application without middlware. 17 | def call(env) 18 | new(env)._roda_handle_main_route 19 | end 20 | 21 | private 22 | 23 | # If new_api is true, use the receiver as the base rack app for better 24 | # performance. 25 | def base_rack_app_callable(new_api=true) 26 | if new_api 27 | self 28 | else 29 | super 30 | end 31 | end 32 | end 33 | end 34 | 35 | register_plugin(:direct_call, DirectCall) 36 | end 37 | end 38 | 39 | -------------------------------------------------------------------------------- /lib/roda/plugins/disallow_file_uploads.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | raise LoadError, "disallow_file_uploads plugin not supported on Rack <1.6" if Rack.release < '1.6' 4 | 5 | # 6 | class Roda 7 | module RodaPlugins 8 | # The disallow_file_uploads plugin raises a Roda::RodaPlugins::DisallowFileUploads::Error 9 | # if there is an attempt to upload a file. This plugin is useful for applications where 10 | # multipart file uploads are not expected and you want to remove the ability for rack 11 | # to create temporary files. Example: 12 | # 13 | # plugin :disallow_file_uploads 14 | # 15 | # This plugin is only supported on Rack 1.6+. This plugin does not technically 16 | # block users from uploading files, it only blocks the parsing of request bodies containing 17 | # multipart file uploads. So if you do not call +r.POST+ (or something that calls it such as 18 | # +r.params+), then Roda will not attempt to parse the request body, and an exception will not 19 | # be raised. 20 | module DisallowFileUploads 21 | # Exception class used when a multipart file upload is attempted. 22 | class Error < RodaError; end 23 | 24 | NO_TEMPFILE = lambda{|_,_| raise Error, "Support for uploading files has been disabled"} 25 | 26 | module RequestMethods 27 | # HTML escape the input and return the escaped version. 28 | def initialize(_, env) 29 | env['rack.multipart.tempfile_factory'] = NO_TEMPFILE 30 | super 31 | end 32 | end 33 | end 34 | 35 | register_plugin(:disallow_file_uploads, DisallowFileUploads) 36 | end 37 | end 38 | 39 | -------------------------------------------------------------------------------- /lib/roda/plugins/drop_body.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # 4 | class Roda 5 | module RodaPlugins 6 | # The drop_body plugin automatically drops the body and 7 | # Content-Type/Content-Length headers from the response if 8 | # the response status indicates that the response should 9 | # not include a body (response statuses 100, 101, 102, 204, 10 | # and 304). For response status 205, the body and Content-Type 11 | # headers are dropped, but the Content-length header is set to 12 | # '0' instead of being dropped. 13 | module DropBody 14 | module ResponseMethods 15 | DROP_BODY_STATUSES = [100, 101, 102, 204, 205, 304].freeze 16 | RodaPlugins.deprecate_constant(self, :DROP_BODY_STATUSES) 17 | 18 | DROP_BODY_RANGE = 100..199 19 | private_constant :DROP_BODY_RANGE 20 | 21 | # If the response status indicates a body should not be 22 | # returned, use an empty body and remove the Content-Length 23 | # and Content-Type headers. 24 | def finish 25 | r = super 26 | case r[0] 27 | when DROP_BODY_RANGE, 204, 304 28 | r[2] = EMPTY_ARRAY 29 | h = r[1] 30 | h.delete(RodaResponseHeaders::CONTENT_LENGTH) 31 | h.delete(RodaResponseHeaders::CONTENT_TYPE) 32 | when 205 33 | r[2] = EMPTY_ARRAY 34 | empty_205_headers(r[1]) 35 | end 36 | r 37 | end 38 | end 39 | end 40 | 41 | register_plugin(:drop_body, DropBody) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/roda/plugins/early_hints.rb: -------------------------------------------------------------------------------- 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. 8 | # Early hints allow clients to preload necessary files before receiving 9 | # the response. 10 | module EarlyHints 11 | module InstanceMethods 12 | # Send given hash of Early Hints using the rack.early_hints environment variable, 13 | # currenly only supported by puma. hash given should generally have the single 14 | # key 'Link', and a string or array of strings for each of the early hints. 15 | def send_early_hints(hash) 16 | if eh_proc = env['rack.early_hints'] 17 | eh_proc.call(hash) 18 | end 19 | end 20 | end 21 | end 22 | 23 | register_plugin(:early_hints, EarlyHints) 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/roda/plugins/empty_root.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | # 4 | class Roda 5 | module RodaPlugins 6 | # The empty_root plugin makes +r.root+ match both on +/+ and 7 | # on the empty string. This is mostly useful when using multiple 8 | # rack applications, where the initial PATH_INFO has been moved 9 | # to SCRIPT_NAME. For example, if you have the following 10 | # applications: 11 | # 12 | # class App1 < Roda 13 | # on "albums" do 14 | # run App2 15 | # end 16 | # end 17 | # 18 | # class App2 < Roda 19 | # plugin :empty_root 20 | # 21 | # route do |r| 22 | # r.root do 23 | # "root" 24 | # end 25 | # end 26 | # end 27 | # 28 | # Then requests for both +/albums/+ and +/albums+ will return 29 | # "root". Without this plugin loaded into App2, only requests 30 | # for +/albums/+ will return "root", since by default, +r.root+ 31 | # matches only when the current PATH_INFO is +/+ and not when 32 | # it is empty. 33 | module EmptyRoot 34 | module RequestMethods 35 | # Match when the remaining path is the empty string, 36 | # in addition to the default behavior of matching when 37 | # the remaining path is +/+. 38 | def root(&block) 39 | super 40 | if remaining_path.empty? && is_get? 41 | always(&block) 42 | end 43 | end 44 | end 45 | end 46 | 47 | register_plugin(:empty_root, EmptyRoot) 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/roda/plugins/erb_h.rb: -------------------------------------------------------------------------------- 1 | # frozen-string-literal: true 2 | 3 | require 'erb/escape' 4 | 5 | # 6 | class Roda 7 | module RodaPlugins 8 | # The erb_h plugin adds an +h+ instance method that will HTML 9 | # escape the input and return it. This is similar to the h 10 | # plugin, but it uses erb/escape to implement the HTML escaping, 11 | # which offers faster performance. 12 | # 13 | # To make sure that this speeds up applications using the h 14 | # plugin, this depends on the h plugin, and overrides the 15 | # h method. 16 | # 17 | # The following example will return "<foo>" as the body. 18 | # 19 | # plugin :erb_h 20 | # 21 | # route do |r| 22 | # h('