0

背景:我正在解决一个问题,我需要一个外部审计程序才能理解和“应用”Rails 路由。编写此外部程序的一种选择可能是解析 的输出rake routes,但这将不必要地最终复制解析这些路由并将它们转换为结构良好的Journey::Route对象的代码。

因此,我的计划是输出Rails.application.routes到外部程序可以理解的通用格式(YAML,或 JSON),并可以根据这些数据构建路由器。

问题:鉴于这种情况,我试图了解Journey::Path::Paternet#spec属性的结构,它发生在Journey::Route对象内部,并且恰好是所有动作的中心。

例如,以下路线 - /posts/:id- 被转换为以下“规范” -

 #<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>>
  • 对象的左/右属性是Journey::Nodes::Cat什么?什么决定哪个令牌将是“左”,哪个令牌将是“右”
  • 这看起来有点像二叉树,但为什么第一个标记(即第一个/)是“最里面的”(或叶节点)?它不应该是“最外层”(或根节点)吗?
  • 在执行路由匹配时,什么是遍历此数据结构的有效方法?
4

1 回答 1

2

Journey 基于匹配路线的有限状态机,内置可视化工具(需要 graphviz):

File.open('routes.html', 'wt'){|f| f.write Rails.application.routes.router.visualizer }

Journey::Nodes::Cat只是您可以遇到的节点类型之一,它是与expressions路径语法中的规则匹配的二进制节点,请参见 parser.y,left 是 first expressionright是所有其他节点,这会产生消耗所有表达式的循环。

关于外部路由分析的其他想法:在一般情况下,路由不能转储到静态文件中,因为它们可以包含:

  • 具有非纯函数的动态约束(例如 -get :r, constraints: ->{rand(2)>0}想法是结果可能取决于请求、时间或状态等之外的某些东西)当这些存在时 - 即使是 rails 路由器本身也可以在第二次运行时产生不同的结果要求。

  • 安装的机架应用程序 - 可以有硬编码或非 Rails 路由器

  • rails 引擎 - 具有 rails 路由器,比通用机架应用程序更容易,但在挂载点和合并到主应用程序范围时很棘手

但是对于简单的情况,您可以利用 rails'ActionDispatch::Routing::RoutesInspectorrake routes获取结构化的路由信息​​,这比仅解析后者的输出要好。

在 gemroutes_coverage中,我这样做了:

class Inspector < ActionDispatch::Routing::RoutesInspector
  def collect_all_routes
    res = collect_routes(@routes)
    @engines.each do |engine_name, engine_routes|
      res += engine_routes.map{|er|
        er.merge({ engine_name: engine_name })
      }
    end
    res
  end

  def collect_routes(routes)
    routes.collect do |route|
      ActionDispatch::Routing::RouteWrapper.new(route)
    end.reject do |route|
      route.internal?
    end.collect do |route|
      collect_engine_routes(route)

      { name:   route.name,
        verb:   route.verb,
        path:   route.path,
        reqs:   route.reqs,
        original: route,
      }
    end
  end

res = Inspector.new(Rails.application.routes.routes.routes).collect_all_routes
于 2019-04-12T11:13:02.863 回答