Create custom route parameters
Although Marten has built-in support for common route parameters, it is also possible to implement your very own parameter types. This may be necessary if your routes have more complex matching requirements.
Defining a route parameter
In order to implement custom parameters, you need to subclass the Marten::Routing::Parameter::Base
abstract class. Each parameter class is responsible for:
- defining a Regex allowing to match the parameters in raw paths (which can be done by implementing a
#regex
method) - defining how the route parameter value should be deserialized (which can be done by implementing a
#loads
method) - defining how the route parameter value should serialized (which can be done by implementing a
#dumps
method)
The #regex
method takes no arguments and must return a valid Regex
object.
The #loads
method takes the raw parameter (string) as argument and is expected to return the final Crystal object corresponding to the route parameter (this is the object that will be forwarded to the handler in the route parameters hash).
The #dumps
method takes the final route parameter object as argument and must return the corresponding string representation. Note that this method can either return a string or nil
: nil
means that the passed value couldn't be serialized properly, which will make any URL reverse resolution fail with a Marten::Routing::Errors::NoReverseMatch
error.
For example, a "year" (1000-2999) route parameter could be implemented as follows:
class YearParameter < Marten::Routing::Parameter::Base
def regex : Regex
/[12][0-9]{3}/
end
def loads(value : ::String) : UInt64
value.to_u64
end
def dumps(value) : Nil | ::String
if value.as?(UInt8 | UInt16 | UInt32 | UInt64)
value.to_s
elsif value.is_a?(Int8 | Int16 | Int32 | Int64) && [1000..2999].includes?(value)
value.to_s
else
nil
end
end
end
Registering route parameters
In order to be able to use custom route parameters in your route definitions, you must register them to Marten's global routing parameters registry.
To do so, you will have to call the Marten::Routing::Parameter#register
method with the identifier of the parameter you wish to use in route path definitions, and the actual parameter class. For example:
Marten::Routing::Parameter.register(:year, YearParameter)
With the above registration, you could technically create the following route definition:
Marten.routes.draw do
path "/vintage/<vintage:year>", VintageHandler, name: "vintage"
end