• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ruby-grape / grape
97%
master: 97%

Build:
Build:
LAST BUILD BRANCH: perf/default-status-single-read
DEFAULT BRANCH: master
Repo Added 04 Apr 2013 06:08AM UTC
Files 169
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

LAST BUILD ON BRANCH perf/version-capture-regexp
branch: perf/version-capture-regexp
CHANGE BRANCH
x
Reset
  • perf/version-capture-regexp
  • DateFormat
  • Handle_json_array
  • ISSUE-2321
  • Issue-1908
  • active_support_6_1_minimal
  • add-agents-md
  • add_irb_in_gemfile_for_bundle_console_warning
  • add_numericality_validator
  • add_rack_3_2_gemfile
  • add_rails_8_1_on_ci
  • add_ruby_3_4
  • add_ruby_4_0_cli
  • api_override_singleton_methods
  • array-param-regression
  • as-explicit-kwarg
  • benchmark/results-machine-spec
  • body-metadata
  • bugfix/1577
  • bugfix/1970
  • bugfix/1986
  • bugfix/2041
  • bugfix/autoload
  • bugfix/memory-leak
  • build_with_registry
  • chore/agents-md-style-rules
  • chore/cleanup-on-compilation
  • chore/coercion-tests
  • chore/drop-middleware-globals
  • chore/guard-clause-cleanup
  • chore/json-parse-fallback
  • chore/min-ruby-3.3
  • chore/normalize-eql-aliases
  • chore/primitive-coercer-spec
  • chore/prune-requires
  • chore/raise-dependency-lower-bounds
  • chore/readability-guard-clauses
  • chore/readability-pass
  • chore/version-throughput-deltas
  • clean_up_useless_dependencies
  • cleanup-deprecated-code
  • compiled_endpoints
  • contract_scope_validator_public_schema
  • cookies_delegation
  • crush_desclared_params
  • custom-validator-namespace-lookup
  • declared-params-scope-kwarg
  • declared_params_optional_array
  • deprecate-positional-options-hash
  • deprecated_endpoint_return
  • doc
  • doc/remove_manual_toc_and_toc_danger_check
  • doc/remove_toc
  • doc/update_reload_documentation_for_current_Rails_versions
  • docs/format-removal-note
  • docs/readme-typos-and-fixnum-cleanup
  • draft/middleware-options-data
  • drop_namespace_description
  • drop_ruby_3_0_as_7_0
  • drop_support_ruby_2_7_0
  • dynamic_registration
  • empty_string_and_bool
  • enable_branch_coverage
  • enable_frozen_string_literal_ci
  • endpoint_api_kwarg
  • endpoint_app_kwarg
  • endpoint_cleanup
  • endpoint_http_methods_kwarg
  • endpoint_path_kwarg
  • entity-dsl
  • eql-hash-consistency
  • erik/remove-faulty-documentation-about-blank-values
  • exclude_ruby_3_2_rails_edge
  • explicit-required-flag
  • explicit_route_options
  • exposes-to-s
  • extract_entity_dsl_and_kwargs_with
  • extract_testing_module
  • extract_translation
  • failing_spec_for_1967
  • faster_normalize_path
  • feature/endpoint-logger
  • feature/error-formatter-kwargs-2527
  • feature/grape-api-setup-var-array
  • feature/hash-oneof-2385
  • feature/return_of_return
  • feature/warn-on-helper-overrides
  • feature_rack_lint
  • fix-636
  • fix-badges
  • fix-before-each-nil-nomethoderror
  • fix-cascade-dsl-getter
  • fix-coercer-request-time-state
  • fix-compile-instance-race
  • fix-docs
  • fix-helper-inheritance
  • fix-line-anchored-regexes
  • fix-multiple-type-values-check
  • fix-options-const-defined-object-fallback
  • fix-path-versioner-root-version
  • fix-router-cascade-routing-args
  • fix-router-cascade-sibling-routes
  • fix-top-level-entity-constant
  • fix-typo
  • fix-validator-non-hash-guard
  • fix/2748-structured-message-leak
  • fix/2763-multijson-deprecations
  • fix/auth-base-subclass-issue-2669
  • fix/avoid_multiple_mounts_pollution
  • fix/base-only-rescue-handlers
  • fix/cascade_reading_api_level
  • fix/case-insensitive-media-types
  • fix/custom-type-coercer-symbolize-collection
  • fix/entity-lookup-own-class-first
  • fix/error-rendering-failsafe
  • fix/grape-exceptions-precedence
  • fix/greedy-route-params-for
  • fix/json-load-create-additions
  • fix/json-use-parse
  • fix/multixml-deprecation
  • fix/nested-array-validation-bypass
  • fix/path-param-encoding
  • fix/recompile-after-route-added
  • fix/recompile-settings-mutation-race
  • fix/redirect-plain-text-body
  • fix/reduce_base_route_array_allocation
  • fix/rescue-from-internal-errors-2482
  • fix/route-param-named-version
  • fix/route-success-failure-readers
  • fix/router-request-time-map-mutation
  • fix/safe-default-rack-exception
  • fix/sendfile_test_fix
  • fix/stackable-copy-aliasing
  • fix/variant-collection-coercer-to-s
  • fix/warn-shadowed-rescue-from
  • fix_2566_memory_leak
  • fix_all_media_types_regression
  • fix_before_each_no_method_error
  • fix_coercer_cache
  • fix_danger_workflow
  • fix_endpoint_status_when_not_calling_error!
  • fix_gh_workflow_annotations_warnings
  • fix_grape_swagger_test
  • fix_helpers_methods_in_public_scope
  • fix_leaky_slash
  • fix_middleware_keywords
  • fix_param_scope_thread_safety
  • fix_registry_warning_message
  • fix_require_logger
  • fix_routes_memoization
  • fix_shared_params_empty_args
  • fix_style_optional_boolean_parameter
  • format_header_best_q_match
  • forward_match_kwarg
  • freeze-coercers-and-sync-caches
  • funding
  • happy_rubocop
  • head_route_on_route_class
  • hook-readme
  • inheritable-setting-auth-params-builder
  • inheritable-setting-callbacks
  • inheritable-setting-cheap-equality
  • inheritable-setting-content-negotiation
  • inheritable-setting-contract-key-map
  • inheritable-setting-finale
  • inheritable-setting-format-defaults
  • inheritable-setting-integration
  • inheritable-setting-middleware-helpers
  • inheritable-setting-params-state
  • inheritable-setting-representations
  • inheritable-setting-rescue-options
  • inheritable-setting-routing-flags
  • inheritable-setting-routing-scopes
  • inheritable-setting-versioning
  • instance-to-s-nil-fallback
  • instance_to_s_delegation
  • internalize-stackable-storage
  • issue-1775
  • issue-2385
  • layer1-thread-safety-invariants
  • layer2-concurrency-stress
  • lazy_block
  • lazy_compile
  • less_active_support_concern
  • less_parse_nested_query
  • master
  • migrate-danger-pr-comment
  • migrate-to-dry-configurable
  • mount-refresh-internal
  • mountable_apis
  • namespace_route_param_explicit_kwargs
  • optimize/parameters-extract-options-non-mutating
  • optimize_api_documentation
  • params-declare-extraction
  • patch-1
  • perf/avoid-empty-hash-merges
  • perf/build-headers-each-header
  • perf/config-param-builder
  • perf/content-type-lookup
  • perf/default-status-single-read
  • perf/endpoint-helpers-prototype
  • perf/formatter-content-negotiation
  • perf/formatter-rack-tuple
  • perf/formatter-skip-rack-request
  • perf/instrument-only-when-subscribed
  • perf/lazy-base-inheritable-new-values
  • perf/lazy-error-backtrace
  • perf/lazy-inheritable-setting-fields
  • perf/lazy-param-scope-tracker
  • perf/lazy-params-builder
  • perf/lazy-response-cookies
  • perf/path-normalizer-fast-path
  • perf/plain-hash-registry
  • perf/precompute-middleware-caches
  • perf/request-hot-path-polish
  • perf/resolve-cascade-once
  • perf/router-transaction-no-proc
  • perf/routing-args-in-place-merge
  • perf/routing-args-single-hash
  • perf/scrub-extension-only
  • perf/share-content-type-tables
  • perf/skip-capture-free-param-extraction
  • perf/skip-default-version-pattern
  • perf/skip-empty-validation-errors-alloc
  • perf/skip-redundant-routing-args-write
  • perf/skip-validation-backtrace
  • perf/stackable-empty-singleton
  • perf/versioner-path-prefix
  • perf/versioner-prefix-each
  • perf/versioner-reuses-route-capture
  • precompiled_json
  • private_namespace_reverse_stackable_with_hash
  • private_within_namespace
  • query-http-method
  • rack_request_error
  • real-api-integration-test
  • reduce_array_alloc_on_setup
  • refactor/api-instance-dsl-modules
  • refactor/attrs_iterator
  • refactor/auth-dsl-kwargs
  • refactor/available-media-types-attr-reader
  • refactor/base-validator-encapsulation
  • refactor/coerce-options-data
  • refactor/de-morgan-conditions
  • refactor/declared-params-handler
  • refactor/declared_availability
  • refactor/define-method-cleanup
  • refactor/drop-indifferent-and-reverse-merge-requires
  • refactor/dsl-group-validators-dry
  • refactor/encapsulate-reverse-stackable
  • refactor/endpoint-options-data
  • refactor/endpoint-options-route-enabled
  • refactor/error-response-data
  • refactor/error-response-value-object
  • refactor/extract-handler-case-when
  • refactor/extract-path-normalizer
  • refactor/guard-clauses-style
  • refactor/inline-mustermann-grape
  • refactor/lazy-base-ancestor
  • refactor/middleware-cleanups
  • refactor/mountable-marker
  • refactor/named_kwargs_params_scope
  • refactor/nest-path-in-pattern
  • refactor/params-dsl-using-except-kwargs
  • refactor/precomputed-content-types-move
  • refactor/replace-tap-usages
  • refactor/rescue-from-explicit-first-arg
  • refactor/rescue-handler-dedup
  • refactor/router-pattern-delegation
  • refactor/router-simplification
  • refactor/shared-options-data
  • refactor/stateless-attributes-iterator
  • refactor/validates-pure-helpers
  • refactor/validation-errors-drop-enumerable
  • refactor/validations-spec-self-validate
  • refactor/version-guard-clause
  • refactor_available_media_types_in_header_middleware
  • refactor_declared_params_handling
  • refactor_http_headers
  • refactor_mime_type_for
  • refactor_route_setting_internal_usage
  • refactor_setting_get_or_set
  • refs/heads/master
  • refs/tags/v1.7.1
  • refs/tags/v1.8.0
  • refs/tags/v2.0.0
  • regression/json-error-formatter-double-wrap
  • release/3.2.1
  • remove-inheritable-values
  • remove-reverse-stackable-values
  • remove-unused-constant
  • remove/http-digest-dsl
  • remove_deprecated_param_builder_extensions
  • remove_guard
  • remove_instance_variable_defined
  • remove_namespace_reverse_stackable_from_public_interface
  • remove_namespace_stackable_and_inheritable_from_public_space
  • remove_namespace_stackable_with_hash_from_public_interface
  • remove_non_supported_jsonapi
  • remove_obsolete_docker_compose_version
  • remove_path_dup
  • remove_pattern_format
  • remove_settings_unset_functions
  • remove_strict_hash_configuration
  • remove_test_prof
  • replace_try_by_respond_to
  • rescue_from
  • resolve_performace_issue_with_given
  • restore-routing-args-per-attempt
  • revert-1953-chore/micto-optimization-3
  • revert-2726-refactor/stateless-attributes-iterator
  • revert-2774-forward_match_kwarg
  • revert-wrong-commit
  • revisit_auth_middleware
  • revisit_endpoint_helpers_and_new
  • revisit_middleware_default_options
  • revisit_validators
  • robust_mounted_app_comparison
  • route-default-hash-collision
  • route-default-reads-description-key
  • route-default-response-alias
  • route-scope-accessors
  • route_attributes_kwargs
  • route_match_params_via_pattern
  • router-compile-registered-methods
  • rubocop-mutable-constant-recursive
  • ruby3_handling_argument_delegation
  • ruby_3_1_shorthand_kwargs
  • ruby_optimizations
  • ruby_style_send
  • security/mounted-rack-auth-and-html-escape
  • skip_instrumentation_no_validators
  • skip_run_filters_on_empty
  • small_refactor_versioner_middleware
  • spec-only-rack-class
  • support-open-ranges-values
  • test-adversarial-nesting-depth
  • test/grape-swagger-integration
  • try_pattern_instead_of_respond_to
  • unify-group-type-check
  • untangle_route_params
  • update-simplecov
  • update_min_ruby_version_and_rubocop
  • update_return_upgrading_notes_and_simplify_execute
  • update_rubocop
  • update_rubocop_1_71_2
  • update_rubocop_1_88
  • update_rubocop_and_autocorrect
  • upgrade-mustermann
  • upgrade-rubocop
  • upgrading-array-unknown-element-type
  • upgrading-description
  • use_forwardable
  • use_require_relative_in_gemspec
  • v0.19.2
  • v1.0.0
  • v1.0.1
  • v1.0.2
  • v1.0.3
  • v1.1.0
  • v1.2.0
  • v1.2.1
  • v1.2.2
  • v1.2.3
  • v1.2.4
  • v1.2.5
  • v1.3.0
  • v1.3.1
  • v1.3.2
  • v1.3.3
  • v1.4.0
  • v1.5.0
  • v1.5.1
  • v2.1.0
  • v2.1.1
  • v2.1.2
  • v2.1.3
  • v2.2.0
  • v2.3.0
  • v2.4.0
  • v3.0.0
  • v3.0.1
  • v3.1.0
  • v3.1.1
  • v3.2.0
  • v3.2.1
  • v3.3.0
  • v3.3.1
  • v3.3.1_release
  • v3.3.2
  • v3.3.2_release
  • v3.3.3
  • v3.3.3_release
  • v3.3.4
  • v3.3.4_release
  • v3.3.5
  • v3.3.5_release
  • valid_encoding_before_scrubing
  • validators_bad_encoding

