Class: ActionDispatch::Journey::Routes
| 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
- .new(routes = []) ⇒ Routes constructor
Instance Attribute Summary
- #anchored_routes readonly
- #custom_routes readonly
- #empty? ⇒ Boolean readonly
- #routes readonly
::Enumerable - Included
| #many? | Returns |
Instance Method Summary
- #add_route(name, mapping)
- #ast
- #clear
- #each(&block)
- #freeze
- #last
- #length (also: #size)
- #partition_route(route)
- #simulator
-
#size
Alias for #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
::Hashlookup that captures no parameters, instead of a simulator walk, a::Regexpmatch, and parameter extraction. - #visualizer
- #clear_cache! private
- #flatten_cat(node, atoms) private
-
#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.
- #single_verb_for(route) private
-
#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.
- #verbs_may_overlap?(a, b) ⇒ Boolean private
::Enumerable - Included
| #compact_blank | Returns a new |
| #exclude? | The negative of the |
| #excluding | Returns a copy of the enumerable excluding the specified elements. |
| #in_order_of | Returns a new |
| #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 | Alias for Enumerable#excluding. |
| #as_json | |
::ActiveSupport::EnumerableCoreExt::Constants - Included
Constructor Details
.new(routes = []) ⇒ Routes
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.
# 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 ]#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.
# 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.
# 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.
# 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)
#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