Skip to content

Inline mustermann-grape into Grape::Router::MustermannPattern#2755

Open
ericproulx wants to merge 1 commit into
masterfrom
refactor/inline-mustermann-grape
Open

Inline mustermann-grape into Grape::Router::MustermannPattern#2755
ericproulx wants to merge 1 commit into
masterfrom
refactor/inline-mustermann-grape

Conversation

@ericproulx
Copy link
Copy Markdown
Contributor

Why

mustermann-grape is a ~40-line Mustermann::AST::Pattern subclass that defines Grape's path syntax (:param, *splat, {name} / {+splat}, ( ) optionals, |, and an Integer digit-only constraint). In practice it exists only for Grape — and it has already grown Grape-specific: the Integer constraint reaches into Grape's own params option.

param_type = pattern&.options&.dig(:params, param_name, :type)
constraint = param_type == 'Integer' ? /\d/ : '[^/?#.]'

Keeping it as a separate gem means a two-step release for any syntax change (cut a mustermann-grape release, then bump Grape's ~> 1.1.0 pin), plus its own CI and versioning — a lot of ceremony for 40 lines only Grape uses. Grape also always instantiated the class directly (Mustermann::Grape.new), never through the type: :grape registry.

What

  • Move the grammar into Grape as Grape::Router::MustermannPattern (MIT attribution to the original authors — namusyaka, Konstantin Haase, Daniel Doubrovkine — preserved), and instantiate it directly from Grape::Router::Pattern.
  • Depend on mustermann directly instead of mustermann-grape.
  • Drop the register :grape call — Grape never used the registry, and not squatting a global Mustermann type keeps the inlined grammar a private Grape detail.

This is not a deep dependency reduction: the grammar extends Mustermann::AST::Pattern, so the mustermann core dependency stays. The graph just collapses from grape → mustermann-grape → mustermann to grape → mustermann, and Grape owns the 40 lines it was already coupled to. The win is maintenance: pattern-syntax changes become a single PR instead of a cross-gem release dance.

Compatibility

One behavioral change: the pattern class is no longer registered as type: :grape. Apps that called Mustermann.new(_, type: :grape) and relied on Grape pulling in mustermann-grape transitively should add gem 'mustermann-grape' to their Gemfile directly. Documented in UPGRADING.md.

Validation

  • bundle install now resolves without mustermann-grape (only mustermann 4.0.0) — Grape is self-sufficient.
  • Full suite 2,316 examples, 0 failures; RuboCop clean.
  • Integer-constraint behavior verified: /api/:id matches 42, rejects foo.

🤖 Generated with Claude Code

@ericproulx ericproulx force-pushed the refactor/inline-mustermann-grape branch from 8d60f9c to 2b7d16c Compare June 1, 2026 13:39
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 1, 2026

Danger Report

No issues found.

View run

@ericproulx ericproulx marked this pull request as draft June 1, 2026 13:56
@ericproulx ericproulx force-pushed the refactor/inline-mustermann-grape branch from 2b7d16c to a969b46 Compare June 1, 2026 13:56
mustermann-grape is a ~40-line Mustermann::AST::Pattern subclass that exists
only for Grape's path syntax, and it had already grown Grape-specific: the
Integer constraint reads Grape's own `params` option. Maintaining it as a
separate gem meant a two-step release for every syntax tweak (cut a
mustermann-grape release, then bump Grape's pin) plus its own CI/versioning.

Move the grammar into Grape as Grape::Router::MustermannPattern (attribution to
the original authors preserved), depend on `mustermann` directly, and
instantiate it directly from Grape::Router::Pattern as Grape always did. The
`mustermann` core dependency is unchanged; the graph collapses from
grape -> mustermann-grape -> mustermann to grape -> mustermann.

The class is no longer registered as a Mustermann `type: :grape` (Grape never
used the registry). Apps that called `Mustermann.new(_, type: :grape)` relying
on Grape pulling in mustermann-grape transitively should depend on it directly;
see UPGRADING.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ericproulx ericproulx force-pushed the refactor/inline-mustermann-grape branch from a969b46 to f3be4cd Compare June 1, 2026 14:01
ericproulx added a commit that referenced this pull request Jun 1, 2026
Grape requires Ruby >= 3.3 and activesupport >= 7.2 (Rails 7.2+), but several
dependency floors still advertised support for much older releases that Grape
no longer tests:

- rack            >= 2     -> >= 2.2.4  (minimum rack across Rails 7.2 and above)
- zeitwerk        (none)   -> >= 2.7    (where zeitwerk moved to Ruby >= 3.2)
- dry-configurable (none)  -> >= 1.4    (first release requiring Ruby >= 3.3)
- dry-types       >= 1.1   -> >= 1.9    (current line, Ruby >= 3.2)

Resolved versions are unchanged (rack 3.2.6, zeitwerk 2.8.2, dry-configurable
1.4.0, dry-types 1.9.1); only the floors change. mustermann is left to #2755.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ericproulx ericproulx marked this pull request as ready for review June 1, 2026 15:09
@dblock
Copy link
Copy Markdown
Member

dblock commented Jun 2, 2026

Makes sense, let's just check-in with @namusyaka on whether there was a reason to have a separate gem for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants