首先 Rails4+,默认的 routes 是’config/routes.rb’。要想拆分 routes,可以往 paths 里加入:
class Application < Rails::Application
  config.paths['config/routes.rb'].concat ['config/routes2.rb']
end
在 rails3 里,采用的是 config.paths['config/routes’]
ActionDispatch::Routing::Mapper::HttpHelpers 中定义了在 route 里可以设置的 5 种 HTTP via
get 'bacon', to: 'food#bacon’
post 'bacon', to: 'food#bacon’
patch 'bacon', to: 'food#bacon’
put 'bacon', to: 'food#bacon’
delete 'broccoli', to: 'food#broccoli’
最终调用的还是 match 方法
match 'path' => 'controller#action', via: patch
match 'path', to: 'controller#action', via: :post
match 'path', 'otherpath', on: :member, via: :together
所以,直接写成 match 方法,似乎,也算少执行一些代码。
Rails 路由的加载的地方,所有被拆分的路由文件放在@paths里
ActionDispatch::Routing::Mapper::Base 里定义了路由的匹配规则,具体可以看看代码注释 ActionDispatch::Routing::RouteSet
通过 initializer :add_routing_paths 的初始器,指定了 routes.rb 的文件位置,
initializer :add_routing_paths do |app|
  routing_paths = self.paths["config/routes.rb"].existent
  if routes? || routing_paths.any?
    app.routes_reloader.paths.unshift(*routing_paths)
    app.routes_reloader.route_sets << routes
  end
end
可以在 app.routes_reloader.paths 中加入多个 routes 文件。
set_routes_reloader_hook 初始化器,开始执行 routes 文件里的代码,
initializer :set_routes_reloader_hook do |app|
  reloader = routes_reloader
  reloader.execute_if_updated
  reloaders << reloader
  app.reloader.to_run do
    # We configure #execute rather than #execute_if_updated because if
    # autoloaded constants are cleared we need to reload routes also in
    # case any was used there, as in
    #
    #   mount MailPreview => 'mail_view'
    #
    # This means routes are also reloaded if i18n is updated, which
    # might not be necessary, but in order to be more precise we need
    # some sort of reloaders dependency support, to be added.
    require_unload_lock!
    reloader.execute
  end
end
initializer :build_middleware_stack do
  build_middleware_stack
end
alias :build_middleware_stack :app
def app
  @app || @app_build_lock.synchronize {
    @app ||= begin
      stack = default_middleware_stack
      config.middleware = build_middleware.merge_into(stack)
      config.middleware.build(endpoint)
    end
  }
end
def endpoint
  self.class.endpoint || routes
end
def routes
  @routes ||= ActionDispatch::Routing::RouteSet.new_with_config(config)
  @routes.append(&Proc.new) if block_given?
  @routes
end
简而言之,就是:build_middleware_stack 初始化一个 RouteSet,作为第一个 rack 加入 middleware,:add_routing_paths 指定了路由的 path,:set_routes_reloader_hook 执行路由文件,装配路由。
当发送请求时,http 服务器会开始调用 middleware 的 call 方法,最底层的 RouteSet,会根据 env 生成一个 ActionDispatch::Request 对象。
def call(env)
  req = make_request(env)
  req.path_info = Journey::Router::Utils.normalize_path(req.path_info)
  @router.serve(req)
end
@router 是一个 ActionDispatch::Journey::Router 对象,里面包含一个@routes,是 ActionDispatch::Journey::Routes 的对象,@routes里包含许多 ActionDispatch::Journey::Route 对象,每一个都是一条 http 的请求匹配模式。
每一个 http 请求先包装成一个 ActionDispatch::Routing::Mapper 对象,指定了@scope_level,@concerns,@scope。
mapping = Mapping.build(@scope, @set, ast, controller, default_action, to, via, formatted, options_constraints, anchor, options)
@set.add_route(mapping, ast, as, anchor)
每一个 Mapper 对象,包装成 ActionDispatch::Routing::Mapper::Mapping 对象。 RouteSet 的初始化方法
def initialize(config = DEFAULT_CONFIG)
       self.named_routes = NamedRouteCollection.new
       self.resources_path_names = self.class.default_resources_path_names
       self.default_url_options = {}
       @config                     = config
       @append                     = []
       @prepend                    = []
       @disable_clear_and_finalize = false
       @finalized                  = false
       @env_key                    = "ROUTES_#{object_id}_SCRIPT_NAME".freeze
       @set    = Journey::Routes.new
       @router = Journey::Router.new @set
       @formatter = Journey::Formatter.new self
     end
此处的@set就是 RouteSet 对象,@set.add_route里调用了 Journey::Routes.new.add_route
def add_route(name, mapping)
  route = mapping.make_route name, routes.length
  routes << route
  partition_route(route)
  clear_cache!
  route
end
def make_route(name, precedence)
  route = Journey::Route.new(name,
                    application,
                    path,
                    conditions,
                    required_defaults,
                    defaults,
                    request_method,
                    precedence,
                    @internal)
  route
end
在 Journey::Routes.new.add_route 里,调用 mapping.make_route,make_route 生成一条 http 请求的匹配模式,加入到 Journey::Routes 中 Journey::Routes << Journey::Route RouteSet.@set = Journey::Routes RouteSet.@router = Journey::Router.new @set 至此 RouteSet 和 @router,Routes,Route 关联起来了。
当调用 RouteSet 的 call 方法,就调用@router的 serve 方法
def serve(req)
  find_routes(req).each do |match, parameters, route|
    set_params  = req.path_parameters
    path_info   = req.path_info
    script_name = req.script_name
    unless route.path.anchored
      req.script_name = (script_name.to_s + match.to_s).chomp("/")
      req.path_info = match.post_match
      req.path_info = "/" + req.path_info unless req.path_info.start_with? "/"
    end
    req.path_parameters = set_params.merge parameters
    status, headers, body = route.app.serve(req)
    if "pass" == headers["X-Cascade"]
      req.script_name     = script_name
      req.path_info       = path_info
      req.path_parameters = set_params
      next
    end
    return [status, headers, body]
  end
  return [404, { "X-Cascade" => "pass" }, ["Not Found"]]
end
此处的 route.app 是 make_route 创建 route 是,传入的 application,是一个 Routing::Endpoint 类,这里是它的一个子类 ActionDispatch::Routing::RouteSet::Dispatcher 对象
def serve(req)
  params     = req.path_parameters
  controller = controller req
  res        = controller.make_response! req
  dispatch(controller, params[:action], req, res)
rescue ActionController::RoutingError
  if @raise_on_name_error
    raise
  else
    return [404, { "X-Cascade" => "pass" }, []]
  end
end
此时 make_response 定义在 ActionController::Base 中,生成 ActionDispatch::Response 对象
dispatch 调用到对应的 action,返回 rack 的返回值
def dispatch(name, request, response) #:nodoc:
  set_request!(request)
  set_response!(response)
  process(name)
  request.commit_flash
  to_a
end
def rack_response(status, header)
  if NO_CONTENT_CODES.include?(status)
    [status, header, []]
  else
    [status, header, RackBody.new(self)]
  end
end
此处的 self 就是 ActionDispatch::Response 对象。