03 Sep 2026 02:38PM UTC coverage: 96.953% (+0.005%) from 96.948%
33768125512

Pull #2889

github

ericproulx
Read the path version off the route the router already matched

Under path versioning the version is a named capture in every route pattern —
`Pattern::Path#build_parts` inserts `:version` ahead of the namespace — and it
is constrained to the declared versions. So by the time a request reaches the
endpoint's middleware stack, the router has already sliced the segment out of
the path, validated it, and left it in `env['grape.routing_args'][:version]`.

`Versioner::Path#before` derived it a second time, from scratch:

    path_info = Grape::Util::PathNormalizer.call(env[Rack::PATH_INFO])
    return if path_info == '/'
    @prefixes.each { |prefix| path_info = path_info.delete_prefix(prefix) if ... }
    slash_position = path_info.index('/', 1)
    return version_from_first_segment(path_info, slash_position) if slash_position

— a normalize, a prefix strip and a slice, for an answer already in the env.

Read it from there instead. The path parse stays, and answers three cases the
routing args cannot:

* no routing args at all, when the middleware runs outside a Grape router;
* the greedy routes behind auto-OPTIONS and 405, which capture nothing;
* a route that declares a `:version` segment of its own on top of the
  versioning one, where Mustermann reports the capture as every position it
  matched rather than the single segment recorded here — hence the
  `is_a?(String)` guard.

