Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
* [#2754](https://github.com/ruby-grape/grape/pull/2754): Merge routing args in place in `Router#process_route` instead of allocating a new Hash via `merge` - [@ericproulx](https://github.com/ericproulx).
* [#2753](https://github.com/ruby-grape/grape/pull/2753): Lazy-allocate `Grape::Validations::ParamScopeTracker`'s identity-keyed hashes so validating requests that never use the index / qualifying-params trackers allocate no hash - [@ericproulx](https://github.com/ericproulx).
* [#2752](https://github.com/ruby-grape/grape/pull/2752): Skip per-request `ActiveSupport::Notifications` payload and dispatch when no subscriber is listening, via private `instrument_<event>` guards on `Endpoint`/`Middleware::Formatter` - [@ericproulx](https://github.com/ericproulx).
* [#2755](https://github.com/ruby-grape/grape/pull/2755): Inline `mustermann-grape` into `Grape::Router::MustermannPattern` and depend on `mustermann` directly - [@ericproulx](https://github.com/ericproulx).
* [#2757](https://github.com/ruby-grape/grape/pull/2757): Build the `Grape::Cookies` jar only when a cookie is read or written (via a new `Grape::Request#cookies?` predicate gating response-cookie flushing), and drop the jar's now-redundant lazy-parse `Proc` - [@ericproulx](https://github.com/ericproulx).
* [#2756](https://github.com/ruby-grape/grape/pull/2756): Tighten dependency lower bounds to their compatibility floors (`rack >= 2.2.4`, `zeitwerk >= 2.6`, `dry-configurable >= 1.0`) - [@ericproulx](https://github.com/ericproulx).
* Your contribution here.
Expand Down
10 changes: 10 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ Upgrading Grape

Grape no longer supports Ruby 3.2; 3.3 is now the minimum (`required_ruby_version = '>= 3.3'`). Upgrade your runtime to Ruby 3.3 or newer before bumping Grape.

#### `mustermann-grape` is no longer a dependency

Grape's path-pattern grammar (previously the `mustermann-grape` gem) now lives in Grape itself as `Grape::Router::MustermannPattern`, and Grape depends on `mustermann` directly. This is transparent for normal Grape usage.

The inlined class is no longer registered as a Mustermann type, so if your app called `Mustermann.new(pattern, type: :grape)` and relied on Grape loading `mustermann-grape` for you, add it to your Gemfile explicitly:

```ruby
gem 'mustermann-grape'
```

#### `Grape::Exceptions::ValidationErrors.new` keyword renamed `errors:` → `exceptions:`

`Grape::Exceptions::ValidationErrors#initialize` now takes its input array under the `exceptions:` keyword instead of `errors:`. The kwarg accepts a mix of `Grape::Exceptions::Validation` and `Grape::Exceptions::ValidationArrayErrors` instances; `ValidationArrayErrors` wrappers are flattened internally via `flat_map(&:errors)`. The `errors` reader on the constructed instance (the grouped `{params => [Validation, ...]}` Hash) is unchanged.
Expand Down
2 changes: 1 addition & 1 deletion grape.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Gem::Specification.new do |s|
s.add_dependency 'activesupport', '>= 7.2'
s.add_dependency 'dry-configurable', '>= 1.0'
s.add_dependency 'dry-types', '>= 1.1'
s.add_dependency 'mustermann-grape', '~> 1.1.0'
s.add_dependency 'mustermann', '>= 4.0'
s.add_dependency 'rack', '>= 2.2.4'
s.add_dependency 'zeitwerk', '>= 2.6'

Expand Down
3 changes: 2 additions & 1 deletion lib/grape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
require 'dry-configurable'
require 'forwardable'
require 'json'
require 'mustermann/grape'
require 'mustermann'
require 'mustermann/ast/pattern'
require 'rack'
require 'rack/auth/basic'
require 'rack/builder'
Expand Down
44 changes: 44 additions & 0 deletions lib/grape/router/mustermann_pattern.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

module Grape
class Router
# Grape-style path patterns for Mustermann: `:param`, `*splat`, `{name}` /
# `{+splat}`, `( )` optionals, `|`, and an Integer digit-only constraint
# (driven by Grape's `params` option).
#
# Inlined from the mustermann-grape gem (MIT) by namusyaka, Konstantin Haase
# and Daniel Doubrovkine. Grape instantiates this class directly (see
# {Grape::Router::Pattern}), so unlike the gem it is not registered as a
# Mustermann `type: :grape`.
class MustermannPattern < ::Mustermann::AST::Pattern
supported_options :params

on(nil, '?', ')') { |c| unexpected(c) }

on('*') { |_c| scan(/\w+/) ? node(:named_splat, buffer.matched) : node(:splat) }
on(':') do |_c|
param_name = scan(/\w+/)
# Integer params (declared via Grape's `params` option) match digits only;
# any other capture matches a single path segment (anything but / ? # .).
param_type = pattern&.options&.dig(:params, param_name, :type)
constraint = param_type == 'Integer' ? /\d/ : '[^/?#.]'
node(:capture, param_name, constraint:) { scan(/\w+/) }
end
on('\\') { |_c| node(:char, expect(/./)) }
on('(') { |_c| node(:optional, node(:group) { read unless scan(')') }) }
on('|') { |_c| node(:or) }

on('{') do |_c|
type = scan('+') ? :named_splat : :capture
name = expect(/[\w.]+/)
type = :splat if (type == :named_splat) && (name == 'splat')
expect('}')
node(type, name)
end

suffix('?') do |_c, element|
node(:optional, element)
end
end
end
end
2 changes: 1 addition & 1 deletion lib/grape/router/pattern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Pattern
def initialize(origin:, suffix:, anchor:, params:, format:, version:, requirements:)
@origin = origin
@path = PatternCache[[build_path_from_pattern(@origin, anchor), suffix]]
@pattern = Mustermann::Grape.new(@path, uri_decode: true, params:, capture: extract_capture(format, version, requirements))
@pattern = MustermannPattern.new(@path, uri_decode: true, params:, capture: extract_capture(format, version, requirements))
@to_regexp = @pattern.to_regexp
end

Expand Down
Loading