Skip to content

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"
You may need to pass the 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