Routing
Wayfarer equips jobs with a declarative routing DSL that maps URLs to actions. Actions are instance methods denoted by symbols, or handlers. Pages are only retrieved from URLs which map to an action.
Routed URLs are normalized
By default, Wayfarer applies some transformations to each URL to bring it
into a canonical form. Routing happens based on this canonical form,
not on the verbatim task.url.
This means that for example a route declaration doesn't match the URL
https://www.example.com, since the URL's hostname is normalized to example.com.
route.host "www.example.com"
wayfarer route CLI sub-command the -r FILE option
and point to the file that configures Wayfarer.config.
A job's route declarations equate to a predicate tree.
When a URL is routed, the predicate tree is searched depth-first. If a
matching leaf predicate is found, the downmost path's action is dispatched.
You can extract data from URL path segments and query parameters and
access it through params in jobs or handlers.
The following routes:
route.host "example.com", scheme: :https do
path "contact", to: :contact
path "users/:id" do
to [UserHandler, :show]
path "gallery", to: [UserHandler, :photos]
end
end
Equate to the following predicate tree:
flowchart LR
Root-->Host["Host <code>example.com</code>"]
Host-->Scheme["Scheme <code>:https</code>"]
%% first-level paths
Scheme-->PathContact["Path <code>contact</code>"]
Scheme-->PathUsersId["Path <code>users/:id</code>"]
%% their targets
PathContact-->TargetRouteContact["Target <code>:contact</code>"]
PathUsersId-->TargetRouteUserHandler["Target <code>[UserHandler, :show]</code>"]
%% nested path under /users/:id
PathUsersId-->PathGallery["Path <code>'gallery'</code>"]
PathGallery-->TargetRouteUserHandlerPhotos["Target <code>[UserHandler, :photos]</code>"]
Traversing the tree depth-first for https://example.com/users/42 stops at the
route with the action [UserHandler, :show]:
flowchart LR
Root:::matching-->Host["Host <code>example.com</code>"]:::matching
Host:::matching-->Scheme["Scheme <code>:https</code>"]:::matching
%% sibling paths from the scheme node
Scheme:::matching-->PathContact["Path <code>/contact</code>"]:::mismatching
Scheme:::matching-->PathUsersId["Path <code>/users/:id</code>"]:::matching
%% successful match for /users/:id
PathUsersId:::matching-->TargetRouteUserHandler["Target <code>[UserHandler, :show]</code>"]:::matching
%% gallery branch is never visited for /users/42
PathContact-->TargetRouteContact["Target <code>:contact</code>"]:::unvisited
PathUsersId:::matching-->PathGallery["Path <code>/gallery</code>"]:::unvisited
PathGallery:::unvisited-->TargetRouteUserHandlerPhotos["Target <code>[UserHandler, :photos]</code>"]:::unvisited
classDef matching fill:#7CB342,stroke:#7CB342,color:#fff
classDef mismatching fill:#FFCDD2,stroke:#F44336,color:#B71C1C
classDef unvisited fill:#BDBDBD,stroke:#BDBDBD,color:#616161
You can also visualise a job's routing tree with with the route CLI subcommand
wayfarer route DummyJob -r dummy_job.rb http://localhost:9000/users/42/gallery
---
routed: true
params:
id: '42'
action:
handler: Class
action: :photos
root_route:
match: true
params: {}
children:
- route:
host:
name: example.com
match: true
params: {}
children:
- route:
scheme:
scheme: :https
match: true
params: {}
children:
- route:
path:
pattern: "/contact"
match: false
params: {}
children:
- target_route:
action:
children: []
- route:
path:
pattern: "/users/:id"
match: true
params:
id: '42'
children:
- target_route:
action:
handler: Class
action: :show
children: []
- route:
path:
pattern: "/gallery"
match: true
params:
id: '42'
children:
- target_route:
action:
handler: Class
action: :photos
children: []
As you can see, Target nodes always match. This means that we could have also defined
our routes as:
route.host "example.com", scheme: :https do
to :contact do
path "/contact"
end
to [UserHandler, :show] do
path "/users/:id"
end
end