Skip to content

Instantly share code, notes, and snippets.

@barnes7td
Created November 30, 2016 19:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save barnes7td/c717f89bba81f00db1732f79011575f5 to your computer and use it in GitHub Desktop.
Save barnes7td/c717f89bba81f00db1732f79011575f5 to your computer and use it in GitHub Desktop.
For learning
require 'action_dispatch/journey/core-ext/hash'
require 'action_dispatch/journey/router/utils'
require 'action_dispatch/journey/router/strexp'
require 'action_dispatch/journey/routes'
require 'action_dispatch/journey/formatter'
before = $-w
$-w = false
require 'action_dispatch/journey/parser'
$-w = before
require 'action_dispatch/journey/route'
require 'action_dispatch/journey/path/pattern'
module ActionDispatch
module Journey
class Router
class RoutingError < ::StandardError
end
VERSION = '2.0.0'
class NullReq # :nodoc:
attr_reader :env
def initialize env
@env = env
end
def request_method
env['REQUEST_METHOD']
end
def path_info
env['PATH_INFO']
end
def ip
env['REMOTE_ADDR']
end
def [](k); env[k]; end
end
attr_reader :request_class, :formatter
attr_accessor :routes
def initialize routes, options
@options = options
@params_key = options[:parameters_key]
@request_class = options[:request_class] || NullReq
@routes = routes
end
def call env
env['PATH_INFO'] = Utils.normalize_path env['PATH_INFO']
find_routes(env).each do |match, parameters, route|
script_name, path_info, set_params = env.values_at('SCRIPT_NAME',
'PATH_INFO',
@params_key)
unless route.path.anchored
env['SCRIPT_NAME'] = (script_name.to_s + match.to_s).chomp('/')
env['PATH_INFO'] = match.post_match
end
env[@params_key] = (set_params || {}).merge parameters
status, headers, body = route.app.call(env)
if 'pass' == headers['X-Cascade']
env['SCRIPT_NAME'] = script_name
env['PATH_INFO'] = path_info
env[@params_key] = set_params
next
end
return [status, headers, body]
end
return [404, {'X-Cascade' => 'pass'}, ['Not Found']]
end
def recognize req
find_routes(req.env).each do |match, parameters, route|
unless route.path.anchored
req.env['SCRIPT_NAME'] = match.to_s
req.env['PATH_INFO'] = match.post_match.sub(/^([^\/])/, '/\1')
end
yield(route, nil, parameters)
end
end
def visualizer
tt = GTG::Builder.new(ast).transition_table
groups = partitioned_routes.first.map(&:ast).group_by { |a| a.to_s }
asts = groups.values.map { |v| v.first }
tt.visualizer asts
end
private
def partitioned_routes
routes.partitioned_routes
end
def ast
routes.ast
end
def simulator
routes.simulator
end
def custom_routes
partitioned_routes.last
end
def filter_routes path
return [] unless ast
data = simulator.match(path)
data ? data.memos : []
end
def find_routes env
req = request_class.new env
routes = filter_routes(req.path_info).concat custom_routes.find_all { |r|
r.path.match(req.path_info)
}
routes.concat get_routes_as_head(routes)
routes.sort_by!(&:precedence).select! { |r|
r.constraints.all? { |k,v| v === req.send(k) } &&
r.verb === req.request_method
}
routes.reject! { |r| req.ip && !(r.ip === req.ip) }
routes.map! { |r|
match_data = r.path.match(req.path_info)
match_names = match_data.names.map { |n| n.to_sym }
match_values = match_data.captures.map { |v| v && Utils.unescape_uri(v) }
info = Hash[match_names.zip(match_values).find_all { |_,y| y }]
[match_data, r.defaults.merge(info), r]
}
end
def get_routes_as_head(routes)
precedence = (routes.map(&:precedence).max || 0) + 1
routes = routes.select { |r|
r.verb === "GET" && !(r.verb === "HEAD")
}.map! { |r|
Route.new(r.name,
r.app,
r.path,
r.conditions.merge(:request_method => "HEAD"),
r.defaults).tap do |route|
route.precedence = r.precedence + precedence
end
}
routes.flatten!
routes
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment