123456789_123456789_123456789_123456789_123456789_

Class: ActionDispatch::Journey::Routes

Do not use. This class is for internal use only.
Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
Inherits: Object
Defined in: actionpack/lib/action_dispatch/journey/routes.rb

Overview

The Routing table. Contains all routes for a system. Routes can be added to the table by calling #add_route.

Class Method Summary

Instance Attribute Summary

::Enumerable - Included

#many?

Returns true if the enumerable has more than 1 element.

Instance Method Summary

::Enumerable - Included

#compact_blank

Returns a new ::Array without the blank items.

#exclude?

The negative of the Enumerable#include?.

#excluding

Returns a copy of the enumerable excluding the specified elements.

#in_order_of

Returns a new ::Array where the order has been set to that provided in the series, based on the key of the objects in the original enumerable.

#including

Returns a new array that includes the passed elements.

#index_by

Convert an enumerable to a hash, using the block result as the key and the element as the value.

#index_with

Convert an enumerable to a hash, using the element as the key and the block result as the value.

#maximum

Calculates the maximum from the extracted elements.

#minimum

Calculates the minimum from the extracted elements.

#pick

Extract the given key from the first element in the enumerable.

#pluck

Extract the given key from each element in the enumerable.

#sole

Returns the sole item in the enumerable.

#without
#as_json

::ActiveSupport::EnumerableCoreExt::Constants - Included

Constructor Details

.new(routes = []) ⇒ Routes

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 12

def initialize(routes = [])
  @routes             = routes
  @ast                = nil
  @anchored_routes    = []
  @custom_routes      = []
  @simulator          = nil
  @static_exact_map   = nil
end

Instance Attribute Details

#anchored_routes (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 10

attr_reader :routes, :custom_routes, :anchored_routes

#custom_routes (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 10

attr_reader :routes, :custom_routes, :anchored_routes

#empty? ⇒ Boolean (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 29

def empty?
  routes.empty?
end

#routes (readonly)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 10

attr_reader :routes, :custom_routes, :anchored_routes

Instance Method Details

#add_route(name, mapping)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 75

def add_route(name, mapping)
  route = mapping.make_route name, routes.length
  routes << route
  partition_route(route)
  clear_cache!
  route
end

#ast

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 61

def ast
  @ast ||= begin
    nodes = anchored_routes.map(&:ast)
    Nodes::Or.new(nodes)
  end
end

#clear

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 46

def clear
  routes.clear
  anchored_routes.clear
  custom_routes.clear
  clear_cache!
end

#clear_cache! (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 120

def clear_cache!
  @ast                = nil
  @simulator          = nil
  @static_exact_map   = nil
end

#each(&block)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 42

def each(&block)
  routes.each(&block)
end

#flatten_cat(node, atoms) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 160

def flatten_cat(node, atoms)
  if node.type == :CAT
    flatten_cat(node.left, atoms)
    flatten_cat(node.right, atoms)
  else
    atoms << node
  end
  atoms
end

#freeze

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 21

def freeze
  ast
  simulator
  static_exact_map

  super
end

#last

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 38

def last
  routes.last
end

#length Also known as: #size

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 33

def length
  routes.length
end

#partition_route(route)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 53

def partition_route(route)
  if route.path.anchored && route.path.requirements_anchored?
    anchored_routes << route
  else
    custom_routes << route
  end
end

#shadowed?(route, path) ⇒ Boolean (private)

Would a route defined earlier also match this path? The candidates come from the simulator, as at request time, which keeps building the map linear in the number of routes. Verb overlap is judged conservatively: only provably disjoint request methods rule a route out.

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 174

def shadowed?(route, path)
  candidates = simulator.memos(path) { [] } + custom_routes.select { |r| r.path.match?(path) }

  candidates.any? do |earlier|
    earlier.precedence < route.precedence && verbs_may_overlap?(earlier, route)
  end
end

#simulator

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 68

def simulator
  @simulator ||= begin
    gtg = GTG::Builder.new(ast).transition_table
    GTG::Simulator.new(gtg)
  end
end

#single_verb_for(route) (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 126

def single_verb_for(route)
  verb = route.verb
  verb unless verb.empty? || verb.include?("|")
end

#size

Alias for #length.

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 36

alias :size :length

#static_exact_map

Maps a request method and an exact path to the route that is certain to be the first match for it, e.g. "GET /posts" for a "/posts(.:format)" route, so that request-time matching for static paths is a ::Hash lookup that captures no parameters, instead of a simulator walk, a ::Regexp match, and parameter extraction.

Only routes that leave no room for request-dependent behavior are included: anchored, a single concrete request method, no extra request constraints, and a path that is all literals once trailing optional groups are dropped. Paths that an earlier route (static or dynamic) could also match are excluded at build time, preserving precedence.

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 94

def static_exact_map
  @static_exact_map ||= begin
    map = {}

    anchored_routes.each do |route|
      next unless route.constraints.empty?
      next unless (verb = single_verb_for(route))
      next unless (path = static_exact_path(route))

      bucket = (map[verb] ||= {})
      bucket[path] = route unless bucket.key?(path) || shadowed?(route, path)
    end

    map.each_value(&:freeze)
    map.freeze
  end
end

#static_exact_path(route) (private)

The exact path a request must carry to match the route statically: its literal spec with trailing optional groups dropped. nil when the route has dynamic segments outside optional groups.

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 134

def static_exact_path(route)
  atoms = flatten_cat(route.path.spec, [])

  path = +""
  seen_group = false
  atoms.each do |atom|
    case atom.type
    when :LITERAL, :SLASH, :DOT
      return nil if seen_group
      path << atom.left
    when :GROUP
      seen_group = true
    else
      return nil
    end
  end
  return nil if path.empty?

  # The pattern itself has the final say: it must match the path
  # without capturing anything.
  match_data = route.path.match(path)
  return nil unless match_data && match_data.captures.all?(&:nil?)

  -path
end

#verbs_may_overlap?(a, b) ⇒ Boolean (private)

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 182

def verbs_may_overlap?(a, b)
  a_verb, b_verb = a.verb, b.verb
  a_verb.empty? || b_verb.empty? || a_verb.include?(b_verb) || b_verb.include?(a_verb)
end

#visualizer

[ GitHub ]

  
# File 'actionpack/lib/action_dispatch/journey/routes.rb', line 112

def visualizer
  tt     = GTG::Builder.new(ast).transition_table
  groups = anchored_routes.map(&:ast).group_by(&:to_s)
  asts   = groups.values.map(&:first)
  tt.visualizer(asts)
end