`Router#call` now also publishes the path it routed on as
`env['grape.normalized_path']`, so that fallback does not normalize `PATH_INFO`
a second time either. It is the same string the router matched against, which
is what the versioner wants to agree with.

This leans on the version capture being strict, so it comes after
"Constrain the version capture with a Regexp instead of an Array" — before it,
a percent-encoded segment matched the route and Mustermann decoded it, and the
versioner would have accepted what it used to reject.

Median of 5 interleaved runs against master (R... (continued)
Pull Request #2889: Constrain the version capture with a Regexp instead of an Array

1244 of 1338 branches covered (92.97%)

Branch coverage included in aggregate %.

9 of 9 new or added lines in 4 files covered. (100.0%)

3943 of 4012 relevant lines covered (98.28%)

27013.42 hits per line

Relevant lines Covered
Build:
Build:
4012 RELEVANT LINES 3943 COVERED LINES
27013.42 HITS PER LINE
Source Files on perf/version-capture-regexp
  • Tree
  • List 169
  • Changed 4
  • Source Changed 4
  • Coverage Changed 4
Coverage ∆ File Lines Relevant Covered Missed Hits/Line Branch Hits Branch Misses

Recent builds

Builds Branch Commit Type Ran Committer Via Coverage
33768125512 perf/version-capture-regexp Read the path version off the route the router already matched Under path versioning the version is a named capture in every route pattern — `Pattern::Path#build_parts` inserts `:version` ahead of the namespace — and it is constrained to the decl... Pull #2889 03 Sep 2026 02:39PM UTC ericproulx github
96.95
33759907081 perf/version-capture-regexp Read the path version off the route the router already matched Under path versioning the version is a named capture in every route pattern — `Pattern::Path#build_parts` inserts `:version` ahead of the namespace — and it is constrained to the decl... Pull #2889 03 Sep 2026 01:14PM UTC ericproulx github
96.95
33758582524 perf/version-capture-regexp Constrain the version capture with a Regexp instead of an Array `Pattern#extract_capture` hands the declared versions to Mustermann as the Array of Strings they arrive in: requirements.merge(version: map_str(version)) Mustermann uses `captu... Pull #2889 03 Sep 2026 01:01PM UTC ericproulx github
96.95
33758398003 perf/version-capture-regexp Constrain the version capture with a Regexp instead of an Array `Pattern#extract_capture` hands the declared versions to Mustermann as the Array of Strings they arrive in: requirements.merge(version: map_str(version)) Mustermann uses `captu... Pull #2889 03 Sep 2026 12:59PM UTC ericproulx github
96.95
33755546064 perf/version-capture-regexp Read the path version off the route the router already matched (#2890) Under path versioning the version is a named capture in every route pattern — `Pattern::Path#build_parts` inserts `:version` ahead of the namespace — and it is constrained to ... Pull #2889 03 Sep 2026 12:30PM UTC ericproulx github
96.95
33728877091 perf/version-capture-regexp Read the path version off the route the router already matched (#2890) Under path versioning the version is a named capture in every route pattern — `Pattern::Path#build_parts` inserts `:version` ahead of the namespace — and it is constrained to ... Pull #2889 03 Sep 2026 07:37AM UTC ericproulx github
96.97
33728112169 perf/version-capture-regexp Read the path version off the route the router already matched (#2890) Under path versioning the version is a named capture in every route pattern — `Pattern::Path#build_parts` inserts `:version` ahead of the namespace — and it is constrained to ... Pull #2889 03 Sep 2026 07:26AM UTC web-flow github
96.97
33727233129 perf/version-capture-regexp Constrain the version capture with a Regexp instead of an Array `Pattern#extract_capture` hands the declared versions to Mustermann as the Array of Strings they arrive in: requirements.merge(version: map_str(version)) Mustermann uses `captu... Pull #2889 03 Sep 2026 07:17AM UTC ericproulx github
96.96
33686809238 perf/version-capture-regexp Constrain the version capture with a Regexp instead of an Array `Pattern#extract_capture` hands the declared versions to Mustermann as the Array of Strings they arrive in: requirements.merge(version: map_str(version)) Mustermann uses `captu... Pull #2889 02 Sep 2026 09:54PM UTC ericproulx github
96.94
33686629872 perf/version-capture-regexp Constrain the version capture with a Regexp instead of an Array `Pattern#extract_capture` hands the declared versions to Mustermann as the Array of Strings they arrive in: requirements.merge(version: map_str(version)) Mustermann uses `captu... push 02 Sep 2026 09:49PM UTC ericproulx github
96.94
See All Builds (3693)
  • Repo on GitHub
  • CI Project
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc