Skip to content

Capture improvements for APIs built on top of Mustermann #161

Description

@rkh

These are some ideas while playing around with Sinatra, Hanami, and custom tooling.

Default Capture

Right now it is possible to set a capture value to generally apply to all captures of a pattern:

Mustermann.new("/:a/:b", capture: Integer)

You can also set it for each capture individually:

Mustermann.new("/:a/:b", capture: { a: Integer, b: :uuid })

It is not possible to set a capture value for all captures but the ones that have explicit values. It would be nice for libraries and frameworks on top of Mustermann if they could do so. Perhaps with a nil key?

Mustermann.new("/:a/:b", capture: { :a => Integer, nil => [Integer, :uuid] })

For end users this feature would probably be irrelevant, but it would be neat for building APIs that allow incremental creation of capture parameters.

Imagine the following (fictional API):

AwesomeFrameworkUsingMustermann.new do
  # set :id to capture integer and uuid values in any pattern
  caputre id: [Integer, :uuid]
 
  scope "/users/:id" do
     get "/projects(/:page)?", caputre: Integer do
        # generated pattern would be /users/:id/projects(/:page)? with catpures = { id: [Integer, :uuid], nil => Integer }
     end
  end
end

Without these features, APIs built on top of Mustermann would have three alternatives:

  1. Don't offer incremental construction of capture rules.
  2. Only allow hash values to be used to begin with, no generic captures.
  3. Scan the pattern for captures. This is what Mustermann already does internally when performing type-native concatenation, but no proper tooling has been exposed for this other than compiling a pattern twice.

Pattern matching captures

Furthermore, we could allow adding capture rules based on a regexp or a small pattern.

Regexp approach:

Mustermann.new("/users/:user_id/posts/:post_id", capture: { /_id$/ => [Integer, :uuid] })

Or with a pattern:

Mustermann.new("/users/:user_id/posts/:post_id", capture: { "*_id" => [Integer, :uuid] })

This again isn't that much of an improvement for users instantiating patterns directly, but it comes in handy for situations where capture rules are reused.

set = Mustermann::Set.new(capture: { "(*_)?id" => Integer })

set.add "/users/:id"
set.add "/users/:user_id/posts/:id"

Implementation should be trivial. Would need to take some care of merging capture rules (as they could now overlap), but as the engine under the hood already allows nested hashes, this wouldn't be too hard either.

Activity

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

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions