Context: I'm solving a problem where I need an external audit program to be able to understand and "apply" Rails routes. One option for writing this external program could been to parse the output of rake routes
, but that would unnecessarily end-up duplicating the code which parses these routes and converts them into well-structured Journey::Route
objects.
Therefore, my plan is to output Rails.application.routes
to a common format (YAML, or JSON), which the external program can understand, and can build a router based on this data.
Question: Given this context, I'm trying to understand the structure of the Journey::Path::Paternet#spec
attribute, which occurs inside a Journey::Route
object, and happens to be the center of all action.
For example, the following route - /posts/:id
- gets converted to the following "spec" -
#<Journey::Nodes::Cat:0x00007ff193327ee0
@left=
#<Journey::Nodes::Cat:0x00007ff193308630
@left=
#<Journey::Nodes::Cat:0x00007ff1933087e8
@left=
#<Journey::Nodes::Cat:0x00007ff193308bf8
@left=#<Journey::Nodes::Slash:0x00007ff193308d38 @left="/", @memo=nil>,
@memo=nil,
@right=#<Journey::Nodes::Literal:0x00007ff193308c48 @left="posts", @memo=nil>>,
@memo=nil,
@right=#<Journey::Nodes::Slash:0x00007ff193308a40 @left="/", @memo=nil>>,
@memo=nil,
@right=#<Journey::Nodes::Symbol:0x00007ff1933086d0 @left=":id", @memo=nil, @regexp=/[^\.\/\?]+/>>,
@memo=nil,
@right=
#<Journey::Nodes::Group:0x00007ff193309c10
@left=
#<Journey::Nodes::Cat:0x00007ff193308220
@left=#<Journey::Nodes::Dot:0x00007ff1933084f0 @left=".", @memo=nil>,
@memo=nil,
@right=#<Journey::Nodes::Symbol:0x00007ff193308338 @left=":format", @memo=nil, @regexp=/[^\.\/\?]+/>>,
@memo=nil>>
- What are the left/right attributes in a
Journey::Nodes::Cat
object? What decides which token will be "left" and which token will be "right" - This looks suspiciously like a binary tree, but why is the very first token (i.e. the first
/
), the "innermost" (or a leaf node)? Shouldn't it be the "outermost" (or the root node)? - What is an efficient way to walk down this data-structure while performing route matching?
Journey is based on Finite State Machine that matches route, there's built-in visualizer (requires graphviz):
Journey::Nodes::Cat
is only one of node types that you can encounter, it is binary node that matchesexpressions
rule in path grammar, see parser.y, left is firstexpression
,right
is all others, this produces loop that consumes all expressions.Other thoughts about external routes analysis: Routes cannot be dumped into a static file in a generic case because they can contain:
dynamic constraints with non-pure functions (for example -
get :r, constraints: ->{rand(2)>0}
, idea is that result can depend on something outside of request, time or state, etc.) When these are present - even rails router itself can produce different result on second run over the same request.mounted rack apps - can have hardcoded or non-rails routers
But for simple case you can tap into rails'
ActionDispatch::Routing::RoutesInspector
which is used forrake routes
and get structured routes info that is better that just parsing the latter output.In gem
routes_coverage
I did this way: